-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite livefs (in Rust) #2293
Rewrite livefs (in Rust) #2293
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: cgwalters The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
There are 4 major things that make this safer than the previous version:
Now after working on this a bit I realized that |
Need to rewrite the tests, and also figure out an elegant way to "escape" our mount namespace to create the "transient" unlock in the real ns. I originally tried using |
7f2bec7
to
48c902a
Compare
Cool, test case successfully found a read of uninitialized variable. |
const LIVEFS_STATE_NAME: &str = "rpmostree-livefs-state"; | ||
|
||
#[derive(Debug, Default, Clone, Serialize, Deserialize)] | ||
struct LiveFsState { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I was touching this data last time, I was unsure about the actual semantics of this, so let me ask it here. Are commit
and inprogress_commit
actually related in some way?
That is, are they maybe in an XOR state (and thus an enum
here) or perhaps in a linked state (and thus a Option<LiveFsState>
with non-null fields)?
Either way, it would be good to document what these two fields hold and what is the scenario they represent, to ease the life of casual readers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's
diff --git a/rust/src/livefs.rs b/rust/src/livefs.rs
index 2dc2bef8..03f9f1a4 100644
--- a/rust/src/livefs.rs
+++ b/rust/src/livefs.rs
@@ -19,7 +19,12 @@ const LIVEFS_STATE_NAME: &str = "rpmostree-livefs-state";
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
struct LiveFsState {
+ /// The OSTree commit that the running root filesystem is using,
+ /// as distinct from the one it was booted with.
commit: Option<String>,
+ /// Set when a livefs operation is in progress; if the process
+ /// is interrupted, some files from this commit may exist
+ /// on disk but in an incomplete state.
inprogress_commit: Option<String>,
}
(But not pushing yet since I don't want to break the green CI)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely not a XOR - we support going from "base commit" ➡️ livefs(A) ➡️ livefs(B) and if the A ➡️ B transition is interrupted, then we have
state {
commit: Some(A),
inprogress_commit: Some(B)
}
Whereas if B completes then we have
state {
commit: Some(B)
inprogress_commit: None
}
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the insight! No need to amend the PR right now, it's already good to have these details noted down here.
Aside, in the end it looks like:
this models a two-statesInProgress
/Active
FSMin both states thecommit
field is present (is there an actual case where it'sNone
?)
EDIT: blurred into this there is a state-machine, which is more complex than my couple of lines above.
Hooray, this one is passing CI now. So...debate as to merging as is and doing more as followup, or waiting? I lean a bit to the former but could be convinced that e.g. we should handle |
This is an
|
Eh I'll do a bit more on this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The general approach here makes sense. In #639, we were discussing possible ways to do this by taking the target commit's /usr
, but I don't think we landed on a design that was clean enough mount-wise. The payoff though would be a much more straightforward implementation.
use crate::ffiutil::*; | ||
|
||
#[no_mangle] | ||
pub extern "C" fn ror_livefs_get_state( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like we could reduce those two APIs (ror_livefs_get_state
and ror_livefs_query
) to just one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm...probably it should be a helper on the C side? There are a lot of callers that just want a boolean, so we'd really end up inlining it anyways.
OK updated 🆕 and now fully supports changes to
again! |
I think we can also merge #2060 after this, since the solution of "escaping" via systemd-run also fixes that issue. |
Now always based on an overlayfs: ostreedev/ostree@f2773c1 This fixes a whole swath of problems with the previous design, including the danger in replacing `/usr/lib/ostree-boot` which broke booting for some people. Further, we don't need to push a rollback deployment; the livefs changes are always transient. So now we store livefs state in `/run` instead of in the origin file. Since we're doing a rewrite, it's now in Rust for much more safety. We also always work in terms of incremental diffs between commits; the previous huge hammer of swapping `/usr` was way too dangerous.
We have no business accessing `/var/roothome` or `/var/home`. In general the ostree design clearly avoids touching those, but since systemd offers us easy tools to toggle on protection, let's use them. In the future it'd be nice to do something like using `DynamicUser=yes` for the main service, and have a system `rpm-ostreed-transaction.service` that runs privileged but as a subprocess.
Nice work! I think let's get this in and we can do any tweaks and fixes as follow-ups. /lgtm |
/refresh |
The old livefs code was broken, it's time to rewrite it, and that's a good excuse to use Rust.