2 Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
17 #endif /* HAVE_CONFIG_H */
27 #include <sys/param.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
34 #include <atalk/afp.h>
35 #include <atalk/util.h>
36 #include <atalk/logger.h>
38 #include <atalk/globals.h>
39 #include <atalk/errchk.h>
40 #include <atalk/iniparser.h>
41 #include <atalk/unix.h>
42 #include <atalk/cnid.h>
43 #include <atalk/dsi.h>
44 #include <atalk/uuid.h>
46 #define VOLOPT_ALLOW 0 /* user allow list */
47 #define VOLOPT_DENY 1 /* user deny list */
48 #define VOLOPT_RWLIST 2 /* user rw list */
49 #define VOLOPT_ROLIST 3 /* user ro list */
50 #define VOLOPT_PASSWORD 4 /* volume password */
51 #define VOLOPT_CASEFOLD 5 /* character case mangling */
52 #define VOLOPT_FLAGS 6 /* various flags */
53 #define VOLOPT_DBPATH 7 /* path to database */
54 #define VOLOPT_LIMITSIZE 8 /* Limit the size of the volume */
56 #define VOLOPT_VETO 10 /* list of veto filespec */
57 #define VOLOPT_PREEXEC 11 /* preexec command */
58 #define VOLOPT_ROOTPREEXEC 12 /* root preexec command */
59 #define VOLOPT_POSTEXEC 13 /* postexec command */
60 #define VOLOPT_ROOTPOSTEXEC 14 /* root postexec command */
61 #define VOLOPT_ENCODING 15 /* mac encoding (pre OSX)*/
62 #define VOLOPT_MACCHARSET 16
63 #define VOLOPT_CNIDSCHEME 17
64 #define VOLOPT_ADOUBLE 18 /* adouble version */
65 /* Usable slot: 19/20 */
66 #define VOLOPT_UMASK 21
67 #define VOLOPT_ALLOWED_HOSTS 22
68 #define VOLOPT_DENIED_HOSTS 23
69 #define VOLOPT_DPERM 24 /* dperm default directories perms */
70 #define VOLOPT_FPERM 25 /* fperm default files perms */
71 #define VOLOPT_DFLTPERM 26 /* perm */
72 #define VOLOPT_EA_VFS 27 /* Extended Attributes vfs indirection */
73 #define VOLOPT_CNIDSERVER 28 /* CNID Server ip address*/
74 #define VOLOPT_CNIDPORT 30 /* CNID server tcp port */
76 #define VOLOPT_MAX 31 /* <== IMPORTANT !!!!!! */
77 #define VOLOPT_NUM (VOLOPT_MAX + 1)
81 #define IS_VAR(a, b) (strncmp((a), (b), 2) == 0)
88 /**************************************************************
90 **************************************************************/
92 static struct vol *Volumes = NULL;
93 static uint16_t lastvid = 0;
96 * Get a volumes UUID from the config file.
97 * If there is none, it is generated and stored there.
99 * Returns pointer to allocated storage on success, NULL on error.
101 static char *get_vol_uuid(const AFPObj *obj, const char *volname)
104 char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
109 if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) { /* read open? */
110 /* scan in the conf file */
111 while (fgets(buf, sizeof(buf), fp) != NULL) {
113 while (p && isblank(*p))
115 if (!p || (*p == '#') || (*p == '\n'))
116 continue; /* invalid line */
119 if ((volname_conf = strtok( p, "\"" )) == NULL)
120 continue; /* syntax error */
122 if ((volname_conf = strtok( p, " \t" )) == NULL)
123 continue; /* syntax error: invalid name */
128 continue; /* syntax error */
130 if (strcmp(volname, volname_conf) != 0)
131 continue; /* another volume name */
133 while (p && isblank(*p))
136 if (sscanf(p, "%36s", uuid) == 1 ) {
137 for (int i=0; uuid[i]; i++)
138 uuid[i] = toupper(uuid[i]);
139 LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
149 /* not found or no file, reopen in append mode */
151 if (stat(obj->options.uuidconf, &tmpstat)) { /* no file */
152 if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
153 LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s).",
154 obj->options.uuidconf, strerror(errno));
157 if (( fp = fdopen( fd, "w" )) == NULL ) {
158 LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).",
159 obj->options.uuidconf, strerror(errno));
163 } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
164 LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
165 obj->options.uuidconf, strerror(errno));
168 fseek(fp, 0L, SEEK_END);
169 if(ftell(fp) == 0) { /* size = 0 */
170 fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
171 fprintf(fp, "# This file is auto-generated by afpd\n");
172 fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
174 fseek(fp, -1L, SEEK_END);
175 if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
178 /* generate uuid and write to file */
181 randombytes((void *)id, 16);
182 cp = uuid_bin2string(id);
184 LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
186 fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
193 Check if the underlying filesystem supports EAs.
194 If not, switch to ea:ad.
195 As we can't check (requires write access) on ro-volumes, we switch ea:auto
196 volumes that are options:ro to ea:none.
198 static int do_check_ea_support(const struct vol *vol)
201 char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
202 const char *eacontent = "yes";
204 if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
205 LOG(log_note, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
213 if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
214 sys_removexattr(vol->v_path, eaname);
217 LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes",
227 static void check_ea_support(struct vol *vol)
230 char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
231 const char *eacontent = "yes";
233 haseas = do_check_ea_support(vol);
235 if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
236 if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
237 LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
238 vol->v_vfs_ea = AFPVOL_EA_NONE;
243 vol->v_vfs_ea = AFPVOL_EA_SYS;
245 LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
247 vol->v_vfs_ea = AFPVOL_EA_AD;
251 if (vol->v_adouble == AD_VERSION_EA) {
253 vol->v_adouble = AD_VERSION2;
258 * Check whether a volume supports ACLs
260 * @param vol (r) volume
262 * @returns 0 if not, 1 if yes
264 static int check_vol_acl_support(const struct vol *vol)
268 #ifdef HAVE_SOLARIS_ACLS
271 if (get_nfsv4_acl(vol->v_path, &aces) == -1)
274 #ifdef HAVE_POSIX_ACLS
277 if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
281 #ifdef HAVE_SOLARIS_ACLS
282 if (aces) free(aces);
284 #ifdef HAVE_POSIX_ACLS
285 if (acl) acl_free(acl);
286 #endif /* HAVE_POSIX_ACLS */
288 LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
289 vol->v_path, ret ? "yes" : "no");
293 static void volfree(struct vol_option *options, const struct vol_option *save)
298 for (i = 0; i < VOLOPT_MAX; i++) {
299 if (options[i].c_value && (options[i].c_value != save[i].c_value))
300 free(options[i].c_value);
303 for (i = 0; i < VOLOPT_MAX; i++) {
304 if (options[i].c_value)
305 free(options[i].c_value);
311 * Handle variable substitutions. here's what we understand:
312 * $b -> basename of path
313 * $c -> client ip/appletalk address
314 * $d -> volume pathname on server
315 * $f -> full name (whatever's in the gecos field)
318 * $i -> client ip/appletalk address without port
319 * $s -> server name (hostname if it doesn't exist)
320 * $u -> username (guest is usually nobody)
321 * $v -> volume name or basename if null
324 * This get's called from readvolfile with
325 * path = NULL, volname = NULL for xlating the volumes path
326 * path = path, volname = NULL for xlating the volumes name
327 * ... and from volumes options parsing code when xlating eg dbpath with
328 * path = path, volname = volname
330 * Using this information we can reject xlation of any variable depeninding on a login
331 * context which is not given in the afp master, where we must evaluate this whole stuff
332 * too for the Zeroconf announcements.
334 static char *volxlate(const AFPObj *obj,
338 const struct passwd *pwd,
346 int xlatevolname = 0;
348 if (path && !volname)
356 dest = calloc(destlen +1, 1);
362 strlcpy(dest, src, destlen +1);
363 if ((p = strchr(src, '$')) == NULL) /* nothing to do */
366 /* first part of the path. just forward to the next variable. */
367 len = MIN((size_t)(p - src), destlen);
373 while (p && destlen > 0) {
374 /* now figure out what the variable is */
376 if (IS_VAR(p, "$b")) {
377 if (!obj->uid && xlatevolname)
380 if ((q = strrchr(path, '/')) == NULL)
382 else if (*(q + 1) != '\0')
385 } else if (IS_VAR(p, "$c")) {
386 if (!obj->uid && xlatevolname)
389 len = sprintf(dest, "%s:%u",
390 getip_string((struct sockaddr *)&dsi->client),
391 getip_port((struct sockaddr *)&dsi->client));
394 } else if (IS_VAR(p, "$d")) {
395 if (!obj->uid && xlatevolname)
398 } else if (pwd && IS_VAR(p, "$f")) {
399 if (!obj->uid && xlatevolname)
401 if ((r = strchr(pwd->pw_gecos, ',')))
404 } else if (pwd && IS_VAR(p, "$g")) {
405 if (!obj->uid && xlatevolname)
407 struct group *grp = getgrgid(pwd->pw_gid);
410 } else if (IS_VAR(p, "$h")) {
411 q = obj->options.hostname;
412 } else if (IS_VAR(p, "$i")) {
413 if (!obj->uid && xlatevolname)
416 q = getip_string((struct sockaddr *)&dsi->client);
417 } else if (IS_VAR(p, "$s")) {
418 q = obj->options.hostname;
419 } else if (obj->username && IS_VAR(p, "$u")) {
420 if (!obj->uid && xlatevolname)
423 if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
427 } else if (IS_VAR(p, "$v")) {
428 if (!obj->uid && xlatevolname)
434 if ((q = strrchr(path, '/')) == NULL)
436 else if (*(q + 1) != '\0')
439 } else if (IS_VAR(p, "$$")) {
444 /* copy the stuff over. if we don't understand something that we
445 * should, just skip it over. */
447 len = MIN(p == q ? 2 : strlen(q), destlen);
448 strncpy(dest, q, len);
453 /* stuff up to next $ */
455 p = strchr(src, '$');
456 len = p ? MIN((size_t)(p - src), destlen) : destlen;
458 strncpy(dest, src, len);
466 /* -------------------- */
467 static void setoption(struct vol_option *options, const struct vol_option *save, int opt, const char *val)
469 if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value))
470 free(options[opt].c_value);
471 options[opt].c_value = strdup(val);
474 /* Parse iniconfig and initalize volume options */
475 static void volset(const dictionary *conf, const char *vol, struct vol_option *options, const struct vol_option *save)
480 if (val = iniparser_getstring(conf, vol, "allow", NULL))
481 setoption(options, save, VOLOPT_ALLOW, val);
483 if (val = iniparser_getstring(conf, vol, "deny", NULL))
484 setoption(options, save, VOLOPT_DENY, val);
486 if (val = iniparser_getstring(conf, vol, "rwlist", NULL))
487 setoption(options, save, VOLOPT_RWLIST, val);
489 if (val = iniparser_getstring(conf, vol, "rolist", NULL))
490 setoption(options, save, VOLOPT_ROLIST, val);
492 if (val = iniparser_getstring(conf, vol, "volcharset", NULL))
493 setoption(options, save, VOLOPT_ENCODING, val);
495 if (val = iniparser_getstring(conf, vol, "maccharset", NULL))
496 setoption(options, save, VOLOPT_MACCHARSET, val);
498 if (val = iniparser_getstring(conf, vol, "veto", NULL))
499 setoption(options, save, VOLOPT_VETO, val);
501 if (val = iniparser_getstring(conf, vol, "cnidscheme", NULL))
502 setoption(options, save, VOLOPT_CNIDSCHEME, val);
504 if (val = iniparser_getstring(conf, vol, "dbpath", NULL))
505 setoption(options, save, VOLOPT_DBPATH, val);
507 if (val = iniparser_getstring(conf, vol, "password", NULL))
508 setoption(options, save, VOLOPT_PASSWORD, val);
510 if (val = iniparser_getstring(conf, vol, "root_preexec", NULL))
511 setoption(options, save, VOLOPT_ROOTPREEXEC, val);
513 if (val = iniparser_getstring(conf, vol, "preexec", NULL))
514 setoption(options, save, VOLOPT_PREEXEC, val);
516 if (val = iniparser_getstring(conf, vol, "root_postexec", NULL))
517 setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
519 if (val = iniparser_getstring(conf, vol, "postexec", NULL))
520 setoption(options, save, VOLOPT_POSTEXEC, val);
522 if (val = iniparser_getstring(conf, vol, "allowed_hosts", NULL))
523 setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
525 if (val = iniparser_getstring(conf, vol, "denied_hosts", NULL))
526 setoption(options, save, VOLOPT_DENIED_HOSTS, val);
528 if (val = iniparser_getstring(conf, vol, "umask", NULL))
529 options[VOLOPT_UMASK].i_value = (int)strtol(val, NULL, 8);
531 if (val = iniparser_getstring(conf, vol, "dperm", NULL))
532 options[VOLOPT_DPERM].i_value = (int)strtol(val, NULL, 8);
534 if (val = iniparser_getstring(conf, vol, "fperm", NULL))
535 options[VOLOPT_FPERM].i_value = (int)strtol(val, NULL, 8);
537 if (val = iniparser_getstring(conf, vol, "perm", NULL))
538 options[VOLOPT_DFLTPERM].i_value = (int)strtol(val, NULL, 8);
540 if (val = iniparser_getstring(conf, vol, "volsizelimit", NULL))
541 options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val, NULL, 10);
543 if (val = iniparser_getstring(conf, vol, "casefold", NULL)) {
544 if (strcasecmp(val, "tolower") == 0)
545 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
546 else if (strcasecmp(val, "toupper") == 0)
547 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
548 else if (strcasecmp(val, "xlatelower") == 0)
549 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
550 else if (strcasecmp(val, "xlateupper") == 0)
551 options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
554 if (val = iniparser_getstring(conf, vol, "adouble", NULL)) {
555 if (strcasecmp(val, "v2") == 0)
556 options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
557 else if (strcasecmp(val, "ea") == 0)
558 options[VOLOPT_ADOUBLE].i_value = AD_VERSION_EA;
561 if (val = iniparser_getstring(conf, vol, "ea", NULL)) {
562 if (strcasecmp(val, "ad") == 0)
563 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
564 else if (strcasecmp(val, "sys") == 0)
565 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS;
566 else if (strcasecmp(val, "none") == 0)
567 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
570 if (p = iniparser_getstrdup(conf, vol, "cnidserver", NULL)) {
571 if (q = strrchr(val, ':')) {
573 setoption(options, save, VOLOPT_CNIDPORT, q + 1);
575 setoption(options, save, VOLOPT_CNIDSERVER, p);
576 LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
577 vol, p, q ? q + 1 : "4700");
581 if (q = iniparser_getstrdup(conf, vol, "options", NULL)) {
582 if (p = strtok(q, ", ")) {
584 if (strcasecmp(p, "ro") == 0)
585 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
586 else if (strcasecmp(p, "nohex") == 0)
587 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
588 else if (strcasecmp(p, "nousedots") == 0)
589 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_USEDOTS;
590 else if (strcasecmp(p, "invisibledots") == 0)
591 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
592 else if (strcasecmp(p, "nostat") == 0)
593 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT;
594 else if (strcasecmp(p, "preexec_close") == 0)
595 options[VOLOPT_PREEXEC].i_value = 1;
596 else if (strcasecmp(p, "root_preexec_close") == 0)
597 options[VOLOPT_ROOTPREEXEC].i_value = 1;
598 else if (strcasecmp(p, "noupriv") == 0)
599 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_UNIX_PRIV;
600 else if (strcasecmp(p, "nodev") == 0)
601 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
602 else if (strcasecmp(p, "caseinsensitive") == 0)
603 options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN;
604 else if (strcasecmp(p, "illegalseq") == 0)
605 options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ;
606 else if (strcasecmp(p, "tm") == 0)
607 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
608 else if (strcasecmp(p, "searchdb") == 0)
609 options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
610 else if (strcasecmp(p, "nonetids") == 0)
611 options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
612 else if (strcasecmp(p, "noacls") == 0)
613 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
614 else if (strcasecmp(p, "nov2toeaconv") == 0)
615 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOV2TOEACONV;
616 p = strtok(NULL, ", ");
623 /* ------------------------------- */
624 static int creatvol(const AFPObj *obj, const struct passwd *pwd,
625 char *path, char *name,
626 struct vol_option *options)
629 int suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
631 char tmpname[AFPVOL_U8MNAMELEN+1];
632 ucs2_t u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
633 char suffix[6]; /* max is #FFFF */
636 LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
638 if ( name == NULL || *name == '\0' ) {
639 if ((name = strrchr( path, '/' )) == NULL) {
640 return -1; /* Obviously not a fully qualified path */
643 /* if you wish to share /, you need to specify a name. */
648 /* suffix for mangling use (lastvid + 1) */
649 /* because v_vid has not been decided yet. */
650 suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
652 vlen = strlen( name );
654 /* Unicode Volume Name */
655 /* Firstly convert name from unixcharset to UTF8-MAC */
657 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
659 strcpy(tmpname, "???");
663 /* Do we have to mangle ? */
664 if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
665 if (tmpvlen + suffixlen > obj->options.volnamelen) {
667 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
668 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
670 strcat(tmpname, suffix);
671 tmpvlen = strlen(tmpname);
674 /* Secondly convert name from UTF8-MAC to UCS2 */
675 if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
678 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
680 /* Maccharset Volume Name */
681 /* Firsty convert name from unixcharset to maccharset */
683 tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
685 strcpy(tmpname, "???");
689 /* Do we have to mangle ? */
690 if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
691 if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
693 tmpvlen = convert_charset(obj->options.unixcharset,
694 obj->options.maccharset,
699 AFPVOL_MACNAMELEN - suffixlen,
701 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
703 strcat(tmpname, suffix);
704 tmpvlen = strlen(tmpname);
707 /* Secondly convert name from maccharset to UCS2 */
708 if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
713 AFPVOL_U8MNAMELEN*2)) )
716 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
718 /* check duplicate */
719 for ( volume = Volumes; volume; volume = volume->v_next ) {
720 if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
722 (!utf8_encoding() && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) {
723 LOG (log_error, logtype_afpd,
724 "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"",
725 volume->v_localname, name);
726 if (volume->v_deleted) {
727 volume->v_new = hide = 1;
730 return -1; /* Won't be able to access it, anyway... */
735 if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
736 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
739 if ( NULL == ( volume->v_localname = strdup(name))) {
740 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
745 if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
746 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
751 if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
752 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
757 if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
758 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
764 volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname;
765 volume->v_hide = hide;
766 strcpy( volume->v_path, path );
770 #endif /* __svr4__ */
771 /* os X start at 1 and use network order ie. 1 2 3 */
772 volume->v_vid = ++lastvid;
773 volume->v_vid = htons(volume->v_vid);
775 if (!check_vol_acl_support(volume)) {
776 LOG(log_debug, logtype_afpd, "creatvol(\"%s\"): disabling ACL support", volume->v_path);
777 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
783 volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
784 volume->v_flags |= options[VOLOPT_FLAGS].i_value;
786 if (options[VOLOPT_EA_VFS].i_value)
787 volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
789 volume->v_ad_options = 0;
790 if ((volume->v_flags & AFPVOL_NODEV))
791 volume->v_ad_options |= ADVOL_NODEV;
792 if ((volume->v_flags & AFPVOL_UNIX_PRIV))
793 volume->v_ad_options |= ADVOL_UNIXPRIV;
794 if ((volume->v_flags & AFPVOL_INV_DOTS))
795 volume->v_ad_options |= ADVOL_INVDOTS;
797 if (options[VOLOPT_PASSWORD].c_value)
798 volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
800 if (options[VOLOPT_VETO].c_value)
801 volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
803 if (options[VOLOPT_ENCODING].c_value)
804 volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
806 if (options[VOLOPT_MACCHARSET].c_value)
807 volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
809 if (options[VOLOPT_DBPATH].c_value)
810 volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);
812 if (options[VOLOPT_CNIDSCHEME].c_value)
813 volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
815 if (options[VOLOPT_CNIDSERVER].c_value)
816 volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
818 if (options[VOLOPT_CNIDPORT].c_value)
819 volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
821 if (options[VOLOPT_UMASK].i_value)
822 volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
824 if (options[VOLOPT_DPERM].i_value)
825 volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value;
827 if (options[VOLOPT_FPERM].i_value)
828 volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value;
830 if (options[VOLOPT_DFLTPERM].i_value)
831 volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value;
833 if (options[VOLOPT_ADOUBLE].i_value)
834 volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
836 volume->v_adouble = AD_VERSION;
838 if (options[VOLOPT_LIMITSIZE].i_value)
839 volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value;
841 /* Mac to Unix conversion flags*/
842 volume->v_mtou_flags = 0;
843 if (!(volume->v_flags & AFPVOL_NOHEX))
844 volume->v_mtou_flags |= CONV_ESCAPEHEX;
845 if (!(volume->v_flags & AFPVOL_USEDOTS))
846 volume->v_mtou_flags |= CONV_ESCAPEDOTS;
847 if ((volume->v_flags & AFPVOL_EILSEQ))
848 volume->v_mtou_flags |= CONV__EILSEQ;
850 if ((volume->v_casefold & AFPVOL_MTOUUPPER))
851 volume->v_mtou_flags |= CONV_TOUPPER;
852 else if ((volume->v_casefold & AFPVOL_MTOULOWER))
853 volume->v_mtou_flags |= CONV_TOLOWER;
855 /* Unix to Mac conversion flags*/
856 volume->v_utom_flags = CONV_IGNORE | CONV_UNESCAPEHEX;
857 if ((volume->v_casefold & AFPVOL_UTOMUPPER))
858 volume->v_utom_flags |= CONV_TOUPPER;
859 else if ((volume->v_casefold & AFPVOL_UTOMLOWER))
860 volume->v_utom_flags |= CONV_TOLOWER;
862 if ((volume->v_flags & AFPVOL_EILSEQ))
863 volume->v_utom_flags |= CONV__EILSEQ;
865 if (options[VOLOPT_PREEXEC].c_value)
866 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path, name);
867 volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value;
869 if (options[VOLOPT_POSTEXEC].c_value)
870 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name);
872 if (options[VOLOPT_ROOTPREEXEC].c_value)
873 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path, name);
874 volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value;
876 if (options[VOLOPT_ROOTPOSTEXEC].c_value)
877 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path, name);
879 volume->v_dperm |= volume->v_perm;
880 volume->v_fperm |= volume->v_perm;
882 /* Check EA support on volume */
883 if (volume->v_vfs_ea == AFPVOL_EA_AUTO || volume->v_adouble == AD_VERSION_EA)
884 check_ea_support(volume);
887 /* get/store uuid from file in afpd master*/
888 if ((parent_or_child == 0) && (volume->v_flags & AFPVOL_TM)) {
889 char *uuid = get_vol_uuid(obj, volume->v_localname);
891 LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
892 volume->v_localname);
894 volume->v_uuid = uuid;
895 LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
896 volume->v_localname, volume->v_uuid);
900 volume->v_next = Volumes;
905 /* check access list. this function wants something of the following
907 * @group,name,name2,@group2,name3
909 * a NULL argument allows everybody to have access.
910 * we return three things:
912 * 0: list exists, but name isn't in it
916 #ifndef NO_REAL_USER_NAME
917 /* authentication is case insensitive
918 * FIXME should we do the same with group name?
920 #define access_strcmp strcasecmp
923 #define access_strcmp strcmp
927 static int accessvol(const AFPObj *obj, const char *args, const char *name)
929 char buf[MAXPATHLEN + 1], *p;
935 strlcpy(buf, args, sizeof(buf));
936 if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
940 if (*p == '@') { /* it's a group */
941 if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid, obj->ngroups, obj->groups))
943 } else if (access_strcmp(p, name) == 0) /* it's a user name */
945 p = strtok(NULL, ",");
951 static int hostaccessvol(const AFPObj *obj, int type, const char *volname, const char *args)
954 char buf[MAXPATHLEN + 1], *p, *b;
955 struct sockaddr_storage client;
956 const DSI *dsi = obj->dsi;
961 strlcpy(buf, args, sizeof(buf));
962 if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */
967 char *ipaddr, *mask_char;
968 struct addrinfo hints, *ai;
970 ipaddr = strtok(p, "/");
971 mask_char = strtok(NULL,"/");
973 /* Get address from string with getaddrinfo */
974 memset(&hints, 0, sizeof hints);
975 hints.ai_family = AF_UNSPEC;
976 hints.ai_socktype = SOCK_STREAM;
977 if ((ret = getaddrinfo(ipaddr, NULL, &hints, &ai)) != 0) {
978 LOG(log_error, logtype_afpd, "hostaccessvol: getaddrinfo: %s\n", gai_strerror(ret));
983 if (mask_char != NULL)
984 mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */
986 if (ai->ai_family == AF_INET) /* IPv4 */
992 /* Apply mask to addresses */
993 client = dsi->client;
994 apply_ip_mask((struct sockaddr *)&client, mask_int);
995 apply_ip_mask(ai->ai_addr, mask_int);
997 if (compare_ip((struct sockaddr *)&client, ai->ai_addr) == 0) {
998 if (type == VOLOPT_DENIED_HOSTS)
999 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s' by denied list",
1000 getip_string((struct sockaddr *)&client), volname);
1007 p = strtok_r(NULL, ",", &b);
1010 if (type == VOLOPT_ALLOWED_HOSTS)
1011 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s', not in allowed list",
1012 getip_string((struct sockaddr *)&dsi->client), volname);
1016 /* ----------------------
1018 static int volfile_changed(struct afp_options *p)
1022 if (!stat(p->configfile, &st) && st.st_mtime > p->volfile.mtime) {
1023 p->volfile.mtime = st.st_mtime;
1029 static int vol_section(const char *sec)
1031 if (STRCMP(sec, ==, INISEC_GLOBAL))
1033 if (STRCMP(sec, ==, INISEC_AFP))
1035 if (STRCMP(sec, ==, INISEC_CNID))
1040 /* ----------------------
1041 * Read volumes from iniconfig and add the volumes contained within to
1042 * the global volume list. This gets called from the forked afpd childs.
1043 * The master now reads this too for Zeroconf announcements.
1045 static int readvolfile(const AFPObj *obj, const struct passwd *pwent)
1047 char path[MAXPATHLEN + 1];
1048 char volname[AFPVOL_U8MNAMELEN + 1];
1049 char tmp[MAXPATHLEN + 1];
1055 struct vol_option save_options[VOLOPT_NUM];
1056 struct vol_option default_options[VOLOPT_NUM];
1057 struct vol_option options[VOLOPT_NUM];
1059 LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
1061 memset(default_options, 0, sizeof(default_options));
1063 /* Enable some default options for all volumes */
1064 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_UNIX_PRIV;
1066 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
1068 default_options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AUTO;
1069 default_options[VOLOPT_UMASK].i_value = obj->options.umask;
1070 memcpy(save_options, default_options, sizeof(options));
1072 int secnum = iniparser_getnsec(obj->iniconfig);
1073 LOG(log_debug, logtype_afpd, "readvolfile: sections: %d", secnum);
1074 const char *secname;
1076 for (i = 0; i < secnum; i++) {
1077 secname = iniparser_getsecname(obj->iniconfig, i);
1078 if (!vol_section(secname))
1081 strlcpy(volname, secname, AFPVOL_U8MNAMELEN);
1082 LOG(log_debug, logtype_afpd, "readvolfile: volume: %s", volname);
1084 if ((p = iniparser_getstrdup(obj->iniconfig, secname, "path", NULL)) == NULL)
1086 strlcpy(path, p, MAXPATHLEN);
1089 if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
1092 memcpy(options, default_options, sizeof(options));
1093 volset(obj->iniconfig, secname, options, default_options);
1095 /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
1096 allow -> either no list (-1), or in list (1)
1097 deny -> either no list (-1), or not in list (0) */
1100 if (accessvol(obj, options[VOLOPT_DENY].c_value, pwent->pw_name) == 1)
1102 if (accessvol(obj, options[VOLOPT_ALLOW].c_value, pwent->pw_name) == 0)
1104 if (hostaccessvol(obj, VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value) == 1)
1106 if (hostaccessvol(obj, VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value) == 0)
1110 /* handle read-only behaviour. semantics:
1111 * 1) neither the rolist nor the rwlist exist -> rw
1112 * 2) rolist exists -> ro if user is in it.
1113 * 3) rwlist exists -> ro unless user is in it. */
1114 if (pwent && !(options[VOLOPT_FLAGS].i_value & AFPVOL_RO)) {
1115 if (accessvol(obj, options[VOLOPT_ROLIST].c_value, pwent->pw_name) == 1
1116 || accessvol(obj, options[VOLOPT_RWLIST].c_value, pwent->pw_name) == 0)
1117 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
1120 /* do variable substitution for volname */
1121 if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL) {
1122 volfree(options, default_options);
1126 creatvol(obj, pwent, path, tmp, options);
1127 volfree(options, default_options);
1129 volfree(save_options, NULL);
1133 /* ------------------------------- */
1134 static void free_volumes(void)
1137 struct vol *nvol, *ovol;
1139 for ( vol = Volumes; vol; vol = vol->v_next ) {
1140 if (( vol->v_flags & AFPVOL_OPEN ) ) {
1147 for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1150 if (vol->v_localname == NULL) {
1151 if (Volumes == vol) {
1156 ovol->v_next = nvol;
1166 /**************************************************************
1168 **************************************************************/
1171 * Remove a volume from the linked list of volumes
1173 void volume_unlink(struct vol *volume)
1175 struct vol *vol, *ovol, *nvol;
1177 if (volume == Volumes) {
1178 Volumes = Volumes->v_next;
1181 for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) {
1184 if (vol == volume) {
1185 ovol->v_next = nvol;
1195 * Free all resources allocated in a struct vol
1197 void volume_free(struct vol *vol)
1199 free(vol->v_localname);
1200 vol->v_localname = NULL;
1201 free(vol->v_u8mname);
1202 vol->v_u8mname = NULL;
1203 free(vol->v_macname);
1204 vol->v_macname = NULL;
1206 free(vol->v_password);
1208 free(vol->v_volcodepage);
1209 free(vol->v_maccodepage);
1210 free(vol->v_cnidscheme);
1211 free(vol->v_dbpath);
1218 * Initialize volumes and load ini configfile
1220 * Depending on the value of obj->uid either access checks are done (!=0) or skipped (=0)
1222 * @param obj (r) handle
1223 * @param delvol_fn (r) callback called for deleted volumes
1225 int load_volumes(AFPObj *obj, void (*delvol_fn)(const struct vol *))
1229 struct passwd *pwent = NULL;
1234 LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
1237 if (!volfile_changed(&obj->options))
1242 /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
1244 fd = open(obj->options.configfile, O_RDONLY);
1246 while (retries < 2) {
1247 if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
1250 LOG(log_error, logtype_afpd, "readvolfile: can't lock configfile \"%s\"",
1251 obj->options.configfile);
1261 pwent = getpwuid(obj->uid);
1264 iniparser_freedict(obj->iniconfig);
1265 LOG(log_debug, logtype_afpd, "load_volumes: reloading: %s", obj->options.configfile);
1266 obj->iniconfig = iniparser_load(obj->options.configfile);
1268 EC_ZERO_LOG( readvolfile(obj, pwent) );
1270 for ( vol = Volumes; vol; vol = vol->v_next ) {
1271 if (vol->v_deleted && !vol->v_new ) {
1283 void unload_volumes(void)
1285 LOG(log_debug, logtype_afpd, "unload_volumes");
1289 const struct vol *getvolumes(void)
1294 /* ------------------------- */
1295 struct vol *getvolbyvid(const uint16_t vid )
1299 for ( vol = Volumes; vol; vol = vol->v_next ) {
1300 if ( vid == vol->v_vid ) {
1304 if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1313 * Initialize an AFPObj and options from ini config file
1315 int afp_config_parse(AFPObj *AFPObj)
1319 struct afp_options *options = &AFPObj->options;
1321 const char *p, *tmp;
1325 options->configfile = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf");
1326 options->sigconffile = strdup(_PATH_CONFDIR "afp_signature.conf");
1327 options->uuidconf = strdup(_PATH_CONFDIR "afp_voluuid.conf");
1328 options->flags = OPTION_ACL2MACCESS | OPTION_UUID | OPTION_SERVERNOTIF | AFPObj->cmdlineflags;
1330 if ((config = iniparser_load(AFPObj->options.configfile)) == NULL)
1332 AFPObj->iniconfig = config;
1335 options->logconfig = iniparser_getstrdup(config, INISEC_GLOBAL, "loglevel", "default:note");
1336 options->logfile = iniparser_getstrdup(config, INISEC_GLOBAL, "logfile", NULL);
1338 /* [AFP] "options" options wo values */
1339 if (p = iniparser_getstrdup(config, INISEC_AFP, "options", NULL)) {
1340 if (p = strtok(q, ", ")) {
1342 if (strcasecmp(p, "nozeroconf"))
1343 options->flags |= OPTION_NOZEROCONF;
1344 if (strcasecmp(p, "icon"))
1345 options->flags |= OPTION_CUSTOMICON;
1346 if (strcasecmp(p, "noicon"))
1347 options->flags &= ~OPTION_CUSTOMICON;
1348 if (strcasecmp(p, "advertise_ssh"))
1349 options->flags |= OPTION_ANNOUNCESSH;
1350 if (strcasecmp(p, "noacl2maccess"))
1351 options->flags &= ~OPTION_ACL2MACCESS;
1352 if (strcasecmp(p, "keepsessions"))
1353 options->flags |= OPTION_KEEPSESSIONS;
1354 if (strcasecmp(p, "closevol"))
1355 options->flags |= OPTION_CLOSEVOL;
1356 if (strcasecmp(p, "client_polling"))
1357 options->flags &= ~OPTION_SERVERNOTIF;
1358 if (strcasecmp(p, "nosavepassword"))
1359 options->passwdbits |= PASSWD_NOSAVE;
1360 if (strcasecmp(p, "savepassword"))
1361 options->passwdbits &= ~PASSWD_NOSAVE;
1362 if (strcasecmp(p, "nosetpassword"))
1363 options->passwdbits &= ~PASSWD_SET;
1364 if (strcasecmp(p, "setpassword"))
1365 options->passwdbits |= PASSWD_SET;
1366 p = strtok(NULL, ", ");
1370 /* figure out options w values */
1372 options->loginmesg = iniparser_getstrdup(config, INISEC_AFP, "loginmesg", "");
1373 options->guest = iniparser_getstrdup(config, INISEC_AFP, "guestname", "nobody");
1374 options->passwdfile = iniparser_getstrdup(config, INISEC_AFP, "passwdfile", _PATH_AFPDPWFILE);
1375 options->uampath = iniparser_getstrdup(config, INISEC_AFP, "uampath", _PATH_AFPDUAMPATH);
1376 options->uamlist = iniparser_getstrdup(config, INISEC_AFP, "uamlist", "uams_dhx.so,uams_dhx2.so");
1377 options->port = iniparser_getstrdup(config, INISEC_AFP, "port", "548");
1378 options->signatureopt = iniparser_getstrdup(config, INISEC_AFP, "signature", "auto");
1379 options->k5service = iniparser_getstrdup(config, INISEC_AFP, "k5service", NULL);
1380 options->k5realm = iniparser_getstrdup(config, INISEC_AFP, "k5realm", NULL);
1381 options->listen = iniparser_getstrdup(config, INISEC_AFP, "listen", NULL);
1382 options->ntdomain = iniparser_getstrdup(config, INISEC_AFP, "ntdomain", NULL);
1383 options->ntseparator = iniparser_getstrdup(config, INISEC_AFP, "ntseparator", NULL);
1384 options->mimicmodel = iniparser_getstrdup(config, INISEC_AFP, "mimicmodel", NULL);
1385 options->adminauthuser = iniparser_getstrdup(config, INISEC_AFP, "adminauthuser", NULL);
1386 options->connections = iniparser_getint (config, INISEC_AFP, "maxcon", 200);
1387 options->passwdminlen = iniparser_getint (config, INISEC_AFP, "passwdminlen", 0);
1388 options->tickleval = iniparser_getint (config, INISEC_AFP, "tickleval", 30);
1389 options->timeout = iniparser_getint (config, INISEC_AFP, "timeout", 4);
1390 options->dsireadbuf = iniparser_getint (config, INISEC_AFP, "dsireadbuf", 12);
1391 options->server_quantum = iniparser_getint (config, INISEC_AFP, "server_quantum", DSI_SERVQUANT_DEF);
1392 options->volnamelen = iniparser_getint (config, INISEC_AFP, "volnamelen", 80);
1393 options->dircachesize = iniparser_getint (config, INISEC_AFP, "dircachesize", DEFAULT_MAX_DIRCACHE_SIZE);
1394 options->tcp_sndbuf = iniparser_getint (config, INISEC_AFP, "tcpsndbuf", 0);
1395 options->tcp_rcvbuf = iniparser_getint (config, INISEC_AFP, "tcprcvbuf", 0);
1396 options->fce_fmodwait = iniparser_getint (config, INISEC_AFP, "fceholdfmod", 60);
1397 options->sleep = iniparser_getint (config, INISEC_AFP, "sleep", 10) * 60 * 2;
1398 options->disconnected = iniparser_getint (config, INISEC_AFP, "disconnect", 24) * 60 * 2;
1400 if ((p = iniparser_getstring(config, INISEC_AFP, "hostname", NULL))) {
1401 EC_NULL_LOG( options->hostname = strdup(p) );
1403 if (gethostname(val, sizeof(val)) < 0 ) {
1404 perror( "gethostname" );
1407 if ((q = strchr(val, '.')))
1409 options->hostname = strdup(val);
1412 if ((p = iniparser_getstring(config, INISEC_AFP, "k5keytab", NULL))) {
1413 EC_NULL_LOG( options->k5keytab = malloc(strlen(p) + 14) );
1414 snprintf(options->k5keytab, strlen(p) + 14, "KRB5_KTNAME=%s", p);
1415 putenv(options->k5keytab);
1419 if ((p = iniparser_getstring(config, INISEC_AFP, "admingroup", NULL))) {
1420 struct group *gr = getgrnam(p);
1422 options->admingid = gr->gr_gid;
1424 #endif /* ADMIN_GRP */
1426 q = iniparser_getstrdup(config, INISEC_AFP, "cnidserver", "localhost:4700");
1427 r = strrchr(q, ':');
1430 options->Cnid_srv = strdup(q);
1432 options->Cnid_port = strdup(r + 1);
1434 options->Cnid_port = strdup("4700");
1435 LOG(log_debug, logtype_afpd, "CNID Server: %s:%s", options->Cnid_srv, options->Cnid_port);
1439 if ((q = iniparser_getstrdup(config, INISEC_AFP, "fqdn", NULL))) {
1440 /* do a little checking for the domain name. */
1444 if (gethostbyname(q)) {
1447 EC_NULL_LOG( options->fqdn = strdup(q) );
1449 LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", c);
1454 if (!(p = iniparser_getstring(config, INISEC_AFP, "unixcodepage", NULL))) {
1455 options->unixcharset = CH_UNIX;
1456 options->unixcodepage = strdup("LOCALE");
1458 if ((options->unixcharset = add_charset(p)) == (charset_t)-1) {
1459 options->unixcharset = CH_UNIX;
1460 options->unixcodepage = strdup("LOCALE");
1461 LOG(log_warning, logtype_afpd, "Setting Unix codepage to '%s' failed", p);
1463 options->unixcodepage = strdup(p);
1467 if (!(p = iniparser_getstring(config, INISEC_AFP, "maccodepage", NULL))) {
1468 options->maccharset = CH_MAC;
1469 options->maccodepage = strdup("MAC_ROMAN");
1471 if ((options->maccharset = add_charset(p)) == (charset_t)-1) {
1472 options->maccharset = CH_MAC;
1473 options->maccodepage = strdup("MAC_ROMAN");
1474 LOG(log_warning, logtype_afpd, "Setting Unix codepage to '%s' failed", p);
1476 options->maccodepage = strdup(p);
1480 /* Check for sane values */
1481 if (options->tickleval <= 0)
1482 options->tickleval = 30;
1483 if (options->timeout <= 0)
1484 options->timeout = 4;
1485 if (options->sleep <= 4)
1486 options->disconnected = options->sleep = 4;
1487 if (options->dsireadbuf < 6)
1488 options->dsireadbuf = 6;
1489 if (options->volnamelen < 8)
1490 options->volnamelen = 8; /* max mangled volname "???#FFFF" */
1491 if (options->volnamelen > 255)
1492 options->volnamelen = 255; /* AFP3 spec */