]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homework: rework how we disassemble a home dir in home_deactivate()
authorLennart Poettering <lennart@poettering.net>
Fri, 29 Oct 2021 07:44:31 +0000 (09:44 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 15 Nov 2021 16:56:35 +0000 (17:56 +0100)
Let's first move the home dir to a new mount point that is only visible
in our own private namespace. Then, do FITRIM and stuff there, so that
we know the regular userspace can't interfere with that, and we know
that the home fs is not used anymore.

(This will become even more important once we add auto-grow/auto-shrink
for home dirs)

src/home/homework-luks.c
src/home/homework-luks.h
src/home/homework.c

index ae4f8a8e32f10a82975632dd3975e40ca75f8a51..7275b231cc7ad7f0094e34f57cfd5411a5c599fd 100644 (file)
@@ -1020,16 +1020,6 @@ int run_fitrim(int root_fd) {
         return 1;
 }
 
-int run_fitrim_by_path(const char *root_path) {
-        _cleanup_close_ int root_fd = -1;
-
-        root_fd = open(root_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
-        if (root_fd < 0)
-                return log_error_errno(errno, "Failed to open file system '%s' for trimming: %m", root_path);
-
-        return run_fitrim(root_fd);
-}
-
 int run_fallocate(int backing_fd, const struct stat *st) {
         struct stat stbuf;
 
@@ -1528,15 +1518,17 @@ int home_deactivate_luks(UserRecord *h, HomeSetup *setup) {
         return we_detached;
 }
 
-int home_trim_luks(UserRecord *h) {
+int home_trim_luks(UserRecord *h, HomeSetup *setup) {
         assert(h);
+        assert(setup);
+        assert(setup->root_fd >= 0);
 
         if (!user_record_luks_offline_discard(h)) {
                 log_debug("Not trimming on logout.");
                 return 0;
         }
 
-        (void) run_fitrim_by_path(user_record_home_directory(h));
+        (void) run_fitrim(setup->root_fd);
         return 0;
 }
 
index f8af66e083edb4da51034bfd6bffec44973cc7f6..f6ec11c2e6c8b4170d8b7083d10105bc4aea74c7 100644 (file)
@@ -9,7 +9,7 @@ int home_setup_luks(UserRecord *h, HomeSetupFlags flags, const char *force_image
 
 int home_activate_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, UserRecord **ret_home);
 int home_deactivate_luks(UserRecord *h, HomeSetup *setup);
-int home_trim_luks(UserRecord *h);
+int home_trim_luks(UserRecord *h, HomeSetup *setup);
 
 int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
 
@@ -39,7 +39,6 @@ static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
 }
 
 int run_fitrim(int root_fd);
-int run_fitrim_by_path(const char *root_path);
 int run_fallocate(int backing_fd, const struct stat *st);
 int run_fallocate_by_path(const char *backing_path);
 int run_mark_dirty(int fd, bool b);
index 65066a5156bab1cf179af3dbc14cb48159e73529..94cfd41780632adccd9c2461ae29949a745b250d 100644 (file)
@@ -924,21 +924,47 @@ static int home_deactivate(UserRecord *h, bool force) {
         if (r < 0)
                 return r;
         if (r == USER_TEST_MOUNTED) {
-                if (user_record_storage(h) == USER_LUKS) {
-                        r = home_trim_luks(h);
-                        if (r < 0)
-                                return r;
-                }
+                /* Before we do anything, let's move the home mount away. */
+                r = home_unshare_and_mkdir();
+                if (r < 0)
+                        return r;
 
-                /* Sync explicitly, so that the drop caches logic below can work as documented */
-                r = syncfs_path(AT_FDCWD, user_record_home_directory(h));
+                r = mount_nofollow_verbose(LOG_ERR, user_record_home_directory(h), HOME_RUNTIME_WORK_DIR, NULL, MS_BIND, NULL);
                 if (r < 0)
-                        log_debug_errno(r, "Failed to synchronize home directory, ignoring: %m");
+                        return r;
+
+                setup.undo_mount = true; /* remember to unmount the new bind mount from HOME_RUNTIME_WORK_DIR */
+
+                /* Let's explicitly open the new root fs, using the moved path */
+                setup.root_fd = open(HOME_RUNTIME_WORK_DIR, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+                if (setup.root_fd < 0)
+                        return log_error_errno(errno, "Failed to open moved home directory: %m");
+
+                /* Now get rid of the home at its original place (we only keep the bind mount we created above) */
+                r = umount_verbose(LOG_ERR, user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0));
+                if (r < 0)
+                        return r;
+
+                if (user_record_storage(h) == USER_LUKS)
+                        (void) home_trim_luks(h, &setup);
+
+                /* Sync explicitly, so that the drop caches logic below can work as documented */
+                if (syncfs(setup.root_fd) < 0)
+                        log_debug_errno(errno, "Failed to synchronize home directory, ignoring: %m");
                 else
                         log_info("Syncing completed.");
 
-                if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
-                        return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
+                setup.root_fd = safe_close(setup.root_fd);
+
+                /* Now get rid of the bind mount, too */
+                r = umount_verbose(LOG_ERR, HOME_RUNTIME_WORK_DIR, UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0));
+                if (r < 0)
+                        return r;
+
+                setup.undo_mount = false; /* Remember that the bind mount doesn't need to be unmounted anymore */
+
+                if (user_record_drop_caches(h))
+                        setup.do_drop_caches = true;
 
                 log_info("Unmounting completed.");
                 done = true;
@@ -956,8 +982,10 @@ static int home_deactivate(UserRecord *h, bool force) {
         if (!done)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
 
-        if (user_record_drop_caches(h))
+        if (setup.do_drop_caches) {
+                setup.do_drop_caches = false;
                 drop_caches_now();
+        }
 
         log_info("Everything completed.");
         return 0;