]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/util/netatalk_conf.c
Merge remote branch 'netafp/master' into branch-allea
[netatalk.git] / libatalk / util / netatalk_conf.c
index 507bd210e65f268c1d0f9761502f7f2daf693d2d..8a4cfb2afe67ca42d3ee4e4eb837bd82ed843874 100644 (file)
@@ -30,6 +30,7 @@
 #include <arpa/inet.h>
 #include <inttypes.h>
 #include <time.h>
+#include <regex.h>
 
 #include <atalk/afp.h>
 #include <atalk/util.h>
@@ -45,6 +46,9 @@
 #include <atalk/netatalk_conf.h>
 
 #define VOLPASSLEN  8
+#ifndef UUID_PRINTABLE_STRING_LENGTH
+#define UUID_PRINTABLE_STRING_LENGTH 37
+#endif
 
 #define IS_VAR(a, b) (strncmp((a), (b), 2) == 0)
 
@@ -178,7 +182,7 @@ static int do_check_ea_support(const struct vol *vol)
         sys_removexattr(vol->v_path, eaname);
         haseas = 1;
     } else {
-        LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes",
+        LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes or read-only volume root",
             vol->v_localname);
         haseas = 0;
     }
@@ -436,7 +440,7 @@ static int hostaccessvol(const AFPObj *obj, const char *volname, const char *arg
     struct sockaddr_storage client;
     const DSI *dsi = obj->dsi;
 
-    if (!args)
+    if (!args || !dsi)
         return -1;
 
     strlcpy(buf, args, sizeof(buf));
@@ -503,7 +507,7 @@ static const char *getoption(const dictionary *conf, const char *vol, const char
     EC_INIT;
     const char *result = NULL;
 
-    if (!(result = iniparser_getstring(conf, vol, opt, NULL)))
+    if ((!(result = iniparser_getstring(conf, vol, opt, NULL))) && (def != NULL))
         result = iniparser_getstring(conf, def, opt, NULL);
     
 EC_CLEANUP:
@@ -592,7 +596,7 @@ static struct vol *creatvol(AFPObj *obj,
         EC_NULL( volume->v_password = strdup(val) );
 
     if (val = getoption(obj->iniconfig, section, "veto", preset))
-        EC_NULL( volume->v_password = strdup(val) );
+        EC_NULL( volume->v_veto = strdup(val) );
 
     if (val = getoption(obj->iniconfig, section, "volcharset", preset))
         EC_NULL( volume->v_volcodepage = strdup(val) );
@@ -609,6 +613,8 @@ static struct vol *creatvol(AFPObj *obj,
 
     if (val = getoption(obj->iniconfig, section, "cnidscheme", preset))
         EC_NULL( volume->v_cnidscheme = strdup(val) );
+    else
+        volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
 
     if (val = getoption(obj->iniconfig, section, "umask", preset))
         volume->v_umask = (int)strtol(val, NULL, 8);
@@ -651,11 +657,14 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_cnidserver = p;
         if (q = strrchr(val, ':')) {
             *q++ = 0;
-            volume->v_cnidport = q;
+            volume->v_cnidport = strdup(q);
         } else {
-            volume->v_cnidport = "4700";
+            volume->v_cnidport = strdup("4700");
         }
 
+    } else {
+        volume->v_cnidserver = strdup(obj->options.Cnid_srv);
+        volume->v_cnidport = strdup(obj->options.Cnid_port);
     }
 
     if (val = getoption(obj->iniconfig, section, "ea", preset)) {
@@ -947,14 +956,11 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
             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)
+            if (obj->username[0] == 0
+                || strcmp(obj->username, obj->options.guest) == 0)
+                /* not an AFP session, but cnid daemon, dbd or ad util, or guest login */
                 continue;
-            strlcpy(tmp, p, MAXPATHLEN);
-            strlcat(tmp, "/", MAXPATHLEN);
-            strlcat(tmp, obj->username, MAXPATHLEN);
+            strlcpy(tmp, pwent->pw_dir, MAXPATHLEN);
             strlcat(tmp, "/", MAXPATHLEN);
             if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
                 strlcat(tmp, p, MAXPATHLEN);
@@ -1037,10 +1043,7 @@ void volume_free(struct vol *vol)
     free(vol->v_gvs);
     free(vol->v_uuid);
     free(vol->v_cnidserver);
-#if 0
-    /* NO! Just points to v_cnidserver + x */
     free(vol->v_cnidport);
-#endif
     free(vol->v_root_preexec);
     free(vol->v_postexec);
 
@@ -1179,6 +1182,27 @@ struct vol *getvolbyvid(const uint16_t vid )
     return( vol );
 }
 
+/*!
+ * Search volume by path, creating user home vols as necessary
+ *
+ * Path may be absolute or relative. Ordinary volume structs are created when
+ * the ini config is initially parsed (load_volumes()), but user volumes are
+ * as load_volumes() only can create the user volume of the logged in user
+ * in an AFP session in afpd, but not when called from eg cnid_metad or dbd.
+ * Both cnid_metad and dbd thus need a way to lookup and create struct vols
+ * for user home by path. This is what this func does as well.
+ *
+ * (1) Search "normal" volume list 
+ * (2) Check if theres a [Homes] section, load_volumes() remembers this for us
+ * (3) If there is, match "path" with "basedir regex" to get the user home parent dir
+ * (4) Built user home path by appending the basedir matched in (3) and appending the username
+ * (5) The next path element then is the username
+ * (6) Append [Homes]->path subdirectory if defined
+ * (7) Create volume
+ *
+ * @param obj  (rw) handle
+ * @param path (r)  path, may be relative or absolute
+ */
 struct vol *getvolbypath(AFPObj *obj, const char *path)
 {
     EC_INIT;
@@ -1186,23 +1210,35 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     struct vol *tmp;
     const struct passwd *pw;
     char        volname[AFPVOL_U8MNAMELEN + 1];
+    char        abspath[MAXPATHLEN + 1];
     char        volpath[MAXPATHLEN + 1];
     char        tmpbuf[MAXPATHLEN + 1];
     const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
     char *user = NULL, *prw;
+    int regexerr = -1;
+    static regex_t reg;
+    regmatch_t match[1];
 
     LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\")", path);
 
-    for (tmp = Volumes; tmp; tmp = tmp->v_next) {
+    if (path[0] != '/') {
+        /* relative path, build absolute path */
+        EC_NULL_LOG( getcwd(abspath, MAXPATHLEN) );
+        strlcat(abspath, "/", MAXPATHLEN);
+        strlcat(abspath, path, MAXPATHLEN);
+        path = abspath;
+    }
+
+
+    for (tmp = Volumes; tmp; tmp = tmp->v_next) { /* (1) */
         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;
+    if (!have_uservol) /* (2) */
+        EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
 
     int secnum = iniparser_getnsec(obj->iniconfig);
 
@@ -1213,18 +1249,35 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     }
 
     if (STRCMP(secname, !=, INISEC_HOMES))
-        EC_FAIL;
-
-    EC_NULL_LOG( basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir", NULL) );
+        EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
 
+    /* (3) */
+    EC_NULL_LOG( basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL) );
     LOG(log_debug, logtype_afpd, "getvolbypath: user home section: '%s', basedir: '%s'", secname, basedir);
 
-    if (strncmp(path, basedir, strlen(basedir)) != 0)
-        EC_FAIL;
+    if (regexerr != 0 && (regexerr = regcomp(&reg, basedir, REG_EXTENDED)) != 0) {
+        char errbuf[1024];
+        regerror(regexerr, &reg, errbuf, sizeof(errbuf));
+        printf("error: %s\n", errbuf);
+        EC_FAIL_LOG("getvolbypath(\"%s\"): bad basedir regex: %s", errbuf);
+    }
+
+    if (regexec(&reg, path, 1, match, 0) == REG_NOMATCH)
+        EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
+
+    if (match[0].rm_eo - match[0].rm_so > MAXPATHLEN)
+        EC_FAIL_LOG("getvolbypath(\"%s\"): path too long", path);
+
+    /* (4) */
+    strncpy(tmpbuf, path + match[0].rm_so, match[0].rm_eo - match[0].rm_so);
+    tmpbuf[match[0].rm_eo - match[0].rm_so] = 0;
+
+    LOG(log_debug, logtype_afpd, "getvolbypath: basedir regex: '%s', basedir match: \"%s\"",
+        basedir, tmpbuf);
 
-    strlcpy(tmpbuf, basedir, MAXPATHLEN);
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
+    /* (5) */
     p = path + strlen(basedir);
     while (*p == '/')
         p++;
@@ -1235,18 +1288,21 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     if (prw != 0)
         subpath = prw;
 
+    strlcpy(obj->username, user, MAXUSERLEN);
     strlcat(tmpbuf, user, MAXPATHLEN);
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
+    /* (6) */
     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);
     }
 
-    strlcat(tmpbuf, subpathconfig, MAXPATHLEN);
-    strlcat(tmpbuf, "/", MAXPATHLEN);
 
+    /* (7) */
     if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
         return NULL;
 
@@ -1267,7 +1323,6 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     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)
@@ -1318,7 +1373,7 @@ int afp_config_parse(AFPObj *AFPObj)
     options->logfile   = iniparser_getstrdup(config, INISEC_GLOBAL, "logfile",  NULL);
 
     /* [AFP] "options" options wo values */
-    if (p = iniparser_getstrdup(config, INISEC_GLOBAL, "options", NULL)) {
+    if (q = iniparser_getstrdup(config, INISEC_GLOBAL, "options", NULL)) {
         if (p = strtok(q, ", ")) {
             while (p) {
                 if (strcasecmp(p, "nozeroconf"))
@@ -1348,6 +1403,7 @@ int afp_config_parse(AFPObj *AFPObj)
                 p = strtok(NULL, ", ");
             }
         }
+        free(q);
     }
     /* figure out options w values */