diff --git a/src/core/execute.c b/src/core/execute.c index b32e341b61d..04dcf4b4274 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3258,16 +3258,34 @@ static int setup_credentials_internal( return r; if (workspace_mounted) { - /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */ - r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); - if (r < 0) - return r; + bool install; - /* And mount it to the final place, read-only */ + /* Determine if we should actually install the prepared mount in the final location by bind + * mounting it there. We do so only if the mount is not established there already, and if the + * mount is actually non-empty (i.e. carries at least one credential). Not that in the best + * case we are doing all this in a mount namespace, thus noone else will see that we + * allocated a file system we are getting rid of again here. */ if (final_mounted) - r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW); - else + install = false; /* already installed */ + else { + r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false); + if (r < 0) + return r; + + install = r == 0; /* install only if non-empty */ + } + + if (install) { + /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */ + r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); + if (r < 0) + return r; + + /* And mount it to the final place, read-only */ r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL); + } else + /* Otherwise get rid of it */ + r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW); if (r < 0) return r; } else { @@ -3405,6 +3423,11 @@ static int setup_credentials( _exit(EXIT_FAILURE); } + /* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's + * try to remove it. This matters in particular if we created the dir as mount point but then didn't + * actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being + * seen by users when trying access this inode. */ + (void) rmdir(p); return 0; } diff --git a/src/core/namespace.c b/src/core/namespace.c index 1d19685d2ea..2fcc096217d 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2385,6 +2385,7 @@ int setup_namespace( .mode = BIND_MOUNT, .read_only = true, .source_const = creds_path, + .ignore = true, }; } else { /* If our service has no credentials store configured, then make the whole