ENT-14297: Improve error message when cf-agent cannot write to an immutable file#6236
Conversation
f012570 to
f8c2765
Compare
larsewi
left a comment
There was a problem hiding this comment.
This only improves the message for when you are using the content attribute. Adding these checks everywhere would be quite tedious. Maybe it would be better to have OverrideImmutableBegin() take care of this logic?
WTH, not all my commits are here and I thought they were, sorry. will get them up later |
f8c2765 to
2378866
Compare
|
Good call — the previous version only covered the I looked at generalizing via The content path writes in place rather than renaming, so it keeps its own detection at the
Both confirm the bit via Added acceptance tests for edit_line and copy_from (15/16) alongside content (14). Verified end-to-end against a real immutable file (KVM VM, ext4 root): |
…-14297) Cover every path where cf-agent can fail to write a file because the immutable bit is set and is not being overridden, asserting that the error names the immutable bit: - content (14, fails at the in-place open) - edit_line (15) - copy_from (16) - mustache (17) fail at the temporary-file rename in - cfengine template (18) OverrideImmutableRename() - edit_xml (19) - inline_mustache (20) Each test drives a self-contained sub policy in its own agent process: the sub creates the target, sets the immutable bit via body fsattrs, and then attempts the modification. The test asserts on the captured output using native functions only (execresult_as_data + regcmp) and never fails on the sub's non-zero exit. Teardown clears the immutable bit natively.
…4297)
When cf-agent could not write a file whose immutable bit is set, the
error gave no reason. Now the actual system error is always reported, and
the immutable bit is called out explicitly when it is set (and not being
overridden):
error: Cannot open file '/tmp/x' for writing: Operation not permitted (the immutable bit is set)
error: Failed to replace original file '/tmp/x' with copy '...': Operation not permitted (the immutable bit is set)
The immutable annotation is confirmed via FSAttrsGetImmutableFlag(), not
guessed from errno (an immutable file typically fails with EPERM, but
that is not exclusive to immutability), so other causes (permissions, MAC
policy, a read-only or full filesystem) are never mislabeled.
The check lives at the two points where the write actually fails: the
in-place open in WriteContentFromString() (content), and the temporary-
file rename in OverrideImmutableRename(), which is the common landing
point for edit_line, edit_xml, copy_from and templating. It is skipped
when overriding the immutable bit, since then the agent writes to a
temporary copy that is renamed into place after clearing the bit.
b9cab70 to
4d490e5
Compare
| && (FSAttrsGetImmutableFlag(new_filename, &is_immutable) == FS_ATTRS_SUCCESS) | ||
| && is_immutable) |
There was a problem hiding this comment.
I wonder if you could have used new_is_immutable from above in this condition instead?
There was a problem hiding this comment.
Good instinct to reuse it, but new_is_immutable isn't valid in this branch: TemporarilyClearImmutableBit() returns early on !override without writing *was_immutable (and new_is_immutable is declared uninitialized), while this check runs specifically in the !override case — so reusing it here would read an uninitialized value. Keeping the explicit FSAttrsGetImmutableFlag() also keeps the extra stat on the rare rename-failure path rather than on every temp-file write. Happy to refactor TemporarilyClearImmutableBit() to always populate the flag if you'd prefer the reuse. (Analysis via Claude.)
Problem
When cf-agent cannot open a file for writing via the
contentattribute, the error gives no reason at all:The immutable bit (
chattr +i) is frequently set deliberately to keep CFEngine from managing a file, and this generic message forces operators to investigate the underlying cause.Change
The actual system error is now always reported, and when the file carries the immutable bit (and it is not being overridden) that is called out explicitly:
Other failures now include the system error string, which the previous message omitted entirely:
Jira: ENT-14297