-
Notifications
You must be signed in to change notification settings - Fork 293
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
Use rename of directories instead of symbolic links in boot partition. #1967
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2026,18 +2026,18 @@ install_deployment_kernel (OstreeSysroot *sysroot, | |
return TRUE; | ||
} | ||
|
||
/* We generate the symlink on disk, then potentially do a syncfs() to ensure | ||
/* We generate the directory on disk, then potentially do a syncfs() to ensure | ||
* that it (and everything else we wrote) has hit disk. Only after that do we | ||
* rename it into place. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't remove comments like this. This is some of the most sensitive code in ostree. If anything, it should have a lot more comments and I don't see any in the code you've added. |
||
static gboolean | ||
prepare_new_bootloader_link (OstreeSysroot *sysroot, | ||
int current_bootversion, | ||
int new_bootversion, | ||
GCancellable *cancellable, | ||
GError **error) | ||
prepare_new_bootloader_dir (OstreeSysroot *sysroot, | ||
int current_bootversion, | ||
int new_bootversion, | ||
GCancellable *cancellable, | ||
GError **error) | ||
{ | ||
GLNX_AUTO_PREFIX_ERROR ("Preparing final bootloader swap", error); | ||
GLNX_AUTO_PREFIX_ERROR ("Preparing new bootloader directory", error); | ||
g_assert ((current_bootversion == 0 && new_bootversion == 1) || | ||
(current_bootversion == 1 && new_bootversion == 0)); | ||
|
||
|
@@ -2047,23 +2047,76 @@ prepare_new_bootloader_link (OstreeSysroot *sysroot, | |
if (errno != EEXIST) | ||
return glnx_throw_errno_prefix (error, "symlinkat"); | ||
|
||
g_autofree char *new_target = g_strdup_printf ("loader.%d", new_bootversion); | ||
if (!_ostree_sysroot_ensure_boot_fd (sysroot, error)) | ||
return FALSE; | ||
|
||
/* We shouldn't actually need to replace but it's easier to reuse | ||
that code */ | ||
if (!symlink_at_replace (new_target, sysroot->sysroot_fd, "boot/loader.tmp", | ||
cancellable, error)) | ||
g_autofree char *loader_dir_name = g_strdup_printf ("loader.%d", new_bootversion); | ||
|
||
if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, loader_dir_name, 0755, | ||
cancellable, error)) | ||
return FALSE; | ||
|
||
g_autofree char *version_name = g_strdup_printf ("%s/version", loader_dir_name); | ||
|
||
if (!glnx_file_replace_contents_at (sysroot->boot_fd, version_name, | ||
(guint8*)loader_dir_name, strlen(loader_dir_name), | ||
0, cancellable, error)) | ||
return FALSE; | ||
|
||
return TRUE; | ||
} | ||
|
||
static gboolean | ||
renameat2_exchange (int olddirfd, | ||
const char *oldpath, | ||
int newdirfd, | ||
const char *newpath, | ||
gboolean *is_atomic, | ||
GError **error) | ||
{ | ||
if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE) == 0) | ||
return TRUE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not setting the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We start within
Then we only mark There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I missed that part. |
||
else | ||
{ | ||
if ((errno == EINVAL) | ||
|| (errno == ENOSYS)) | ||
{ | ||
if (glnx_renameat2_exchange (olddirfd, oldpath, newdirfd, newpath) == 0) | ||
{ | ||
is_atomic = FALSE; | ||
return TRUE; | ||
} | ||
} | ||
} | ||
|
||
if (errno != ENOENT) | ||
return glnx_throw_errno_prefix (error, "renameat2"); | ||
|
||
if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) == 0) | ||
return TRUE; | ||
else | ||
{ | ||
if ((errno == EINVAL) | ||
|| (errno == ENOSYS)) | ||
{ | ||
if (glnx_renameat2_noreplace (olddirfd, oldpath, newdirfd, newpath) == 0) | ||
{ | ||
is_atomic = FALSE; | ||
return TRUE; | ||
} | ||
} | ||
} | ||
|
||
return glnx_throw_errno_prefix (error, "renameat2"); | ||
} | ||
|
||
/* Update the /boot/loader symlink to point to /boot/loader.$new_bootversion */ | ||
static gboolean | ||
swap_bootloader (OstreeSysroot *sysroot, | ||
OstreeBootloader *bootloader, | ||
int current_bootversion, | ||
int new_bootversion, | ||
gboolean *is_atomic, | ||
GCancellable *cancellable, | ||
GError **error) | ||
{ | ||
|
@@ -2075,11 +2128,8 @@ swap_bootloader (OstreeSysroot *sysroot, | |
if (!_ostree_sysroot_ensure_boot_fd (sysroot, error)) | ||
return FALSE; | ||
|
||
/* The symlink was already written, and we used syncfs() to ensure | ||
* its data is in place. Renaming now should give us atomic semantics; | ||
* see https://bugzilla.gnome.org/show_bug.cgi?id=755595 | ||
*/ | ||
if (!glnx_renameat (sysroot->boot_fd, "loader.tmp", sysroot->boot_fd, "loader", error)) | ||
g_autofree char *new_target = g_strdup_printf ("loader.%d", new_bootversion); | ||
if (!renameat2_exchange(sysroot->boot_fd, new_target, sysroot->boot_fd, "loader", is_atomic, error)) | ||
return FALSE; | ||
|
||
/* Now we explicitly fsync this directory, even though it | ||
|
@@ -2254,6 +2304,7 @@ write_deployments_bootswap (OstreeSysroot *self, | |
OstreeBootloader *bootloader, | ||
SyncStats *out_syncstats, | ||
char **out_subbootdir, | ||
gboolean *is_atomic, | ||
GCancellable *cancellable, | ||
GError **error) | ||
{ | ||
|
@@ -2317,15 +2368,16 @@ write_deployments_bootswap (OstreeSysroot *self, | |
return glnx_prefix_error (error, "Bootloader write config"); | ||
} | ||
|
||
if (!prepare_new_bootloader_link (self, self->bootversion, new_bootversion, | ||
cancellable, error)) | ||
if (!prepare_new_bootloader_dir (self, self->bootversion, new_bootversion, | ||
cancellable, error)) | ||
return FALSE; | ||
|
||
if (!full_system_sync (self, out_syncstats, cancellable, error)) | ||
return FALSE; | ||
|
||
if (!swap_bootloader (self, bootloader, self->bootversion, new_bootversion, | ||
cancellable, error)) | ||
is_atomic, cancellable, error)) | ||
|
||
return FALSE; | ||
|
||
if (out_subbootdir) | ||
|
@@ -2534,7 +2586,8 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, | |
bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader); | ||
|
||
if (!write_deployments_bootswap (self, new_deployments, opts, bootloader, | ||
&syncstats, &new_subbootdir, cancellable, error)) | ||
&syncstats, &new_subbootdir, &bootloader_is_atomic, | ||
cancellable, error)) | ||
return FALSE; | ||
} | ||
|
||
|
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.
Is this logic even right? In
swap_bootloader
,loader.<newversion>
is exchanged withloader
. That means that the current bootloader directory is now namedloader.<newversion>
. What's even atloader.<currentversion>
? This is also in conflict with the code below that cleans uploader.<currentversion>
. So, after this change, there's no moreloader.*
bootloader directories left. Is that correct?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.
Right. Only
loader
directory is left after deployment is finished successfully. We could just useloader.tmp
andloader
. But if I remember correctly, we need to support older deployments that are still using symlinks. For that reason we still to need to keep this 0/1 version names. Also maintaining a patch for long term I cannot really afford changing too much the logic.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.
Reading the code, I do not think the support for older deployment is working. I will try to add tests for that.