CFE-90: reconcile storage mount options when they drift (opt-in remount) - #6222
Merged
nickanderson merged 6 commits intoAug 3, 2026
Merged
Conversation
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.
Storage/mount promises only applied
mount_optionsto the initial mount and (ifedit_fstabis enabled) the fstab entry. A filesystem already mounted with different options was never corrected, becausemount -askips already-mounted filesystems. This PR adds opt-in reconciliation of a live mount when its options drift from the promise, and along the way fixes several long-standing storage-promise bugs (CFE-1539, CFE-1863, CFE-2350, CFE-3366).Opt-in live remount reconciliation (CFE-90, CFE-1864)
New
mountbody attributes:remount(defaultfalse) — reconcile a live mount's options when they differ. When false, a mounted filesystem with the correct source is kept regardless of option drift (backwards compatible; options still drive the initial mount and, withedit_fstab, the fstab entry).remount_methods(default{ remount }) — mechanisms tried in order, re-reading the live mount after each (the kernel returns success from a remount even when it silently ignores NFS-negotiated options). Defaults to the non-disruptive in-placeremountonly; the disruptiveunmount_mount(which tears the filesystem down and back up) is opt-in, and is required to change options a live remount cannot — e.g. NFS-negotiatedvers=/rsize=, or the server.remount_timeout— protect against a hung or unreachable server.Option comparison (live mount). Only the options the promise names are enforced; any option it does not name is left unpoliced — its provenance is unknown (kernel-negotiated like
vers=/rsize=/proto=/sec=, or added by a prior manualmount -o, indistinguishable). The promise is first resolved with util-linux "last wins" semantics — exactly asmount -oapplies a list, a later option overrides an earlier conflicting one, sodefaults,rois a read-only mount andro,rwisrw. Each surviving option must then hold on the live mount: its inverse absent, and it either present or a default-on flag. Inverse pairs (noatime/relatime,hard/soft,ro/rw,sync/async, genericno<opt>/<opt>) andtcp/udp↔proto=aliases are recognized; an option the promise specifies (including a negotiated one such asrsize=8192) must be present. A correctly-mounted filesystem converges instead of being reported changed every run, and an override (roshadowing an earlierrw) is logged at verbose.The
defaultspseudo-option.defaults(=rw,suid,dev,exec,auto,nouser,async, per mount(8)) is never echoed by the kernel, so it's expanded to its checkable componentsrw,suid,dev,exec,asyncand subjected to the same last-wins resolution. It holds unless one of the violating negatives is present —ro(vsrw),nosuid/nodev/noexec(vssuid/dev/exec), orsync(vsasync) — and a later explicit option overrides the matching component (sodefaults,rorequires read-only,defaults,nosuidallowsnosuid).auto/nouserare fstab / mount-permission concepts, not runtime state, so they aren't enforced. When reconciling a drifteddefaultsmount in place, the remount command likewise usesrw,suid,dev,exec,async(util-linux doesn't apply the options implied by a baredefaultson a remount), and mount's own last-wins applies any trailing override — so a mount that drifted toro/nosuid/etc. is restored non-disruptively rather than only viaunmount_mount.fstab maintenance for already-mounted filesystems (CFE-1539)
Previously a filesystem already mounted with the correct source was reported "mounted as promised" and fstab was never consulted, so a missing fstab entry was not restored and an options change was not written until the mount happened to be redone.
VerifyInFstabnow runs on the mounted-correctly path too (whenedit_fstab => "true"), deliberately independent of the opt-in liveremount: keeping fstab correct is the documented behavior ofmount_options. fstab option comparison usesstrcmpbecause option order matters.Surgical single-filesystem mount, not
mount -a(CFE-1863)A storage promise for a not-yet-mounted filesystem used to arm
mount -a(mount -vaon Linux), which mounts every unmounted fstab entry — unrelated devices and foreign filesystem types included — as a side effect of a single promise. The not-mounted path now mounts just the promised filesystem surgically (VerifyMount), then persists it to fstab. Themount -amechanism (MountAll) is retained only for the explicitmountfilesystemsagent-control attribute, which still means "mount everything in fstab".Target a specific mount on unmount (CFE-2350)
An unmount promise that named
mount_source/mount_serverwas logged as "probably an error", and the server was never used to pick which mount to act on — so you couldn't unmount one specific mount (e.g. from a server being migrated away) without affecting others. The bogus warning is removed, and the server (host) is now part of the "mounted correctly" identity check, gated onremountorunmountso it only engages when the promise opts into disruptive mount management. An unmount promise that finds a different filesystem at the mount point leaves it — and its fstab entry — untouched, andLiveMountConvergedtreats the server as identity so a remount-in-place that can't change it escalates tounmount_mount.Correct dry-run / warn reporting (CFE-3366)
The mount, unmount and remount outcomes are based on the promise action (
MakingInternalChanges) rather than a bare!DONTDO. A dry-run (-n) orwarnpromise now reportsWARNwithout definingpromise_repaired, so dependent promises no longer fire on a no-op run.Supporting mount-info fixes
GetFstabEntryOptionsreturned the fstab type field instead of the options field (spurious rewrite every run).ReplaceFstabEntryleaked the previous entry string.optionsvsraw_opts).LOG_LEVEL_ERR(the outcome isINTERRUPTED), and leaked options strings on the error paths are freed.Testing
Unit coverage in
tests/unit/nfs_test.c(option subset matching, inverse/alias pairs, thedefaultsnegative-violation check, contradiction detection, and thedefaults→rw,suid,dev,exec,asyncremount expansion), runnable unprivileged viamake -C tests/unit check. The behavioral NFS reconcile/escalation, fstab maintenance, and surgical single-filesystem mount — which need root and a real NFS server — are covered by the system-testing PR and were exercised against a loopback NFS export during development.Commits
References
mount -askipping already-mounted filesystems, anddefaults=rw,suid,dev,exec,auto,nouser,async— mount(8)/proc/mounts) and NFS-specific options not modifiable on remount — nfs(5)Resolves CFE-90 (and its duplicate CFE-1864), CFE-1539 (fstab maintenance), CFE-1863 (
mount -ascope), CFE-2350 (target a specific mount on unmount), and CFE-3366 (dry-run/warn definingpromise_repaired) for storage mount promises.Ticket: https://northerntech.atlassian.net/browse/CFE-90
Together with: https://github.com/cfengine/system-testing/pull/693