return result;
}
+/*!
+ * Get boolean option from volume, default section or global - use default value if not set
+ *
+ * Order of precedence: volume -> default section -> global -> default value
+ *
+ * "vdg" means volume, default section or global
+ *
+ * @param conf (r) config handle
+ * @param vol (r) volume name (must be section name ie wo vars expanded)
+ * @param opt (r) option
+ * @param defsec (r) if "option" is not found in "vol", try to find it in section "defsec"
+ * @param defval (r) if neither "vol" nor "defsec" contain "opt" return "defval"
+ *
+ * @returns const option string from "vol" or "defsec", or "defval" if not found
+ */
+static int vdgoption_bool(const dictionary *conf, const char *vol, const char *opt, const char *defsec, int defval)
+{
+ int result;
+
+ result = atalk_iniparser_getboolean(conf, vol, opt, -1);
+
+ if ((result == -1) && (defsec != NULL))
+ result = atalk_iniparser_getboolean(conf, defsec, opt, -1);
+
+ if (result == -1)
+ result = atalk_iniparser_getboolean(conf, INISEC_GLOBAL, opt, defval);
+
+ return result;
+}
+
/*!
* Create volume struct
*
uint16_t flags;
const char *val;
char *p, *q;
+ bstring dbpath = NULL;
+ bstring global_path_tmp = NULL;
strlcpy(path, path_in, MAXPATHLEN);
- LOG(log_debug, logtype_afpd, "createvol(volume: '%s', path: \"%s\", preset: '%s'): BEGIN",
+ LOG(log_debug, logtype_afpd, "creatvol(volume: '%s', path: \"%s\", preset: '%s'): BEGIN",
name, path, preset ? preset : "-");
if ( name == NULL || *name == '\0' ) {
if(tmpname[i] == '/') tmpname[i] = ':';
- if (!atalk_iniparser_getboolean(obj->iniconfig, INISEC_GLOBAL, "vol dbnest", 0)) {
- bstring dbpath;
- EC_NULL( val = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
- EC_NULL( dbpath = bformat("%s/%s/", val, tmpname) );
- EC_NULL( volume->v_dbpath = strdup(cfrombstr(dbpath)) );
- bdestroy(dbpath);
- } else {
+ if (atalk_iniparser_getboolean(obj->iniconfig, INISEC_GLOBAL, "vol dbnest", 0)) {
EC_NULL( volume->v_dbpath = strdup(path) );
+ } else {
+ char *global_path;
+ val = getoption(obj->iniconfig, section, "vol dbpath", preset, NULL);
+ if (val == NULL) {
+ /* check global option */
+ global_path = atalk_iniparser_getstring(obj->iniconfig,
+ INISEC_GLOBAL,
+ "vol dbpath",
+ NULL);
+ if (global_path) {
+ /* check for pre 3.1.1 behaviour without variable */
+ if (strchr(global_path, '$') == NULL) {
+ global_path_tmp = bformat("%s/%s/", global_path, tmpname);
+ val = cfrombstr(global_path_tmp);
+ } else {
+ val = global_path;
+ }
+ }
+ }
+
+ if (val == NULL) {
+ EC_NULL( dbpath = bformat("%s/%s/", _PATH_STATEDIR "CNID/", tmpname) );
+ } else {
+ EC_NULL( dbpath = bfromcstr(val));
+ }
+ EC_NULL( volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN + 1,
+ cfrombstr(dbpath), pwd, NULL, tmpname) );
}
if ((val = getoption(obj->iniconfig, section, "cnid scheme", preset, NULL)))
volume->v_preexec_close = 1;
if (getoption_bool(obj->iniconfig, section, "root preexec close", preset, 0))
volume->v_root_preexec_close = 1;
+ if (vdgoption_bool(obj->iniconfig, section, "force xattr with sticky bit", preset, 0))
+ volume->v_flags |= AFPVOL_FORCE_STICKY_XATTR;
if ((val = getoption(obj->iniconfig, section, "ignored attributes", preset, obj->options.ignored_attr))) {
if (strstr(val, "all")) {
}
}
+ val = getoption(obj->iniconfig, section, "chmod request", preset, NULL);
+ if (val == NULL) {
+ val = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "chmod request", "preserve");
+ }
+ if (strcasecmp(val, "ignore") == 0) {
+ volume->v_flags |= AFPVOL_CHMOD_IGNORE;
+ } else if (strcasecmp(val, "preserve") == 0) {
+ volume->v_flags |= AFPVOL_CHMOD_PRESERVE_ACL;
+ } else if (strcasecmp(val, "simple") != 0) {
+ LOG(log_warning, logtype_afpd, "unknown 'chmod request' setting: '%s', using default", val);
+ volume->v_flags |= AFPVOL_CHMOD_PRESERVE_ACL;
+ }
+
/*
* Handle read-only behaviour. semantics:
* 1) neither the rolist nor the rwlist exist -> rw
volume->v_ad_options |= ADVOL_FOLLO_SYML;
if ((volume->v_flags & AFPVOL_RO))
volume->v_ad_options |= ADVOL_RO;
+ if ((volume->v_flags & AFPVOL_FORCE_STICKY_XATTR))
+ volume->v_ad_options |= ADVOL_FORCE_STICKY_XATTR;
/* Mac to Unix conversion flags*/
if ((volume->v_flags & AFPVOL_EILSEQ))
if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
EC_FAIL;
- LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
+ LOG(log_maxdebug, logtype_afpd, "creatvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
/* Maccharset Volume Name */
/* Firsty convert name from unixcharset to maccharset */
AFPVOL_U8MNAMELEN*2)) )
EC_FAIL;
- LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
+ LOG(log_maxdebug, logtype_afpd, "creatvol: Volume '%s' -> Longname: '%s'", name, tmpname);
EC_NULL( volume->v_localname = strdup(name) );
EC_NULL( volume->v_u8mname = strdup_w(u8mtmpname) );
volume->v_obj = obj;
EC_CLEANUP:
- LOG(log_debug, logtype_afpd, "createvol: END: %d", ret);
+ LOG(log_debug, logtype_afpd, "creatvol: END: %d", ret);
+ if (dbpath)
+ bdestroy(dbpath);
+ if (global_path_tmp)
+ bdestroy(global_path_tmp);
if (ret != 0) {
if (volume)
volume_free(volume);
/* ----------------------
*/
-static int volfile_changed(struct afp_options *p)
+static int volfile_changed(AFPObj *obj)
{
struct stat st;
+ struct afp_options *p = &obj->options;
+ int result;
+ const char *includefile;
+
+ result = stat(p->configfile, &st);
+ if (result != 0) {
+ LOG(log_debug, logtype_afpd, "where is the config file %s ?",
+ p->configfile);
+ /*
+ * We return 1 which means "config file changed". The caller
+ * will re-read config and fail too which is what we want.
+ */
+ return 1;
+ }
- if (!stat(p->configfile, &st) && st.st_mtime > p->volfile.mtime) {
+ if (st.st_mtime > p->volfile.mtime) {
p->volfile.mtime = st.st_mtime;
return 1;
}
+
+ includefile = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL,
+ "include", NULL);
+ if (includefile) {
+ result = stat(includefile, &st);
+ if (result != 0) {
+ LOG(log_debug, logtype_afpd, "where is the include file %s ?",
+ includefile);
+ return 1;
+ }
+
+ if (st.st_mtime > p->includefile.mtime) {
+ p->includefile.mtime = st.st_mtime;
+ return 1;
+ }
+ }
+
return 0;
}
|| strcmp(obj->username, obj->options.guest) == 0)
/* not an AFP session, but cnid daemon, dbd or ad util, or guest login */
continue;
- if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir))
- /* no user home */
+ if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir)) {
+ LOG(log_debug, logtype_afpd, "readvolfile: pwent->pw_dir: NULL or \"\" - no user home");
continue;
+ }
+ LOG(log_debug, logtype_afpd, "readvolfile: pwent->pw_dir: '%s'", pwent->pw_dir);
- if ((realpath(pwent->pw_dir, tmp)) == NULL)
+ if ((realpath(pwent->pw_dir, tmp)) == NULL) {
+ LOG(log_debug, logtype_afpd, "readvolfile: Cannot get realpath '%s' (%s).", pwent->pw_dir, strerror(errno));
continue;
+ }
+ LOG(log_debug, logtype_afpd, "readvolfile: realpath pwent->pw_dir: '%s'", tmp);
/* check if user home matches our "basedir regex" */
if ((basedir = atalk_iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
/*!
* Initialize volumes and load ini configfile
*
- * Depending on the value of obj->uid either access checks are done (!=0) or skipped (=0)
- *
- * @param obj (r) handle
- * @param delvol_fn (r) callback called for deleted volumes
+ * @param obj (r) handle
+ * @param flags (r) flags controlling volume load behaviour
*/
-int load_volumes(AFPObj *obj)
+int load_volumes(AFPObj *obj, lv_flags_t flags)
{
EC_INIT;
struct stat st;
int retries = 0;
struct vol *vol;
+ char *includefile;
LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
EC_NULL( pwbuf = malloc(bufsize) );
}
- if (obj->uid) {
+ if (!(flags & lv_all) && obj->uid) {
ret = getpwuid_r(obj->uid, &pwent, pwbuf, bufsize, &pwresult);
if (pwresult == NULL) {
LOG(log_error, logtype_afpd, "load_volumes: getpwuid_r: %s", strerror(errno));
}
if (Volumes) {
- if (!volfile_changed(&obj->options))
+ if (!volfile_changed(obj))
goto EC_CLEANUP;
have_uservol = 0;
for (vol = Volumes; vol; vol = vol->v_next) {
LOG(log_debug, logtype_afpd, "load_volumes: no volumes yet");
EC_ZERO_LOG( lstat(obj->options.configfile, &st) );
obj->options.volfile.mtime = st.st_mtime;
+
+ includefile = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL,
+ "include", NULL);
+ if (includefile) {
+ EC_ZERO_LOG( stat(includefile, &st) );
+ obj->options.includefile.mtime = st.st_mtime;
+ }
}
/* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
struct vol *tmp;
const struct passwd *pw;
char volname[AFPVOL_U8MNAMELEN + 1];
- char abspath[MAXPATHLEN + 1];
+ char *realabspath = NULL;
char volpath[MAXPATHLEN + 1], *realvolpath = NULL;
char tmpbuf[MAXPATHLEN + 1];
const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
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;
- }
-
+ /* build absolute path */
+ EC_NULL( realabspath = realpath_safe(path) );
+ path = realabspath;
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;
+ size_t v_path_len = strlen(tmp->v_path);
+ if (strncmp(path, tmp->v_path, v_path_len) == 0) {
+ if (v_path_len < strlen(path) && path[v_path_len] != '/') {
+ LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") != volume(\"%s\")", path, tmp->v_path);
+ } else {
+ LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") == volume(\"%s\")", path, tmp->v_path);
+ vol = tmp;
+ goto EC_CLEANUP;
+ }
+ } else {
+ LOG(log_debug, logtype_afpd, "getvolbypath: path(\"%s\") != volume(\"%s\")", path, tmp->v_path);
}
}
strlcat(tmpbuf, "/", MAXPATHLEN);
/* (5) */
- p = path + strlen(basedir);
+ p = path + match[0].rm_eo - match[0].rm_so;
while (*p == '/')
p++;
EC_NULL_LOG( user = strdup(p) );
subpath = prw;
strlcat(tmpbuf, user, MAXPATHLEN);
- if (getpwnam(user) == NULL) {
+ if ((pw = getpwnam(user)) == NULL) {
/* (5b) */
char *tuser;
if ((tuser = getuserbypath(tmpbuf)) != NULL) {
free(user);
user = strdup(tuser);
}
+ if ((pw = getpwnam(user)) == NULL)
+ EC_FAIL_LOG("unknown user: %s", user);
}
strlcpy(obj->username, user, MAXUSERLEN);
strlcat(tmpbuf, "/", MAXPATHLEN);
free(user);
if (realvolpath)
free(realvolpath);
+ if (realabspath)
+ free(realabspath);
if (ret != 0)
vol = NULL;
return vol;
options->configfile = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf");
options->sigconffile = strdup(_PATH_STATEDIR "afp_signature.conf");
options->uuidconf = strdup(_PATH_STATEDIR "afp_voluuid.conf");
-#ifdef HAVE_TRACKER_SPARQL
- options->slmod_path = strdup(_PATH_AFPDUAMPATH "slmod_sparql.so");
-#endif
options->flags = OPTION_UUID | AFPObj->cmdlineflags;
if ((config = atalk_iniparser_load(AFPObj->options.configfile)) == NULL)
options->passwdbits |= PASSWD_NOSAVE;
if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
options->passwdbits |= PASSWD_SET;
+ if (atalk_iniparser_getboolean(config, INISEC_GLOBAL, "spotlight expr", 1))
+ options->flags |= OPTION_SPOTLIGHT_EXPR;
/* figure out options w values */
options->loginmesg = atalk_iniparser_getstrdup(config, INISEC_GLOBAL, "login message", NULL);
options->sleep = atalk_iniparser_getint (config, INISEC_GLOBAL, "sleep time", 10);
options->disconnected = atalk_iniparser_getint (config, INISEC_GLOBAL, "disconnect time",24);
options->splice_size = atalk_iniparser_getint (config, INISEC_GLOBAL, "splice size", 64*1024);
+ options->sparql_limit = atalk_iniparser_getint (config, INISEC_GLOBAL, "sparql results limit", 0);
p = atalk_iniparser_getstring(config, INISEC_GLOBAL, "map acls", "rights");
if (STRCMP(p, ==, "rights"))
CONFIG_ARG_FREE(obj->options.fqdn);
if (obj->options.ignored_attr)
CONFIG_ARG_FREE(obj->options.ignored_attr);
- if (obj->options.slmod_path)
- CONFIG_ARG_FREE(obj->options.slmod_path);
-
if (obj->options.unixcodepage)
CONFIG_ARG_FREE(obj->options.unixcodepage);
if (obj->options.maccodepage)