+/*!
+ * 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;
+ static int regexerr = -1;
+ static regex_t reg;
+ struct vol *vol;
+ 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;
+ regmatch_t match[1];
+
+ LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\")", path);
+
+ 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;
+ }
+ }
+
+ if (!have_uservol) /* (2) */
+ EC_FAIL_LOG("getvolbypath(\"%s\"): no volume for path", path);
+
+ 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_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 (regexerr != 0 && (regexerr = regcomp(®, basedir, REG_EXTENDED)) != 0) {
+ char errbuf[1024];
+ regerror(regexerr, ®, errbuf, sizeof(errbuf));
+ printf("error: %s\n", errbuf);
+ EC_FAIL_LOG("getvolbypath(\"%s\"): bad basedir regex: %s", errbuf);
+ }
+
+ if (regexec(®, 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);
+
+ strlcat(tmpbuf, "/", MAXPATHLEN);
+
+ /* (5) */
+ 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(obj->username, user, MAXUSERLEN);
+ 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);
+ }
+
+
+ /* (7) */
+ 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, "home 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:
+ if (user)
+ free(user);
+ if (ret != 0)
+ vol = NULL;
+ return vol;
+}
+
+struct vol *getvolbyname(const char *name)