Repository navigation
Fabric store: publish entries at a mode derived from the store's readers, not the umask - #12342
Merged
Merged
Conversation
…ers, not the umask Every object and head the CI principal wrote to /opt/gunbc/fabric-storage was 0600 (srv1 2026-09-26), so the serve principal could not read them. The cause is not an explicit mode: gunbc_file_write_create_new opens its staging file with no mode, and the fleet-converge steps that write the store run `umask 077` in their credential prelude in the same shell as gunbc. - extdeps.filesystem: Filesystem.WriteCreateNewWithMode; the one canonical create-new realization takes Option<u32> and sets the bits on the staged inode before the hard_link publishes it. None is today's behaviour exactly. - gunbc.managed_directory: managed_directory_entry_mode derives the entries' mode per (writer, reader) pair from the same dependents as the directory mode, and REFUSES other-read when the directory grants other-execute. - The fabric placement derives 0444 (briansrls reads ghrunner-written entries through other; neither area grants o+x), both writers bind it, and the CAS store gains DeclaredModeCreateOnly for heads. - The srv1 area ensure repairs existing entries: unprivileged find -user ghrunner -exec chmod u+r,g+r,o+r, additive and idempotent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…quoted argv Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lace bare get in touched files Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Addressed review 71507 in cb64dcf.
|
… row as ImportsFixed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…f passing it as a step A latent main type error the floor now compiles because this change reaches its closure. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… + local wet schedule) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # src/v1/stage0/src/v1_compiler_emit_rust.rs
…laim_executor --regen-round-cost) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # src/v1/stage0/src/v1_compiler_emit_rust.rs
Contributor
Author
|
Re the recurring advisory on hand-written seed Rust (reviews 71505, 71514, 71556, 71570, 71629): the admission reason and a record are in the PR body under Seed Rust record.
|
…d by --regen-round-cost; the next round converges with 0 stages) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dashboard node adhoc-f7be727b-2e8 (fixes a wrong assumption in #12294).
Defect
On srv1 (2026-09-26), every file the CI principal (ghrunner) wrote under
/opt/gunbc/fabric-storage/{objects,heads}was 0600. The serve principal (briansrls) could not read them. The service journal shows no failed reads yet, because the serve side hasn't read a CI-written object.Cause (walked back up the chain)
extdeps.filesystem.rust_realizationrust_file_write_create_new_fn_defopens its staging file with no mode. The seed interpreter runs the same code. So the published mode is0666 & ~umask.umask 077in their credential prelude, in the same shell asgunbc run. Fabric store: owner/group/mode derived from its two writers (CI claim principal can write) #12294 assumed the umask would give 0644. It gave 0600.Fix (the parent's rulings B, then A)
Filesystem.WriteCreateNewWithMode { path, content, mode }. One canonical realization now takesdeclared_mode: Option<u32>and sets the bits on the staged inode before the hard link publishes it.Noneproduces today's behaviour exactly, so every existing caller is unchanged. The v1 emitter gets a new verb and a typedFileWriteMissingModeInputrefusal. Stage0 mirrors are regenerated, and--required-regenreportsfirst_generation_equal=true.gunbc.managed_directorymanaged_directory_entry_modederives each class's read bit per (writer, reader) pair. A file is owned by whoever created it; its group is the directory's group under setgid.fabric_storage_placed_file_root. Serve returns 503 and the client returns a typedFabricStorageBindingRefusedif the derivation refuses. The CAS store gainsDeclaredModeCreateOnlyfor heads. Fixtures that build a root with no mode keep the umask behaviour.find <area> -maxdepth 1 -type f -user ghrunner -exec /bin/chmod u+r,g+r,o+r {} +. It runs unprivileged as the applier, who owns the 0600 files. It only adds bits, running it twice changes nothing, and no sudoers grant is needed.Evidence (executed locally, private target dir)
write_file_create_new_tests): 13/13 pass.a_declared_mode_is_published_regardless_of_the_umask: a forked child withumask 077and declared 0644 gets 0644; with umask 022 and declared 0640 it gets 0640. Without the new mode handling it would publish the umask's mode instead.an_absent_mode_keeps_the_umask_derived_mode: umask 077 gives 0600, umask 022 gives 0644..dagclaims, all passing:a_declared_entry_mode_is_the_published_mode_of_objects_and_heads: stat reads 0444 on the object and on the head generation, and a store with no declared mode does not come out 0444.test.claim.managed_directory_witness: the 0444 derivation; the owner reads through other and the second writer through group; a single writer derives 0400; and the refusal when the directory grants o+x.the_fabric_storage_area_ensure_repairs_the_ci_principals_entries,the_placed_fabric_store_derives_entry_mode_0444): still running locally; I'll add the result here.RED as the brief worded it
"An object written by writer A is readable by writer B" can't be expressed in a single-uid fixture. It is covered at two levels instead: the class derivation over two principals (model), and the published mode under
umask 077(seed test and wet claim).Seed Rust record (review 71505)
Hand-written seed Rust added: the interpreter's
write_create_new_with_modearm,Nonepassed at the existingwrite_create_newarm, and two umask tests inwrite_file_create_new_tests.extdeps.filesystem.rust_realization), widened by anOption<u32>. The seed,v1_rtand the generated realization carry identical bytes, andboth_projections_carry_the_one_authority_verbatimchecks that.Nonebehaves exactly as before, andan_absent_mode_keeps_the_umask_derived_modechecks that too.write_create_newarm and exists because the interpreter's file-transport verb dispatch is hand-maintained seed code, the same as the four existing verbs. No scaffold path is added, and none is deleted.src/v1/05_emit.dag/05_emit_rust.dag. The generatedv1_compiler_emit*.rsfiles are regenerated from it, and--required-regenreportsfirst_generation_equal=true.🤖 Generated with Claude Code