]> arthur.barton.de Git - netatalk.git/commitdiff
Support for home dirs
authorFrank Lahm <franklahm@googlemail.com>
Wed, 29 Feb 2012 15:26:51 +0000 (16:26 +0100)
committerFrank Lahm <franklahm@googlemail.com>
Wed, 29 Feb 2012 15:26:51 +0000 (16:26 +0100)
15 files changed:
bin/ad/ad.c
bin/ad/ad.h
bin/ad/ad_cp.c
bin/ad/ad_find.c
bin/ad/ad_ls.c
bin/ad/ad_mv.c
bin/ad/ad_rm.c
bin/ad/ad_util.c
etc/cnid_dbd/cmd_dbd.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/main.c
include/atalk/globals.h
include/atalk/netatalk_conf.h
include/atalk/volume.h
libatalk/util/netatalk_conf.c

index ea312fa9f9a1a073ee741ad72a8e7c3f7cc8e6be..6115c2f2fb3c21b73e07d70793785fe82aae4417 100644 (file)
@@ -62,15 +62,15 @@ int main(int argc, char **argv)
         return 1;
 
     if (STRCMP(argv[1], ==, "ls"))
-        return ad_ls(argc - 1, argv + 1);
+        return ad_ls(argc - 1, argv + 1, &obj);
     else if (STRCMP(argv[1], ==, "cp"))
-        return ad_cp(argc - 1, argv + 1);
+        return ad_cp(argc - 1, argv + 1, &obj);
     else if (STRCMP(argv[1], ==, "rm"))
-        return ad_rm(argc - 1, argv + 1);
+        return ad_rm(argc - 1, argv + 1, &obj);
     else if (STRCMP(argv[1], ==, "mv"))
-        return ad_mv(argc, argv);
+        return ad_mv(argc, argv, &obj);
     else if (STRCMP(argv[1], ==, "find"))
-        return ad_find(argc, argv);
+        return ad_find(argc, argv, &obj);
     else if (STRCMP(argv[1], ==, "-v")) {
         show_version();
         return 1;
index d97f4b0643df9543e9b333179caf533c72af3a53..fcfe1220115c48e43639a65ea3db3e98f681ed0b 100644 (file)
@@ -22,6 +22,7 @@
 #include <signal.h>
 #include <arpa/inet.h>
 
+#include <atalk/globals.h>
 #include <atalk/ftw.h>
 #include <atalk/cnid.h>
 #include <atalk/compat.h>
@@ -56,14 +57,15 @@ typedef struct {
 extern int log_verbose;             /* Logging flag */
 extern void _log(enum logtype lt, char *fmt, ...);
 
-extern int ad_ls(int argc, char **argv);
-extern int ad_cp(int argc, char **argv);
-extern int ad_rm(int argc, char **argv);
-extern int ad_mv(int argc, char **argv);
-extern int ad_find(int argc, char **argv);
+extern int ad_ls(int argc, char **argv, AFPObj *obj);
+extern int ad_cp(int argc, char **argv, AFPObj *obj);
+
+extern int ad_rm(int argc, char **argv, AFPObj *obj);
+extern int ad_mv(int argc, char **argv, AFPObj *obj);
+extern int ad_find(int argc, char **argv, AFPObj *obj);
 
 /* ad_util.c */
-extern int openvol(const char *path, afpvol_t *vol);
+extern int openvol(AFPObj *obj, const char *path, afpvol_t *vol);
 extern void closevol(afpvol_t *vol);
 extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did);
 extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did);
index 40500441ff1c0f043f6ebb9ad58da5f09300aa6e..8f14e3f18a0b3d4d7f292d03d93201125559a6af 100644 (file)
@@ -211,7 +211,7 @@ static void usage_cp(void)
     exit(EXIT_FAILURE);
 }
 
-int ad_cp(int argc, char *argv[])
+int ad_cp(int argc, char *argv[], AFPObj *obj)
 {
     struct stat to_stat, tmp_stat;
     int r, ch, have_trailing_slash;
@@ -351,11 +351,11 @@ int ad_cp(int argc, char *argv[])
 #endif
 
     /* Load .volinfo file for destination*/
-    openvol(to.p_path, &dvolume);
+    openvol(obj, to.p_path, &dvolume);
 
     for (int i = 0; argv[i] != NULL; i++) {
         /* Load .volinfo file for source */
-        openvol(argv[i], &svolume);
+        openvol(obj, argv[i], &svolume);
 
         if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
             if (alarmed) {
index 20c67afb8cfec80955174318e220973380468f8e..88a46752e2f1199b20f506c3a9d0034c912e645b 100644 (file)
@@ -82,7 +82,7 @@ static void usage_find(void)
         );
 }
 
-int ad_find(int argc, char **argv)
+int ad_find(int argc, char **argv, AFPObj *obj)
 {
     int c, ret;
     afpvol_t vol;
@@ -111,7 +111,7 @@ int ad_find(int argc, char **argv)
     set_signal();
     cnid_init();
 
-    if (openvol(srchvol, &vol) != 0)
+    if (openvol(obj, srchvol, &vol) != 0)
         ERROR("Cant open volume \"%s\"", srchvol);
 
     uint16_t flags = CONV_TOLOWER;
index 8cda318a99d2e4afe04ffa4392e981d10d542909..d8362595e4647a7ef54098d4f1711b5b45b5b2ce 100644 (file)
@@ -594,7 +594,7 @@ exit:
     return ret;
 }
 
-int ad_ls(int argc, char **argv)
+int ad_ls(int argc, char **argv, AFPObj *obj)
 {
     int c, firstarg;
     afpvol_t vol;
@@ -630,7 +630,7 @@ int ad_ls(int argc, char **argv)
     cnid_init();
 
     if ((argc - optind) == 0) {
-        openvol(".", &vol);
+        openvol(obj, ".", &vol);
         ad_ls_r(".", &vol);
         closevol(&vol);
     }
@@ -650,7 +650,7 @@ int ad_ls(int argc, char **argv)
             first = 1;
             recursion = 0;
 
-            openvol(argv[optind], &vol);
+            openvol(obj, argv[optind], &vol);
             ad_ls_r(argv[optind], &vol);
             closevol(&vol);
         next:
@@ -672,7 +672,7 @@ int ad_ls(int argc, char **argv)
             first = 1;
             recursion = 0;
 
-            openvol(argv[optind], &vol);
+            openvol(obj, argv[optind], &vol);
             ad_ls_r(argv[optind], &vol);
             closevol(&vol);
 
index 6991b27600296344ca7a62d921350d732659c915..7f592dd27e1aaf0623bb2a2e34038e13a6468b33 100644 (file)
@@ -139,7 +139,7 @@ static void usage_mv(void)
     exit(EXIT_FAILURE);
 }
 
-int ad_mv(int argc, char *argv[])
+int ad_mv(int argc, char *argv[], AFPObj *obj)
 {
     size_t baselen, len;
     int rval;
@@ -184,7 +184,7 @@ int ad_mv(int argc, char *argv[])
 
     set_signal();
     cnid_init();
-    if (openvol(argv[argc - 1], &dvolume) != 0) {
+    if (openvol(obj, argv[argc - 1], &dvolume) != 0) {
         SLOG("Error opening CNID database for source \"%s\": ", argv[argc - 1]);
         return 1;
     }
@@ -196,7 +196,7 @@ int ad_mv(int argc, char *argv[])
     if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
         if (argc > 2)
             usage_mv();
-        if (openvol(argv[0], &svolume) != 0) {
+        if (openvol(obj, argv[0], &svolume) != 0) {
             SLOG("Error opening CNID database for destination \"%s\": ", argv[0]);
             return 1;
         }
@@ -234,7 +234,7 @@ int ad_mv(int argc, char *argv[])
             rval = 1;
         } else {
             memmove(endp, p, (size_t)len + 1);
-            openvol(*argv, &svolume);
+            openvol(obj, *argv, &svolume);
 
             if (do_move(*argv, path))
                 rval = 1;
index e65d6f274ce76230510dccff35cea49891fdf259..195fee06979301eeef8e9e80d1c66c7d34f00904 100644 (file)
@@ -132,7 +132,7 @@ static void usage_rm(void)
     exit(EXIT_FAILURE);
 }
 
-int ad_rm(int argc, char *argv[])
+int ad_rm(int argc, char *argv[], AFPObj *obj)
 {
     int ch;
 
@@ -165,7 +165,7 @@ int ad_rm(int argc, char *argv[])
 
     for (int i = 0; argv[i] != NULL; i++) {
         /* Load .volinfo file for source */
-        openvol(argv[i], &volume);
+        openvol(obj, argv[i], &volume);
 
         if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
             if (alarmed) {
index d7e9fee50dffbbb9fc31548e751268d19e036665..0143a1e4c7abb635c4e8e5c6f4cbdd19d5e204c2 100644 (file)
 #include <atalk/logger.h>
 #include <atalk/errchk.h>
 #include <atalk/unicode.h>
+#include <atalk/globals.h>
 #include <atalk/netatalk_conf.h>
 
+
 #include "ad.h"
 
 int log_verbose;             /* Logging flag */
@@ -96,21 +98,21 @@ void _log(enum logtype lt, char *fmt, ...)
  *
  * @returns 0 on success, exits on error
  */
-int openvol(const char *path, afpvol_t *vol)
+int openvol(AFPObj *obj, const char *path, afpvol_t *vol)
 {
     int flags = 0;
 
     memset(vol, 0, sizeof(afpvol_t));
 
-    /* try to find a .AppleDesktop/.volinfo */
-    if ((vol->vol = getvolbypath(path)) == NULL)
+    if ((vol->vol = getvolbypath(obj, path)) == NULL)
         return -1;
 
     if (STRCMP(vol->vol->v_cnidscheme, != , "dbd"))
         ERROR("\"%s\" isn't a \"dbd\" CNID volume!", vol->vol->v_path);
 
     /* Sanity checks to ensure we can touch this volume */
-    if (vol->vol->v_adouble != AD_VERSION2)
+    if (vol->vol->v_adouble != AD_VERSION2
+        && vol->vol->v_adouble != AD_VERSION_EA)
         ERROR("Unsupported adouble versions: %u", vol->vol->v_adouble);
 
     if (vol->vol->v_vfs_ea != AFPVOL_EA_SYS)
index d197efd5d76d98cda8df018fd967e8619747b6d6..7a1614f7ac4c4f549101e4d9ae85a01489c56a89 100644 (file)
@@ -268,7 +268,7 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);
     }
 
-    if ((vol = getvolbypath(volpath)) == NULL) {
+    if ((vol = getvolbypath(&obj, volpath)) == NULL) {
         dbd_log( LOGSTD, "Couldn't find volume for '%s'", volpath);
         exit(EXIT_FAILURE);
     }
index c7426713c6d34cdb1e0e43a55d54a6d227a4d9f2..0f6e07a2603c1bc5b1dec62e517a2098ad720792 100644 (file)
@@ -113,7 +113,7 @@ static uint maxvol;
 #define DEFAULTPORT  "4700"
 
 struct server {
-    struct vol *vol;
+    char *v_path;
     pid_t pid;
     time_t tm;                    /* When respawned last */
     int count;                    /* Times respawned in the last TESTTIME secondes */
@@ -141,15 +141,12 @@ static void sigterm_handler(int sig)
     daemon_exit(0);
 }
 
-static struct server *test_usockfn(const struct vol *vol)
+static struct server *test_usockfn(const char *path)
 {
     int i;
 
-    if (!(vol->v_flags & AFPVOL_OPEN))
-        return NULL;
-
     for (i = 0; i < maxvol; i++) {
-        if (vol->v_vid == srv[i].vol->v_vid)
+        if (srv[i].v_path && STRCMP(path, ==, srv[i].v_path))
             return &srv[i];
     }
 
@@ -157,7 +154,7 @@ static struct server *test_usockfn(const struct vol *vol)
 }
 
 /* -------------------- */
-static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol)
+static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, const char *volpath)
 {
     pid_t pid;
     struct server *up;
@@ -166,13 +163,13 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol)
     time_t t;
     char buf1[8];
     char buf2[8];
-    char *volpath = vol->v_path;
 
-    LOG(log_debug, logtype_cnid, "maybe_start_dbd: Volume: \"%s\"", volpath);
+    LOG(log_debug, logtype_cnid, "maybe_start_dbd(\"%s\"): BEGIN", volpath);
 
-    up = test_usockfn(vol);
+    up = test_usockfn(volpath);
     if (up && up->pid) {
         /* we already have a process, send our fd */
+        LOG(log_debug, logtype_cnid, "maybe_start_dbd: cnid_dbd[%d] already serving", up->pid);
         if (send_fd(up->control_fd, rqstfd) < 0) {
             /* FIXME */
             return -1;
@@ -180,16 +177,16 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol)
         return 0;
     }
 
-    LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet");
+    LOG(log_debug, logtype_cnid, "maybe_start_dbd: no cnid_dbd serving yet");
 
     time(&t);
     if (!up) {
         /* find an empty slot (i < maxvol) or the first free slot (i == maxvol)*/
         for (i = 0; i <= maxvol; i++) {
-            if (srv[i].vol == NULL && i < MAXVOLS) {
+            if (srv[i].v_path == NULL && i < MAXVOLS) {
                 up = &srv[i];
-                up->vol = vol;
-                vol->v_flags |= AFPVOL_OPEN;
+                if ((up->v_path = strdup(volpath)) == NULL)
+                    return -1;
                 up->tm = t;
                 up->count = 0;
                 if (i == maxvol)
@@ -261,7 +258,7 @@ static int maybe_start_dbd(const AFPObj *obj, char *dbdpn, struct vol *vol)
             /* there's a pb with the db inform child, it will delete the db */
             LOG(log_warning, logtype_cnid,
                 "Multiple attempts to start CNID db daemon for \"%s\" failed, wiping the slate clean...",
-                up->vol->v_path);
+                up->v_path);
             ret = execlp(dbdpn, dbdpn, "-F", obj->options.configfile, "-p", volpath, "-t", buf1, "-l", buf2, "-d", NULL);
         } else {
             ret = execlp(dbdpn, dbdpn, "-F", obj->options.configfile, "-p", volpath, "-t", buf1, "-l", buf2, NULL);
@@ -533,7 +530,8 @@ int main(int argc, char *argv[])
                 if (srv[i].pid == pid) {
                     srv[i].pid = 0;
                     close(srv[i].control_fd);
-                    srv[i].vol->v_flags &= ~AFPVOL_OPEN;
+                    free(srv[i].v_path);
+                    srv[i].v_path = NULL;
                     break;
                 }
             }
@@ -590,7 +588,7 @@ int main(int argc, char *argv[])
             goto loop_end;
         }
 
-        if ((vol = getvolbypath(volpath)) == NULL) {
+        if ((vol = getvolbypath(&obj, volpath)) == NULL) {
             LOG(log_severe, logtype_cnid, "main: no volume for path \"%s\"", volpath);
             goto loop_end;
         }
@@ -599,7 +597,7 @@ int main(int argc, char *argv[])
             goto loop_end;
         }
 
-        maybe_start_dbd(&obj, dbdpn, vol);
+        maybe_start_dbd(&obj, dbdpn, vol->v_path);
     loop_end:
         close(rqstfd);
     }
index fea36b81317376e2ba931b1c307bcd618c290ced..1079fb5839051da06b03f6fd14515708cfd7e9d1 100644 (file)
@@ -318,7 +318,7 @@ int main(int argc, char *argv[])
     setuplog(obj.options.logconfig, obj.options.logfile);
 
     EC_ZERO( load_volumes(&obj, NULL) );
-    EC_NULL( vol = getvolbypath(volpath) );
+    EC_NULL( vol = getvolbypath(&obj, volpath) );
     EC_ZERO( load_charset(vol) );
     pack_setvol(vol);
 
index 60ba4ffb27c4756b505fd900284a8ea9ad306f52..c6b8eb2862fdc56609427bc1189939f8f55e3770 100644 (file)
@@ -54,6 +54,7 @@
  **********************************************************************************************/
 
 #define INISEC_GLOBAL "Global"
+#define INISEC_HOMES  "Homes"
 
 struct DSI;
 #define AFPOBJ_TMPSIZ (MAXPATHLEN)
index 9c189e6385b3b934407f2df6bb254c11d2643185..5715df748fed5f9c05198a0a8b4d3b59db61aeaa 100644 (file)
@@ -27,7 +27,8 @@ extern int        load_volumes(AFPObj *obj, void (*delvol_fn)(struct vol *));
 extern void       unload_volumes(AFPObj *obj);
 extern struct vol *getvolumes(void);
 extern struct vol *getvolbyvid(const uint16_t);
-extern struct vol *getvolbypath(const char *path);
+extern struct vol *getvolbypath(AFPObj *obj, const char *path);
+extern struct vol *getvolbyname(const char *name);
 extern void       volume_free(struct vol *vol);
 extern void       volume_unlink(struct vol *volume);
 #endif
index 18a841a83992e18de82679dbd5862f179a8a668c..6ae66baac1a7ae8304aac5518f039bbdf366581e 100644 (file)
@@ -70,7 +70,8 @@ struct vol {
     
     /* only when opening/closing volumes or in error */
     int             v_casefold;
-    char            *v_localname;   /* as defined in AppleVolumes.default */
+    char            *v_configname;   /* as defined in afpc.conf */
+    char            *v_localname;    /* as defined in afp.conf but with vars expanded */
     char            *v_volcodepage;
     char            *v_maccodepage;
     char            *v_password;
@@ -112,6 +113,7 @@ struct vol {
   Keep in sync with libatalk/util/volinfo.c
 */
 #define AFPVOL_NOV2TOEACONV (1 << 5) /* no adouble:v2 to adouble:ea conversion */
+#define AFPVOL_UNIX_CTXT (1 << 6)   /* volume created by getvolbypath ie UNIX access, not afpd AFP user session */
 #define AFPVOL_RO        (1 << 8)   /* read-only volume */
 #define AFPVOL_NOHEX     (1 << 10)  /* don't do :hex translation */
 #define AFPVOL_USEDOTS   (1 << 11)  /* use real dots */
index 805a9ad0d081723501d4831af35733f6adcfb258..507bd210e65f268c1d0f9761502f7f2daf693d2d 100644 (file)
@@ -52,6 +52,7 @@
  * Locals
  **************************************************************/
 
+static int have_uservol = 0; /* whether there's generic user home share in config ("~" or "~/path", but not "~user") */
 static struct vol *Volumes = NULL;
 static uint16_t    lastvid = 0;
 
@@ -320,8 +321,6 @@ static char *volxlate(const AFPObj *obj,
         /* now figure out what the variable is */
         q = NULL;
         if (IS_VAR(p, "$b")) {
-            if (!obj->uid && xlatevolname)
-                return NULL;
             if (path) {
                 if ((q = strrchr(path, '/')) == NULL)
                     q = path;
@@ -329,8 +328,6 @@ static char *volxlate(const AFPObj *obj,
                     q++;
             }
         } else if (IS_VAR(p, "$c")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             DSI *dsi = obj->dsi;
             len = sprintf(dest, "%s:%u",
                           getip_string((struct sockaddr *)&dsi->client),
@@ -338,41 +335,29 @@ static char *volxlate(const AFPObj *obj,
             dest += len;
             destlen -= len;
         } else if (IS_VAR(p, "$d")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             q = path;
         } else if (pwd && IS_VAR(p, "$f")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             if ((r = strchr(pwd->pw_gecos, ',')))
                 *r = '\0';
             q = pwd->pw_gecos;
         } else if (pwd && IS_VAR(p, "$g")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             struct group *grp = getgrgid(pwd->pw_gid);
             if (grp)
                 q = grp->gr_name;
         } else if (IS_VAR(p, "$h")) {
             q = obj->options.hostname;
         } else if (IS_VAR(p, "$i")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             DSI *dsi = obj->dsi;
             q = getip_string((struct sockaddr *)&dsi->client);
         } else if (IS_VAR(p, "$s")) {
             q = obj->options.hostname;
         } else if (obj->username && IS_VAR(p, "$u")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             char* sep = NULL;
             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
                 q = sep+1;
             else
                 q = obj->username;
         } else if (IS_VAR(p, "$v")) {
-            if (!obj->uid  && xlatevolname)
-                return NULL;
             if (volname) {
                 q = volname;
             }
@@ -507,7 +492,7 @@ static int hostaccessvol(const AFPObj *obj, const char *volname, const char *arg
  * Get option string from config, use default value if not set
  *
  * @param conf    (r) config handle
- * @param vol     (r) volume name
+ * @param vol     (r) volume name (must be section name ie wo vars expanded)
  * @param opt     (r) option
  * @param def     (r) if "option" is not found in "name", try to find it in section "def"
  *
@@ -530,16 +515,18 @@ EC_CLEANUP:
  *
  * @param obj      (r) handle
  * @param pwd      (r) struct passwd of logged in user, may be NULL in master afpd
- * @param path     (r) volume path
+ * @param section  (r) volume name wo variables expanded (exactly as in iniconfig)
  * @param name     (r) volume name
+ * @param path     (r) volume path
  * @param preset   (r) default preset, may be NULL
- * @returns               0 on success, -1 on error
+ * @returns            vol on success, NULL on error
  */
-static int creatvol(AFPObj *obj,
-                    const struct passwd *pwd,
-                    const char *path,
-                    const char *name,
-                    const char *preset)
+static struct vol *creatvol(AFPObj *obj,
+                            const struct passwd *pwd,
+                            const char *section,
+                            const char *name,
+                            const char *path,
+                            const char *preset)
 {
     EC_INIT;
     struct vol  *volume = NULL;
@@ -566,9 +553,10 @@ static int creatvol(AFPObj *obj,
 
     /* Once volumes are loaded, we never change options again, we just delete em when they're removed from afp.conf */
     for (struct vol *vol = Volumes; vol; vol = vol->v_next) {
-        if (STRCMP(name, ==, vol->v_localname)) {
+        if (STRCMP(path, ==, vol->v_path)) {
             LOG(log_debug, logtype_afpd, "createvol('%s'): already loaded", name);
             vol->v_deleted = 0;
+            volume = vol;
             goto EC_CLEANUP;
         }
     }
@@ -579,75 +567,77 @@ static int creatvol(AFPObj *obj,
      * deny -> either no list (-1), or not in list (0)
      */
     if (pwd) {
-        if (accessvol(obj, getoption(obj->iniconfig, name, "deny", preset), pwd->pw_name) == 1)
+        if (accessvol(obj, getoption(obj->iniconfig, section, "deny", preset), pwd->pw_name) == 1)
             goto EC_CLEANUP;
-        if (accessvol(obj, getoption(obj->iniconfig, name, "allow", preset), pwd->pw_name) == 0)
+        if (accessvol(obj, getoption(obj->iniconfig, section, "allow", preset), pwd->pw_name) == 0)
             goto EC_CLEANUP;
-        if (hostaccessvol(obj, name, getoption(obj->iniconfig, name, "denied_hosts", preset)) == 1)
+        if (hostaccessvol(obj, section, getoption(obj->iniconfig, section, "denied_hosts", preset)) == 1)
             goto EC_CLEANUP;
-        if (hostaccessvol(obj, name, getoption(obj->iniconfig, name, "allowed_hosts", preset)) == 0)
+        if (hostaccessvol(obj, section, getoption(obj->iniconfig, section, "allowed_hosts", preset)) == 0)
             goto EC_CLEANUP;
     }
 
     EC_NULL( volume = calloc(1, sizeof(struct vol)) );
 
     volume->v_flags = AFPVOL_USEDOTS | AFPVOL_UNIX_PRIV;
+    EC_NULL( volume->v_configname = strdup(section));
+
 #ifdef HAVE_ACLS
     volume->v_flags |= AFPVOL_ACLS;
 #endif
     volume->v_vfs_ea = AFPVOL_EA_AUTO;
     volume->v_umask = obj->options.umask;
 
-    if (val = getoption(obj->iniconfig, name, "password", preset))
+    if (val = getoption(obj->iniconfig, section, "password", preset))
         EC_NULL( volume->v_password = strdup(val) );
 
-    if (val = getoption(obj->iniconfig, name, "veto", preset))
+    if (val = getoption(obj->iniconfig, section, "veto", preset))
         EC_NULL( volume->v_password = strdup(val) );
 
-    if (val = getoption(obj->iniconfig, name, "volcharset", preset))
+    if (val = getoption(obj->iniconfig, section, "volcharset", preset))
         EC_NULL( volume->v_volcodepage = strdup(val) );
     else
         EC_NULL( volume->v_volcodepage = strdup("UTF8") );
 
-    if (val = getoption(obj->iniconfig, name, "maccharset", preset))
+    if (val = getoption(obj->iniconfig, section, "maccharset", preset))
         EC_NULL( volume->v_maccodepage = strdup(val) );
     else
         EC_NULL( volume->v_maccodepage = strdup(obj->options.maccodepage) );
 
-    if (val = getoption(obj->iniconfig, name, "dbpath", preset))
+    if (val = getoption(obj->iniconfig, section, "dbpath", preset))
         EC_NULL( volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, name, "cnidscheme", preset))
+    if (val = getoption(obj->iniconfig, section, "cnidscheme", preset))
         EC_NULL( volume->v_cnidscheme = strdup(val) );
 
-    if (val = getoption(obj->iniconfig, name, "umask", preset))
+    if (val = getoption(obj->iniconfig, section, "umask", preset))
         volume->v_umask = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, name, "dperm", preset))
+    if (val = getoption(obj->iniconfig, section, "dperm", preset))
         volume->v_dperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, name, "fperm", preset))
+    if (val = getoption(obj->iniconfig, section, "fperm", preset))
         volume->v_fperm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, name, "perm", preset))
+    if (val = getoption(obj->iniconfig, section, "perm", preset))
         volume->v_perm = (int)strtol(val, NULL, 8);
 
-    if (val = getoption(obj->iniconfig, name, "volsizelimit", preset))
+    if (val = getoption(obj->iniconfig, section, "volsizelimit", preset))
         volume->v_limitsize = (uint32_t)strtoul(val, NULL, 10);
 
-    if (val = getoption(obj->iniconfig, name, "preexec", preset))
+    if (val = getoption(obj->iniconfig, section, "preexec", preset))
         EC_NULL( volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, name, "postexec", preset))
+    if (val = getoption(obj->iniconfig, section, "postexec", preset))
         EC_NULL( volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, name, "root_preexec", preset))
+    if (val = getoption(obj->iniconfig, section, "root_preexec", preset))
         EC_NULL( volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, name, "root_postexec", preset))
+    if (val = getoption(obj->iniconfig, section, "root_postexec", preset))
         EC_NULL( volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, val, pwd, path, name) );
 
-    if (val = getoption(obj->iniconfig, name, "adouble", preset)) {
+    if (val = getoption(obj->iniconfig, section, "adouble", preset)) {
         if (strcmp(val, "v2") == 0)
             volume->v_adouble = AD_VERSION2;
         else if (strcmp(val, "ea") == 0)
@@ -656,7 +646,7 @@ static int creatvol(AFPObj *obj,
         volume->v_adouble = AD_VERSION;
     }
 
-    if (val = getoption(obj->iniconfig, name, "cnidserver", preset)) {
+    if (val = getoption(obj->iniconfig, section, "cnidserver", preset)) {
         EC_NULL( p = strdup(val) );
         volume->v_cnidserver = p;
         if (q = strrchr(val, ':')) {
@@ -668,7 +658,7 @@ static int creatvol(AFPObj *obj,
 
     }
 
-    if (val = getoption(obj->iniconfig, name, "ea", preset)) {
+    if (val = getoption(obj->iniconfig, section, "ea", preset)) {
         if (strcasecmp(val, "ad") == 0)
             volume->v_vfs_ea = AFPVOL_EA_AD;
         else if (strcasecmp(val, "sys") == 0)
@@ -677,7 +667,7 @@ static int creatvol(AFPObj *obj,
             volume->v_vfs_ea = AFPVOL_EA_NONE;
     }
 
-    if (val = getoption(obj->iniconfig, name, "casefold", preset)) {
+    if (val = getoption(obj->iniconfig, section, "casefold", preset)) {
         if (strcasecmp(val, "tolower") == 0)
             volume->v_casefold = AFPVOL_UMLOWER;
         else if (strcasecmp(val, "toupper") == 0)
@@ -688,7 +678,7 @@ static int creatvol(AFPObj *obj,
             volume->v_casefold = AFPVOL_ULOWERMUPPER;
     }
 
-    if (val = getoption(obj->iniconfig, name, "options", preset)) {
+    if (val = getoption(obj->iniconfig, section, "options", preset)) {
         q = strdup(val);
         if (p = strtok(q, ", ")) {
             while (p) {
@@ -737,8 +727,8 @@ static int creatvol(AFPObj *obj,
      * 3) rwlist exists -> ro unless user is in it.
      */
     if (pwd) {
-        if (accessvol(obj, getoption(obj->iniconfig, name, "rolist", preset), pwd->pw_name) == 1
-            || accessvol(obj, getoption(obj->iniconfig, name, "rwlist", preset), pwd->pw_name) == 0)
+        if (accessvol(obj, getoption(obj->iniconfig, section, "rolist", preset), pwd->pw_name) == 1
+            || accessvol(obj, getoption(obj->iniconfig, section, "rwlist", preset), pwd->pw_name) == 0)
             volume->v_flags |= AFPVOL_RO;
     }
 
@@ -775,6 +765,7 @@ static int creatvol(AFPObj *obj,
     /* because v_vid has not been decided yet. */
     suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
 
+
     vlen = strlen( name );
 
     /* Unicode Volume Name */
@@ -891,14 +882,15 @@ static int creatvol(AFPObj *obj,
     volume->v_obj = obj;
 
 EC_CLEANUP:
+    LOG(log_debug, logtype_afpd, "createvol: END: %d", ret);
     if (ret != 0) {
         if (volume) {
             volume_free(volume);
             free(volume);
         }
+        return NULL;
     }
-    LOG(log_debug, logtype_afpd, "createvol: END: %d", ret);
-    EC_EXIT;
+    return volume;
 }
 
 /* ----------------------
@@ -934,7 +926,9 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
     char        volname[AFPVOL_U8MNAMELEN + 1];
     char        tmp[MAXPATHLEN + 1];
     const char  *preset, *default_preset, *p;
+    char        *q, *u;
     int         i;
+    struct passwd   *pw;
 
     LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
 
@@ -942,8 +936,7 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
     LOG(log_debug, logtype_afpd, "readvolfile: sections: %d", secnum);
     const char *secname;
 
-    if ((p = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL))) {
-        default_preset = p;
+    if ((default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL))) {
         LOG(log_debug, logtype_afpd, "readvolfile: default_preset: %s", default_preset);
     }
 
@@ -952,25 +945,44 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
 
         if (!vol_section(secname))
             continue;
+        if (STRCMP(secname, ==, INISEC_HOMES)) {
+            have_uservol = 1;
+            if (obj->username[0] == 0)
+                /* not an AFP session, but cnid daemon, dbd or ad util */
+                continue;
+            if ((p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir", NULL)) == NULL)
+                continue;
+            strlcpy(tmp, p, MAXPATHLEN);
+            strlcat(tmp, "/", MAXPATHLEN);
+            strlcat(tmp, obj->username, MAXPATHLEN);
+            strlcat(tmp, "/", MAXPATHLEN);
+            if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
+                strlcat(tmp, p, MAXPATHLEN);
+        } else {
+            /* Get path */
+            if ((p = iniparser_getstring(obj->iniconfig, secname, "path", NULL)) == NULL)
+                continue;
+            strlcpy(tmp, p, MAXPATHLEN);
+        }
 
-        /* Get path */
-        if ((p = iniparser_getstring(obj->iniconfig, secname, "path", NULL)) == NULL)
-            continue;
-        strlcpy(tmp, p, MAXPATHLEN);
         if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
             continue;
 
-        strlcpy(tmp, secname, AFPVOL_U8MNAMELEN);
-        LOG(log_debug, logtype_afpd, "readvolfile: volume: %s", volname);
-
-        /* do variable substitution for volname */
-        if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, path, NULL) == NULL) {
-            continue;
+        /* do variable substitution for volume name */
+        if (STRCMP(secname, ==, INISEC_HOMES)) {
+            if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "name", "$u's home"))
+                strlcpy(tmp, p, MAXPATHLEN);
+            else
+                strlcpy(tmp, p, MAXPATHLEN);
+        } else {
+            strlcpy(tmp, secname, AFPVOL_U8MNAMELEN);
         }
+        if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, path, NULL) == NULL)
+            continue;
 
         preset = iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
 
-        creatvol(obj, pwent, path, volname, preset ? preset : default_preset ? default_preset : NULL);
+        creatvol(obj, pwent, secname, volname, path, preset ? preset : default_preset ? default_preset : NULL);
     }
 
 EC_CLEANUP:
@@ -1075,8 +1087,12 @@ int load_volumes(AFPObj *obj, void (*delvol_fn)(struct vol *))
     if (Volumes) {
         if (!volfile_changed(&obj->options))
             goto EC_CLEANUP;
-        for (vol = Volumes; vol; vol = vol->v_next)
+        have_uservol = 0;
+        for (vol = Volumes; vol; vol = vol->v_next) {
+            if (vol->v_flags & AFPVOL_UNIX_CTXT)
+                continue;
             vol->v_deleted = 1;
+        }
     } else {
         LOG(log_debug, logtype_afpd, "load_volumes: no volumes yet");
         EC_ZERO_LOG( lstat(obj->options.configfile, &st) );
@@ -1163,13 +1179,109 @@ struct vol *getvolbyvid(const uint16_t vid )
     return( vol );
 }
 
-struct vol *getvolbypath(const char *path)
+struct vol *getvolbypath(AFPObj *obj, const char *path)
 {
-    struct vol *vol = NULL;
+    EC_INIT;
+    struct vol *vol;
     struct vol *tmp;
+    const struct passwd *pw;
+    char        volname[AFPVOL_U8MNAMELEN + 1];
+    char        volpath[MAXPATHLEN + 1];
+    char        tmpbuf[MAXPATHLEN + 1];
+    const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
+    char *user = NULL, *prw;
+
+    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\")", path);
 
     for (tmp = Volumes; tmp; tmp = tmp->v_next) {
         if (strncmp(path, tmp->v_path, strlen(tmp->v_path)) == 0) {
+            vol = tmp;
+            goto EC_CLEANUP;
+        }
+    }
+
+    /* might be a user home, check for that and create a volume if yes */
+    if (!have_uservol)
+        EC_FAIL;
+
+    int secnum = iniparser_getnsec(obj->iniconfig);
+
+    for (int i = 0; i < secnum; i++) { 
+        secname = iniparser_getsecname(obj->iniconfig, i);
+        if (STRCMP(secname, ==, INISEC_HOMES))
+            break;
+    }
+
+    if (STRCMP(secname, !=, INISEC_HOMES))
+        EC_FAIL;
+
+    EC_NULL_LOG( basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir", NULL) );
+
+    LOG(log_debug, logtype_afpd, "getvolbypath: user home section: '%s', basedir: '%s'", secname, basedir);
+
+    if (strncmp(path, basedir, strlen(basedir)) != 0)
+        EC_FAIL;
+
+    strlcpy(tmpbuf, basedir, MAXPATHLEN);
+    strlcat(tmpbuf, "/", MAXPATHLEN);
+
+    p = path + strlen(basedir);
+    while (*p == '/')
+        p++;
+    EC_NULL_LOG( user = strdup(p) );
+
+    if (prw = strchr(user, '/'))
+        *prw++ = 0;
+    if (prw != 0)
+        subpath = prw;
+
+    strlcat(tmpbuf, user, MAXPATHLEN);
+    strlcat(tmpbuf, "/", MAXPATHLEN);
+
+    if (subpathconfig = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL)) {
+        if (!subpath || strncmp(subpathconfig, subpath, strlen(subpathconfig)) != 0) {
+            EC_FAIL;
+        }
+    }
+
+    strlcat(tmpbuf, subpathconfig, MAXPATHLEN);
+    strlcat(tmpbuf, "/", MAXPATHLEN);
+
+    if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
+        return NULL;
+
+    EC_NULL( pw = getpwnam(user) );
+
+    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => volpath: \"%s\"",
+        path, user, pw->pw_dir, volpath);
+
+    /* do variable substitution for volume name */
+    p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "name", "$u's home");
+    strlcpy(tmpbuf, p, AFPVOL_U8MNAMELEN);
+    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, volpath, NULL) );
+
+    const char  *preset, *default_preset;
+    default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL);
+    preset = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "vol preset", NULL);
+
+    vol = creatvol(obj, pw, INISEC_HOMES, volname, volpath, preset ? preset : default_preset ? default_preset : NULL);
+
+EC_CLEANUP:
+    endpwent();
+    if (user)
+        free(user);
+    if (ret != 0)
+        vol = NULL;
+    return vol;
+}
+
+struct vol *getvolbyname(const char *name)
+{
+    struct vol *vol = NULL;
+    struct vol *tmp;
+
+    for (tmp = Volumes; tmp; tmp = tmp->v_next) {
+        if (strncmp(name, tmp->v_configname, strlen(tmp->v_configname)) == 0) {
             vol = tmp;
             break;
         }