diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 900efe499..b024b2106 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #ifdef HAVE_LIBMOUNT #include @@ -59,6 +61,35 @@ static gboolean is_ro_mount (const char *path); +static gboolean +fs_is_fat (int parent_dfd) +{ + struct statfs buf; + + if (TEMP_FAILURE_RETRY (fstatfs (parent_dfd, &buf)) < 0) + return FALSE; + + if (buf.f_type == MSDOS_SUPER_MAGIC) + return TRUE; + + return FALSE; +} + +static gboolean +symlink_fat (const char *target, + int parent_dfd, + const char *linkpath, + GCancellable *cancellable, + GError **error) +{ + if (!glnx_file_replace_contents_at(parent_dfd, linkpath, (guint8*)target, + -1, GLNX_FILE_REPLACE_DATASYNC_NEW, + cancellable, error)) + return FALSE; + + return TRUE; +} + /* * Like symlinkat() but overwrites (atomically) an existing * symlink. @@ -70,6 +101,9 @@ symlink_at_replace (const char *oldpath, GCancellable *cancellable, GError **error) { + g_autofree char *dest_dir = g_path_get_dirname(newpath); + glnx_autofd int dest_dir_dfd = -1; + gboolean fat; /* Possibly in the future generate a temporary random name here, * would need to move "generate a temporary name" code into * libglnx or glib? @@ -79,12 +113,35 @@ symlink_at_replace (const char *oldpath, /* Clean up any stale temporary links */ (void) unlinkat (parent_dfd, temppath, 0); - /* Create the temp link */ - if (TEMP_FAILURE_RETRY (symlinkat (oldpath, parent_dfd, temppath)) < 0) - return glnx_throw_errno_prefix (error, "symlinkat"); + /* Clean up any stale FAT "symlinks" */ + g_autofree char *temp_fat_path = g_strconcat (temppath, ".sln", NULL); + (void) unlinkat (parent_dfd, temp_fat_path, 0); + + if (!glnx_opendirat (parent_dfd, dest_dir, TRUE, &dest_dir_dfd, error)) + return FALSE; + fat = fs_is_fat (dest_dir_dfd); + + /* Create the temp link */ + if (!fat) + { + if (TEMP_FAILURE_RETRY (symlinkat (oldpath, parent_dfd, temppath)) < 0) + return glnx_throw_errno_prefix (error, "symlinkat"); + } + else + { + if (!symlink_fat (oldpath, parent_dfd, temp_fat_path, cancellable, error)) + return glnx_throw_errno_prefix (error, "symlinkat"); + } /* Rename it into place */ - if (!glnx_renameat (parent_dfd, temppath, parent_dfd, newpath, error)) + if (fat) + { + g_autofree char *new_fat_path = g_strconcat (newpath, ".sln", NULL); + if (!glnx_renameat (parent_dfd, temp_fat_path, parent_dfd, new_fat_path, error)) + return FALSE; + return TRUE; + } + else if (!glnx_renameat (parent_dfd, temppath, parent_dfd, newpath, error)) return FALSE; return TRUE; @@ -2076,17 +2133,27 @@ swap_bootloader (OstreeSysroot *sysroot, g_assert ((current_bootversion == 0 && new_bootversion == 1) || (current_bootversion == 1 && new_bootversion == 0)); + gboolean fat; glnx_autofd int boot_dfd = -1; if (!glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &boot_dfd, error)) return FALSE; + fat = fs_is_fat (boot_dfd); + /* 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 (boot_dfd, "loader.tmp", boot_dfd, "loader", error)) - return FALSE; - + if (!fat) + { + if (!glnx_renameat (boot_dfd, "loader.tmp", boot_dfd, "loader", error)) + return FALSE; + } + else + { + if (!glnx_renameat (boot_dfd, "loader.tmp.sln", boot_dfd, "loader.sln", error)) + return FALSE; + } /* Now we explicitly fsync this directory, even though it * isn't required for atomicity, for two reasons: * - It should be very cheap as we're just syncing whatever diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index e3d7e425c..7389dd638 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -580,32 +580,37 @@ read_current_bootversion (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - int ret_bootversion; + int ret_bootversion = 0; struct stat stbuf; + g_autofree char *target = NULL; if (!glnx_fstatat_allow_noent (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW, error)) return FALSE; if (errno == ENOENT) { - ret_bootversion = 0; + /* Don't share the error because we want error handling to be the same as before we + * added fake symlinks */ + target = glnx_file_get_contents_utf8_at(self->sysroot_fd, "boot/loader.sln", NULL, cancellable, NULL); + if (!target) goto not_found; } else { if (!S_ISLNK (stbuf.st_mode)) return glnx_throw (error, "Not a symbolic link: boot/loader"); - - g_autofree char *target = + target = glnx_readlinkat_malloc (self->sysroot_fd, "boot/loader", cancellable, error); if (!target) return FALSE; - if (g_strcmp0 (target, "loader.0") == 0) - ret_bootversion = 0; - else if (g_strcmp0 (target, "loader.1") == 0) - ret_bootversion = 1; - else - return glnx_throw (error, "Invalid target '%s' in boot/loader", target); } + if (g_strcmp0 (target, "loader.0") == 0) + ret_bootversion = 0; + else if (g_strcmp0 (target, "loader.1") == 0) + ret_bootversion = 1; + else + return glnx_throw (error, "Invalid target '%s' in boot/loader", target); + +not_found: *out_bootversion = ret_bootversion; return TRUE; }