#include <arpa/inet.h>
#include <inttypes.h>
#include <time.h>
+#include <regex.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#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)
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;
}
struct sockaddr_storage client;
const DSI *dsi = obj->dsi;
- if (!args)
+ if (!args || !dsi)
return -1;
strlcpy(buf, args, sizeof(buf));
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:
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) );
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);
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)) {
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);
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);
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;
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);
}
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(®, 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);
- strlcpy(tmpbuf, basedir, MAXPATHLEN);
strlcat(tmpbuf, "/", MAXPATHLEN);
+ /* (5) */
p = path + strlen(basedir);
while (*p == '/')
p++;
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;
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)
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"))
p = strtok(NULL, ", ");
}
}
+ free(q);
}
/* figure out options w values */