static struct vol *Volumes = NULL;
static uint16_t lastvid = 0;
+
+/*
+ * Normalize volume path
+ */
+static char *realpath_safe(const char *path)
+{
+ char *resolved_path;
+
+#ifdef REALPATH_TAKES_NULL
+ if ((resolved_path = realpath(path, NULL)) == NULL) {
+ LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+ return NULL;
+ }
+ return resolved_path;
+
+#else
+if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
+ return NULL;
+ if (realpath(path, resolved_path) == NULL) {
+ free(resolved_path);
+ LOG(log_error, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
+ return NULL;
+ }
+ /* Safe some memory */
+ char *tmp;
+ if ((tmp = strdup(resolved_path)) == NULL) {
+ free(resolved_path);
+ return NULL;
+ }
+ free(resolved_path);
+ resolved_path = tmp;
+ return resolved_path;
+#endif
+}
+
+
/*
* Get a volumes UUID from the config file.
* If there is none, it is generated and stored there.
As we can't check (requires write access) on ro-volumes, we switch ea:auto
volumes that are options:ro to ea:none.
*/
+#define EABUFSZ 4
static int do_check_ea_support(const struct vol *vol)
{
int haseas;
- char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
+ const char *eaname = "org.netatalk.has-Extended-Attributes";
const char *eacontent = "yes";
+ char buf[EABUFSZ];
- if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
- LOG(log_note, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
+ if (sys_lgetxattr(vol->v_path, eaname, buf, EABUFSZ) != -1)
return 1;
- }
- mktemp(eaname);
+ if (vol->v_flags & AFPVOL_RO) {
+ LOG(log_debug, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
+ return 1;
+ }
become_root();
- if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
- sys_removexattr(vol->v_path, eaname);
+ if ((sys_setxattr(vol->v_path, eaname, eacontent, strlen(eacontent) + 1, 0)) == 0) {
haseas = 1;
} else {
- LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes or read-only volume root",
+ LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes or read-only volume",
vol->v_localname);
haseas = 0;
}
static void check_ea_support(struct vol *vol)
{
int haseas;
- char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
- const char *eacontent = "yes";
haseas = do_check_ea_support(vol);
if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
- if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
- LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
- vol->v_vfs_ea = AFPVOL_EA_NONE;
- return;
- }
-
- if (haseas) {
+ if (haseas)
vol->v_vfs_ea = AFPVOL_EA_SYS;
- } else {
- LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
- vol->v_localname);
- vol->v_vfs_ea = AFPVOL_EA_AD;
- }
+ else
+ vol->v_vfs_ea = AFPVOL_EA_NONE;
}
if (vol->v_adouble == AD_VERSION_EA) {
{
EC_INIT;
struct vol *volume = NULL;
- int suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
- char tmpname[AFPVOL_U8MNAMELEN+1];
+ int i, suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
+ char *tmpname;
ucs2_t u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
char suffix[6]; /* max is #FFFF */
uint16_t flags;
else
EC_NULL( volume->v_maccodepage = strdup(obj->options.maccodepage) );
+ vlen = strlen(name);
+ tmpname = strdup(name);
+ for(i = 0; i < vlen; i++)
+ if(tmpname[i] == '/') tmpname[i] = ':';
+
bstring dbpath;
EC_NULL_LOG( val = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
- EC_NULL_LOG( dbpath = bformat("%s/%s/", val, name) );
+ EC_NULL_LOG( dbpath = bformat("%s/%s/", val, tmpname) );
EC_NULL_LOG( volume->v_dbpath = strdup(bdata(dbpath)) );
bdestroy(dbpath);
if (getoption_bool(obj->iniconfig, section, "acls", preset, 1))
volume->v_flags |= AFPVOL_ACLS;
#endif
- if (!getoption_bool(obj->iniconfig, section, "convert adouble", preset, 1))
+ if (!getoption_bool(obj->iniconfig, section, "convert appledouble", preset, 1))
volume->v_flags |= AFPVOL_NOV2TOEACONV;
if (getoption_bool(obj->iniconfig, section, "preexec close", preset, 0))
* 1) neither the rolist nor the rwlist exist -> rw
* 2) rolist exists -> ro if user is in it.
* 3) rwlist exists -> ro unless user is in it.
+ * 4) cnid scheme = last -> ro forcibly.
*/
if (pwd) {
if (accessvol(obj, getoption(obj->iniconfig, section, "rolist", preset, NULL), pwd->pw_name) == 1
|| accessvol(obj, getoption(obj->iniconfig, section, "rwlist", preset, NULL), pwd->pw_name) == 0)
volume->v_flags |= AFPVOL_RO;
}
+ if (0 == strcmp(volume->v_cnidscheme, "last"))
+ volume->v_flags |= AFPVOL_RO;
if ((volume->v_flags & AFPVOL_NODEV))
volume->v_ad_options |= ADVOL_NODEV;
/* because v_vid has not been decided yet. */
suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
-
- vlen = strlen( name );
-
/* Unicode Volume Name */
/* Firstly convert name from unixcharset to UTF8-MAC */
- flags = CONV_IGNORE;
+ flags = CONV_IGNORE | CONV_ALLOW_SLASH;
tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
if (tmpvlen <= 0) {
strcpy(tmpname, "???");
/* Do we have to mangle ? */
if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
if (tmpvlen + suffixlen > obj->options.volnamelen) {
- flags = CONV_FORCE;
+ flags = CONV_FORCE | CONV_ALLOW_SLASH;
tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
}
/* Maccharset Volume Name */
/* Firsty convert name from unixcharset to maccharset */
- flags = CONV_IGNORE;
+ flags = CONV_IGNORE | CONV_ALLOW_SLASH;
tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
if (tmpvlen <= 0) {
strcpy(tmpname, "???");
/* Do we have to mangle ? */
if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
- flags = CONV_FORCE;
+ flags = CONV_FORCE | CONV_ALLOW_SLASH;
tmpvlen = convert_charset(obj->options.unixcharset,
obj->options.maccharset,
0,
EC_INIT;
static int regexerr = -1;
static regex_t reg;
- char path[MAXPATHLEN + 1];
+ char *realpw_dir;
+ char *realvolpath;
char volname[AFPVOL_U8MNAMELEN + 1];
- char tmp[MAXPATHLEN + 1];
+ char tmp[MAXPATHLEN + 1], tmp2[MAXPATHLEN + 1];
const char *preset, *default_preset, *p, *basedir;
char *q, *u;
int i;
if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir))
/* no user home */
continue;
+ if ((realpw_dir = realpath_safe(pwent->pw_dir)) == NULL)
+ continue;
/* check if user home matches our "basedir regex" */
if ((basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
LOG(log_debug, logtype_default, "readvolfile: bad basedir regex: %s", errbuf);
}
- if (regexec(®, pwent->pw_dir, 1, match, 0) == REG_NOMATCH) {
+ if (regexec(®, realpw_dir, 1, match, 0) == REG_NOMATCH) {
LOG(log_debug, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
- pwent->pw_dir, basedir);
+ realpw_dir, basedir);
continue;
}
- strlcpy(tmp, pwent->pw_dir, MAXPATHLEN);
+ strlcpy(tmp, realpw_dir, MAXPATHLEN);
strlcat(tmp, "/", MAXPATHLEN);
if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
strlcat(tmp, p, MAXPATHLEN);
strlcpy(tmp, p, MAXPATHLEN);
}
- if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
+ if (volxlate(obj, tmp2, sizeof(tmp2) - 1, tmp, pwent, NULL, NULL) == NULL)
continue;
/* do variable substitution for volume name */
LOG(log_warning, logtype_afpd, "home name must contain $u.");
p = "$u's home";
}
+ if (strchr(p, ':') != NULL) {
+ LOG(log_warning, logtype_afpd, "home name must not contain \":\".");
+ p = "$u's home";
+ }
strlcpy(tmp, p, MAXPATHLEN);
} else {
strlcpy(tmp, secname, AFPVOL_U8MNAMELEN);
}
- if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, path, NULL) == NULL)
+ if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, tmp2, NULL) == NULL)
continue;
preset = iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
- creatvol(obj, pwent, secname, volname, path, preset ? preset : default_preset ? default_preset : NULL);
+ if ((realvolpath = realpath_safe(tmp2)) == NULL)
+ continue;
+
+ creatvol(obj, pwent, secname, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
}
EC_CLEANUP:
const struct passwd *pw;
char volname[AFPVOL_U8MNAMELEN + 1];
char abspath[MAXPATHLEN + 1];
- char volpath[MAXPATHLEN + 1];
+ char volpath[MAXPATHLEN + 1], *realvolpath;
char tmpbuf[MAXPATHLEN + 1];
const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
char *user = NULL, *prw;
subpath = prw;
strlcat(tmpbuf, user, MAXPATHLEN);
- strlcat(obj->username, user, MAXUSERLEN);
+ strlcpy(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);
}
if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
return NULL;
+ if ((realvolpath = realpath_safe(volpath)) == 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);
+ LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => realvolpath: \"%s\"",
+ path, user, pw->pw_dir, realvolpath);
/* do variable substitution for volume name */
p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
if (strstr(p, "$u") == NULL)
p = "$u's home";
strlcpy(tmpbuf, p, AFPVOL_U8MNAMELEN);
- EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, volpath, NULL) );
+ EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, realvolpath, 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);
+ vol = creatvol(obj, pw, INISEC_HOMES, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
EC_CLEANUP:
if (user)
/* "server options" boolean options */
if (!iniparser_getboolean(config, INISEC_GLOBAL, "zeroconf", 1))
options->flags |= OPTION_NOZEROCONF;
- if (iniparser_getboolean(config, INISEC_GLOBAL, "icon", 0))
- options->flags |= OPTION_CUSTOMICON;
if (iniparser_getboolean(config, INISEC_GLOBAL, "advertise ssh", 0))
options->flags |= OPTION_ANNOUNCESSH;
if (iniparser_getboolean(config, INISEC_GLOBAL, "map acls", 1))
options->flags |= OPTION_NOSENDFILE;
if (iniparser_getboolean(config, INISEC_GLOBAL, "solaris share reservations", 1))
options->flags |= OPTION_SHARE_RESERV;
+ if (iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
+ options->flags |= OPTION_AFP_READ_LOCK;
if (!iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
options->passwdbits |= PASSWD_NOSAVE;
if (iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
/* unix charset is in [G] only */
if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "unix charset", NULL))) {
options->unixcodepage = strdup("UTF8");
- charset_names[CH_UNIX] = strdup("UTF8");
+ set_charset_name(CH_UNIX, "UTF8");
} else {
if (strcasecmp(p, "LOCALE") == 0) {
#if defined(CODESET)
p = strdup("UTF8");
}
options->unixcodepage = strdup(p);
- charset_names[CH_UNIX] = strdup(p);
+ set_charset_name(CH_UNIX, p);
}
options->unixcharset = CH_UNIX;
LOG(log_debug, logtype_afpd, "Global unix charset is %s", options->unixcodepage);
/* mac charset is in [G] and [V] */
if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "mac charset", NULL))) {
options->maccodepage = strdup("MAC_ROMAN");
- charset_names[CH_MAC] = strdup("MAC_ROMAN");
+ set_charset_name(CH_MAC, "MAC_ROMAN");
} else {
if (strncasecmp(p, "MAC", 3) != 0) {
LOG(log_warning, logtype_afpd, "Is '%s' really mac charset? ", p);
}
options->maccodepage = strdup(p);
- charset_names[CH_MAC] = strdup(p);
+ set_charset_name(CH_MAC, p);
}
options->maccharset = CH_MAC;
LOG(log_debug, logtype_afpd, "Global mac charset is %s", options->maccodepage);