-#define VOLOPT_DEFAULT ":DEFAULT:"
-#define VOLOPT_DEFAULT_LEN 9
-
-struct vol_option {
- char *c_value;
- int i_value;
-};
-
-typedef struct _special_folder {
- const char *name;
- int precreate;
- mode_t mode;
- int hide;
-} _special_folder;
-
-static const _special_folder special_folders[] = {
- {"Network Trash Folder", 1, 0777, 1},
- {".AppleDesktop", 1, 0777, 0},
- {NULL, 0, 0, 0}};
-
-/* Forward declarations */
-static void handle_special_folders (const struct vol *);
-static void deletevol(struct vol *vol);
-static void volume_free(struct vol *vol);
-static void check_ea_sys_support(struct vol *vol);
-static char *get_vol_uuid(const AFPObj *obj, const char *volname);
-static int readvolfile(AFPObj *obj, struct afp_volume_name *p1,char *p2, int user, struct passwd *pwent);
-
-static void volfree(struct vol_option *options, const struct vol_option *save)
-{
- int i;
-
- if (save) {
- for (i = 0; i < VOLOPT_MAX; i++) {
- if (options[i].c_value && (options[i].c_value != save[i].c_value))
- free(options[i].c_value);
- }
- } else {
- for (i = 0; i < VOLOPT_MAX; i++) {
- if (options[i].c_value)
- free(options[i].c_value);
- }
- }
-}
-
-
-#define is_var(a, b) (strncmp((a), (b), 2) == 0)
-
-/*
- * Handle variable substitutions. here's what we understand:
- * $b -> basename of path
- * $c -> client ip/appletalk address
- * $d -> volume pathname on server
- * $f -> full name (whatever's in the gecos field)
- * $g -> group
- * $h -> hostname
- * $i -> client ip/appletalk address without port
- * $s -> server name (hostname if it doesn't exist)
- * $u -> username (guest is usually nobody)
- * $v -> volume name or basename if null
- * $z -> zone (may not exist)
- * $$ -> $
- *
- * This get's called from readvolfile with
- * path = NULL, volname = NULL for xlating the volumes path
- * path = path, volname = NULL for xlating the volumes name
- * ... and from volumes options parsing code when xlating eg dbpath with
- * path = path, volname = volname
- *
- * Using this information we can reject xlation of any variable depeninding on a login
- * context which is not given in the afp master, where we must evaluate this whole stuff
- * too for the Zeroconf announcements.
- */
-static char *volxlate(AFPObj *obj,
- char *dest,
- size_t destlen,
- char *src,
- struct passwd *pwd,
- char *path,
- char *volname)
-{
- char *p, *r;
- const char *q;
- int len;
- char *ret;
- int afpmaster = 0;
- int xlatevolname = 0;
-
- if (parent_or_child == 0)
- afpmaster = 1;
-
- if (path && !volname)
- /* cf above */
- xlatevolname = 1;
-
- if (!src) {
- return NULL;
- }
- if (!dest) {
- dest = calloc(destlen +1, 1);
- }
- ret = dest;
- if (!ret) {
- return NULL;
- }
- strlcpy(dest, src, destlen +1);
- if ((p = strchr(src, '$')) == NULL) /* nothing to do */
- return ret;
-
- /* first part of the path. just forward to the next variable. */
- len = MIN((size_t)(p - src), destlen);
- if (len > 0) {
- destlen -= len;
- dest += len;
- }
-
- while (p && destlen > 0) {
- /* now figure out what the variable is */
- q = NULL;
- if (is_var(p, "$b")) {
- if (afpmaster && xlatevolname)
- return NULL;
- if (path) {
- if ((q = strrchr(path, '/')) == NULL)
- q = path;
- else if (*(q + 1) != '\0')
- q++;
- }
- } else if (is_var(p, "$c")) {
- if (afpmaster && xlatevolname)
- return NULL;
- DSI *dsi = obj->handle;
- len = sprintf(dest, "%s:%u",
- getip_string((struct sockaddr *)&dsi->client),
- getip_port((struct sockaddr *)&dsi->client));
- dest += len;
- destlen -= len;
- } else if (is_var(p, "$d")) {
- if (afpmaster && xlatevolname)
- return NULL;
- q = path;
- } else if (pwd && is_var(p, "$f")) {
- if (afpmaster && xlatevolname)
- return NULL;
- if ((r = strchr(pwd->pw_gecos, ',')))
- *r = '\0';
- q = pwd->pw_gecos;
- } else if (pwd && is_var(p, "$g")) {
- if (afpmaster && xlatevolname)
- return NULL;
- struct group *grp = getgrgid(pwd->pw_gid);
- if (grp)
- q = grp->gr_name;
- } else if (is_var(p, "$h")) {
- q = obj->options.hostname;
- } else if (is_var(p, "$i")) {
- if (afpmaster && xlatevolname)
- return NULL;
- DSI *dsi = obj->handle;
- q = getip_string((struct sockaddr *)&dsi->client);
- } else if (is_var(p, "$s")) {
- if (obj->Obj)
- q = obj->Obj;
- else if (obj->options.server) {
- q = obj->options.server;
- } else
- q = obj->options.hostname;
- } else if (obj->username && is_var(p, "$u")) {
- if (afpmaster && xlatevolname)
- return NULL;
- char* sep = NULL;
- if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
- q = sep+1;
- else
- q = obj->username;
- } else if (is_var(p, "$v")) {
- if (afpmaster && xlatevolname)
- return NULL;
- if (volname) {
- q = volname;
- }
- else if (path) {
- if ((q = strrchr(path, '/')) == NULL)
- q = path;
- else if (*(q + 1) != '\0')
- q++;
- }
- } else if (is_var(p, "$z")) {
- q = obj->Zone;
- } else if (is_var(p, "$$")) {
- q = "$";
- } else
- q = p;
-
- /* copy the stuff over. if we don't understand something that we
- * should, just skip it over. */
- if (q) {
- len = MIN(p == q ? 2 : strlen(q), destlen);
- strncpy(dest, q, len);
- dest += len;
- destlen -= len;
- }
-
- /* stuff up to next $ */
- src = p + 2;
- p = strchr(src, '$');
- len = p ? MIN((size_t)(p - src), destlen) : destlen;
- if (len > 0) {
- strncpy(dest, src, len);
- dest += len;
- destlen -= len;
- }
- }
- return ret;
-}
-
-/* to make sure that val is valid, make sure to select an opt that
- includes val */
-static int optionok(const char *buf, const char *opt, const char *val)
-{
- if (!strstr(buf,opt))
- return 0;
- if (!val[1])
- return 0;
- return 1;
-}
-
-
-/* -------------------- */
-static void setoption(struct vol_option *options, struct vol_option *save, int opt, const char *val)
-{
- if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value))
- free(options[opt].c_value);
- options[opt].c_value = strdup(val + 1);
-}
-
-/* ------------------------------------------
- handle all the options. tmp can't be NULL. */
-static void volset(struct vol_option *options, struct vol_option *save,
- char *volname, int vlen,
- const char *tmp)
-{
- char *val;
-
- val = strchr(tmp, ':');
- if (!val) {
- /* we'll assume it's a volume name. */
- strncpy(volname, tmp, vlen);
- volname[vlen] = 0;
- return;
- }
-#if 0
- LOG(log_debug, logtype_afpd, "Parsing volset %s", val);
-#endif
- if (optionok(tmp, "allow:", val)) {
- setoption(options, save, VOLOPT_ALLOW, val);
-
- } else if (optionok(tmp, "deny:", val)) {
- setoption(options, save, VOLOPT_DENY, val);
-
- } else if (optionok(tmp, "rwlist:", val)) {
- setoption(options, save, VOLOPT_RWLIST, val);
-
- } else if (optionok(tmp, "rolist:", val)) {
- setoption(options, save, VOLOPT_ROLIST, val);
-
- } else if (optionok(tmp, "codepage:", val)) {
- LOG (log_error, logtype_afpd, "The old codepage system has been removed. Please make sure to read the documentation !!!!");
- /* Make sure we don't screw anything */
- exit (EXITERR_CONF);
- } else if (optionok(tmp, "volcharset:", val)) {
- setoption(options, save, VOLOPT_ENCODING, val);
- } else if (optionok(tmp, "maccharset:", val)) {
- setoption(options, save, VOLOPT_MACCHARSET, val);
- } else if (optionok(tmp, "veto:", val)) {
- setoption(options, save, VOLOPT_VETO, val);
- } else if (optionok(tmp, "cnidscheme:", val)) {
- setoption(options, save, VOLOPT_CNIDSCHEME, val);
- } else if (optionok(tmp, "casefold:", val)) {
- if (strcasecmp(val + 1, "tolower") == 0)
- options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
- else if (strcasecmp(val + 1, "toupper") == 0)
- options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
- else if (strcasecmp(val + 1, "xlatelower") == 0)
- options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
- else if (strcasecmp(val + 1, "xlateupper") == 0)
- options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
- } else if (optionok(tmp, "adouble:", val)) {
- if (strcasecmp(val + 1, "v2") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
- else if (strcasecmp(val + 1, "ea") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION_EA;
- } else if (optionok(tmp, "options:", val)) {
- char *p;
-
- if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
- return;
-
- while (p) {
- if (strcasecmp(p, "ro") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
- else if (strcasecmp(p, "nohex") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
- else if (strcasecmp(p, "usedots") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
- else if (strcasecmp(p, "invisibledots") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_INV_DOTS;
- else if (strcasecmp(p, "nostat") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT;
- else if (strcasecmp(p, "preexec_close") == 0)
- options[VOLOPT_PREEXEC].i_value = 1;
- else if (strcasecmp(p, "root_preexec_close") == 0)
- options[VOLOPT_ROOTPREEXEC].i_value = 1;
- else if (strcasecmp(p, "upriv") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
- else if (strcasecmp(p, "nodev") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
- else if (strcasecmp(p, "caseinsensitive") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN;
- else if (strcasecmp(p, "illegalseq") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ;
- else if (strcasecmp(p, "tm") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
- else if (strcasecmp(p, "searchdb") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
- else if (strcasecmp(p, "nonetids") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
- else if (strcasecmp(p, "noacls") == 0)
- options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
- else if (strcasecmp(p, "nov2toeaconv") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_NOV2TOEACONV;
- p = strtok(NULL, ",");
- }
-
- } else if (optionok(tmp, "cnidserver:", val)) {
- setoption(options, save, VOLOPT_CNIDSERVER, val);
-
- char *p = strrchr(val + 1, ':');
- if (p) {
- *p = 0;
- setoption(options, save, VOLOPT_CNIDPORT, p);
- }
-
- LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
- volname, val + 1, p ? p + 1 : Cnid_port);
-
- } else if (optionok(tmp, "dbpath:", val)) {
- setoption(options, save, VOLOPT_DBPATH, val);
-
- } else if (optionok(tmp, "umask:", val)) {
- options[VOLOPT_UMASK].i_value = (int)strtol(val +1, NULL, 8);
- } else if (optionok(tmp, "dperm:", val)) {
- options[VOLOPT_DPERM].i_value = (int)strtol(val+1, NULL, 8);
- } else if (optionok(tmp, "fperm:", val)) {
- options[VOLOPT_FPERM].i_value = (int)strtol(val+1, NULL, 8);
- } else if (optionok(tmp, "perm:", val)) {
- options[VOLOPT_DFLTPERM].i_value = (int)strtol(val+1, NULL, 8);
- } else if (optionok(tmp, "password:", val)) {
- setoption(options, save, VOLOPT_PASSWORD, val);
- } else if (optionok(tmp, "root_preexec:", val)) {
- setoption(options, save, VOLOPT_ROOTPREEXEC, val);
-
- } else if (optionok(tmp, "preexec:", val)) {
- setoption(options, save, VOLOPT_PREEXEC, val);
-
- } else if (optionok(tmp, "root_postexec:", val)) {
- setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
-
- } else if (optionok(tmp, "postexec:", val)) {
- setoption(options, save, VOLOPT_POSTEXEC, val);
-
- } else if (optionok(tmp, "allowed_hosts:", val)) {
- setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
-
- } else if (optionok(tmp, "denied_hosts:", val)) {
- setoption(options, save, VOLOPT_DENIED_HOSTS, val);
-
- } else if (optionok(tmp, "ea:", val)) {
- if (strcasecmp(val + 1, "ad") == 0)
- options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
- else if (strcasecmp(val + 1, "sys") == 0)
- options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS;
- else if (strcasecmp(val + 1, "none") == 0)
- options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
-
- } else if (optionok(tmp, "volsizelimit:", val)) {
- options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val + 1, NULL, 10);
-
- } else {
- /* ignore unknown options */
- LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
-
- }
-}
-
-/* ----------------- */
-static void showvol(const ucs2_t *name)
-{
- struct vol *volume;
- for ( volume = Volumes; volume; volume = volume->v_next ) {
- if (volume->v_hide && !strcasecmp_w( volume->v_name, name ) ) {
- volume->v_hide = 0;
- return;
- }
- }
-}
-
-/* ------------------------------- */
-static int creatvol(AFPObj *obj, struct passwd *pwd,
- char *path, char *name,
- struct vol_option *options,
- const int user /* user defined volume */
- )
-{
- struct vol *volume;
- int suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
- int hide = 0;
- char tmpname[AFPVOL_U8MNAMELEN+1];
- ucs2_t u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
- char suffix[6]; /* max is #FFFF */
- uint16_t flags;
-
- LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
-
- if ( name == NULL || *name == '\0' ) {
- if ((name = strrchr( path, '/' )) == NULL) {
- return -1; /* Obviously not a fully qualified path */
- }
-
- /* if you wish to share /, you need to specify a name. */
- if (*++name == '\0')
- return -1;
- }
-
- /* suffix for mangling use (lastvid + 1) */
- /* because v_vid has not been decided yet. */
- suffixlen = sprintf(suffix, "%c%X", MANGLE_CHAR, lastvid + 1 );
-
- vlen = strlen( name );
-
- /* Unicode Volume Name */
- /* Firstly convert name from unixcharset to UTF8-MAC */
- flags = CONV_IGNORE;
- tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
- if (tmpvlen <= 0) {
- strcpy(tmpname, "???");
- tmpvlen = 3;
- }
-
- /* Do we have to mangle ? */
- if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
- if (tmpvlen + suffixlen > obj->options.volnamelen) {
- flags = CONV_FORCE;
- tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
- tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
- }
- strcat(tmpname, suffix);
- tmpvlen = strlen(tmpname);
- }
-
- /* Secondly convert name from UTF8-MAC to UCS2 */
- if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
- return -1;
-
- LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
-
- /* Maccharset Volume Name */
- /* Firsty convert name from unixcharset to maccharset */
- flags = CONV_IGNORE;
- tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
- if (tmpvlen <= 0) {
- strcpy(tmpname, "???");
- tmpvlen = 3;
- }
-
- /* Do we have to mangle ? */
- if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
- if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
- flags = CONV_FORCE;
- tmpvlen = convert_charset(obj->options.unixcharset,
- obj->options.maccharset,
- 0,
- name,
- vlen,
- tmpname,
- AFPVOL_MACNAMELEN - suffixlen,
- &flags);
- tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
- }
- strcat(tmpname, suffix);
- tmpvlen = strlen(tmpname);
- }
-
- /* Secondly convert name from maccharset to UCS2 */
- if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
- CH_UCS2,
- tmpname,
- tmpvlen,
- mactmpname,
- AFPVOL_U8MNAMELEN*2)) )
- return -1;
-
- LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
-
- /* check duplicate */
- for ( volume = Volumes; volume; volume = volume->v_next ) {
- if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
- ||
- (!utf8_encoding() && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) {
- LOG (log_error, logtype_afpd,
- "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"",
- volume->v_localname, name);
- if (volume->v_deleted) {
- volume->v_new = hide = 1;
- }
- else {
- return -1; /* Won't be able to access it, anyway... */
- }
- }
- }
-
- if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
- LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
- return -1;
- }
- if ( NULL == ( volume->v_localname = strdup(name))) {
- LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
- free(volume);
- return -1;
- }
-
- if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
- LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
- volume_free(volume);
- free(volume);
- return -1;
- }
- if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
- LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
- volume_free(volume);
- free(volume);
- return -1;
- }
- if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
- LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
- volume_free(volume);
- free(volume);
- return -1;
- }
-
- volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname;
- volume->v_hide = hide;
- strcpy( volume->v_path, path );
-
-#ifdef __svr4__
- volume->v_qfd = -1;
-#endif /* __svr4__ */
- /* os X start at 1 and use network order ie. 1 2 3 */
- volume->v_vid = ++lastvid;
- volume->v_vid = htons(volume->v_vid);
-#ifdef HAVE_ACLS
- if (!check_vol_acl_support(volume)) {
- LOG(log_debug, logtype_afpd, "creatvol(\"%s\"): disabling ACL support", volume->v_path);
- options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
- }
-#endif
-
- /* handle options */
- if (options) {
- volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
- volume->v_flags |= options[VOLOPT_FLAGS].i_value;
-
- if (options[VOLOPT_EA_VFS].i_value)
- volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
-
- volume->v_ad_options = 0;
- if ((volume->v_flags & AFPVOL_NODEV))
- volume->v_ad_options |= ADVOL_NODEV;
- if ((volume->v_flags & AFPVOL_UNIX_PRIV))
- volume->v_ad_options |= ADVOL_UNIXPRIV;
- if ((volume->v_flags & AFPVOL_INV_DOTS))
- volume->v_ad_options |= ADVOL_INVDOTS;
-
- if (options[VOLOPT_PASSWORD].c_value)
- volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
-
- if (options[VOLOPT_VETO].c_value)
- volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
-
- if (options[VOLOPT_ENCODING].c_value)
- volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
-
- if (options[VOLOPT_MACCHARSET].c_value)
- volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
-
- if (options[VOLOPT_DBPATH].c_value)
- volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);