]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homework: allow specifying a dir component in CIFS services
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Oct 2021 13:52:23 +0000 (15:52 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 27 Oct 2021 20:37:56 +0000 (22:37 +0200)
Allow specifying CIFS services in the format //host/service/subdir/… to
allow multiple homedirs on the same share, and not in the main dir of
the share.

All other backends allow placing the data store at arbitrary places,
let's allow this too for the CIFS backend. This is particularly useful
for testing.

docs/USER_RECORD.md
man/homectl.xml
src/home/homework-cifs.c
src/home/homework.c
src/home/homework.h

index 6b607dfd45e29f28f9500528b850dc493a79837e..6710b00c0c531346d6ea14e6719c084984a5eb76 100644 (file)
@@ -411,7 +411,9 @@ useful when `cifs` is used as storage mechanism for the user's home directory,
 see above.
 
 `cifsService` → A string indicating the Windows File Share service (CIFS) to
-mount as home directory of the user on login.
+mount as home directory of the user on login. Should be in format
+`//<host>/<service>/<directory/…>`. The directory part is optional. If missing
+the top-level directory of the CIFS share is used.
 
 `imagePath` → A string with an absolute file system path to the file, directory
 or block device to use for storage backing the home directory. If the `luks`
index c2b1ec6c9b2855e6cfd3734c80ee7205b7d08daf..f670566593df4e5b881db82ae8ec54293e92a802 100644 (file)
         <term><option>--cifs-service=</option><replaceable>SERVICE</replaceable></term>
 
         <listitem><para>Configures the Windows File Sharing (CIFS) domain and user to associate with the home
-        directory/user account, as well as the file share ("service") to mount as directory. The latter is used when
-        <literal>cifs</literal> storage is selected.</para></listitem>
+        directory/user account, as well as the file share ("service") to mount as directory. The latter is
+        used when <literal>cifs</literal> storage is selected. The file share should be specified in format
+        <literal>//<replaceable>host</replaceable>/<replaceable>share</replaceable>/<replaceable>directory/…</replaceable></literal>. The
+        directory part is optional — if not specified the home directory will be placed in the top-level
+        directory of the share.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index b9200531632536892222695ed3239e2c6fbedc95..55b8c2588d684a946e078961ebe0fec0893d21bd 100644 (file)
@@ -7,6 +7,7 @@
 #include "fs-util.h"
 #include "homework-cifs.h"
 #include "homework-mount.h"
+#include "mkdir.h"
 #include "mount-util.h"
 #include "process-util.h"
 #include "stat-util.h"
@@ -18,6 +19,7 @@ int home_setup_cifs(
                 HomeSetupFlags flags,
                 HomeSetup *setup) {
 
+        _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL;
         char **pw;
         int r;
 
@@ -38,6 +40,15 @@ int home_setup_cifs(
         if (!h->cifs_service)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
 
+        r = parse_cifs_service(h->cifs_service, &chost, &cservice, &cdir);
+        if (r < 0)
+                return log_error_errno(r, "Failed parse CIFS service specification: %m");
+
+        /* Just the host and service part, without the directory */
+        chost_and_service = strjoin("//", chost, "/", cservice);
+        if (!chost_and_service)
+                return log_oom();
+
         r = home_unshare_and_mkdir();
         if (r < 0)
                 return r;
@@ -78,7 +89,7 @@ int home_setup_cifs(
                 if (r == 0) {
                         /* Child */
                         execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
-                              h->cifs_service, HOME_RUNTIME_WORK_DIR,
+                              chost_and_service, HOME_RUNTIME_WORK_DIR,
                               "-o", options, NULL);
 
                         log_error_errno(errno, "Failed to execute mount: %m");
@@ -104,10 +115,23 @@ int home_setup_cifs(
         if (r < 0)
                 return r;
 
-        setup->root_fd = open(HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        if (cdir) {
+                j = path_join(HOME_RUNTIME_WORK_DIR, cdir);
+                if (!j)
+                        return log_oom();
+
+                if (FLAGS_SET(flags, HOME_SETUP_CIFS_MKDIR)) {
+                        r = mkdir_p(j, 0700);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create CIFS subdirectory: %m");
+                }
+        }
+
+        setup->root_fd = open(j ?: HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
         if (setup->root_fd < 0)
                 return log_error_errno(errno, "Failed to open home directory: %m");
 
+        setup->mount_suffix = TAKE_PTR(cdir);
         return 0;
 }
 
@@ -139,7 +163,7 @@ int home_activate_cifs(
 
         setup->root_fd = safe_close(setup->root_fd);
 
-        r = home_move_mount(NULL, hd);
+        r = home_move_mount(setup->mount_suffix, hd);
         if (r < 0)
                 return r;
 
@@ -171,7 +195,7 @@ int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
                 return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
         }
 
-        r = home_setup_cifs(h, 0, setup);
+        r = home_setup_cifs(h, HOME_SETUP_CIFS_MKDIR, setup);
         if (r < 0)
                 return r;
 
index cfc0c945defb104478bd5bc37bc4a9489d645e3d..8ffed827426a66a3883db5279e48e19979e928a3 100644 (file)
@@ -365,6 +365,8 @@ int home_setup_done(HomeSetup *setup) {
         if (setup->do_drop_caches)
                 drop_caches_now();
 
+        setup->mount_suffix = mfree(setup->mount_suffix);
+
         return r;
 }
 
index 1fa5a1e37a56d87dea1df233c11cc9f867319e12..08d97b82610310db44fae4d8534d857b80e32f2e 100644 (file)
@@ -37,6 +37,8 @@ typedef struct HomeSetup {
 
         uint64_t partition_offset;
         uint64_t partition_size;
+
+        char *mount_suffix;           /* The directory to use as home dir is this path below /run/systemd/user-home-mount */
 } HomeSetup;
 
 typedef struct PasswordCache {
@@ -66,6 +68,9 @@ static inline bool password_cache_contains(const PasswordCache *cache, const cha
 /* Various flags for the operation of setting up a home directory */
 typedef enum HomeSetupFlags {
         HOME_SETUP_ALREADY_ACTIVATED = 1 << 0, /* Open an already activated home, rather than activate it afresh */
+
+        /* CIFS backend: */
+        HOME_SETUP_CIFS_MKDIR        = 1 << 1, /* Create CIFS subdir when missing */
 } HomeSetupFlags;
 
 int home_setup_done(HomeSetup *setup);