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>
45 #include <atalk/netatalk_conf.h>
47 #define VOLOPT_ALLOW 0 /* user allow list */
48 #define VOLOPT_DENY 1 /* user deny list */
49 #define VOLOPT_RWLIST 2 /* user rw list */
50 #define VOLOPT_ROLIST 3 /* user ro list */
51 #define VOLOPT_PASSWORD 4 /* volume password */
52 #define VOLOPT_CASEFOLD 5 /* character case mangling */
53 #define VOLOPT_FLAGS 6 /* various flags */
54 #define VOLOPT_DBPATH 7 /* path to database */
55 #define VOLOPT_LIMITSIZE 8 /* Limit the size of the volume */
57 #define VOLOPT_VETO 10 /* list of veto filespec */
58 #define VOLOPT_PREEXEC 11 /* preexec command */
59 #define VOLOPT_ROOTPREEXEC 12 /* root preexec command */
60 #define VOLOPT_POSTEXEC 13 /* postexec command */
61 #define VOLOPT_ROOTPOSTEXEC 14 /* root postexec command */
62 #define VOLOPT_ENCODING 15 /* mac encoding (pre OSX)*/
63 #define VOLOPT_MACCHARSET 16
64 #define VOLOPT_CNIDSCHEME 17
65 #define VOLOPT_ADOUBLE 18 /* adouble version */
66 /* Usable slot: 19/20 */
67 #define VOLOPT_UMASK 21
68 #define VOLOPT_ALLOWED_HOSTS 22
69 #define VOLOPT_DENIED_HOSTS 23
70 #define VOLOPT_DPERM 24 /* dperm default directories perms */
71 #define VOLOPT_FPERM 25 /* fperm default files perms */
72 #define VOLOPT_DFLTPERM 26 /* perm */
73 #define VOLOPT_EA_VFS 27 /* Extended Attributes vfs indirection */
74 #define VOLOPT_CNIDSERVER 28 /* CNID Server ip address*/
75 #define VOLOPT_CNIDPORT 30 /* CNID server tcp port */
77 #define VOLOPT_MAX 31 /* <== IMPORTANT !!!!!! */
78 #define VOLOPT_NUM (VOLOPT_MAX + 1)
82 #define IS_VAR(a, b) (strncmp((a), (b), 2) == 0)
89 /**************************************************************
91 **************************************************************/
93 static struct vol *Volumes = NULL;
94 static uint16_t lastvid = 0;
97 * Get a volumes UUID from the config file.
98 * If there is none, it is generated and stored there.
100 * Returns pointer to allocated storage on success, NULL on error.
102 static char *get_vol_uuid(const AFPObj *obj, const char *volname)
105 char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
110 if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) { /* read open? */
111 /* scan in the conf file */
112 while (fgets(buf, sizeof(buf), fp) != NULL) {
114 while (p && isblank(*p))
116 if (!p || (*p == '#') || (*p == '\n'))
117 continue; /* invalid line */
120 if ((volname_conf = strtok( p, "\"" )) == NULL)
121 continue; /* syntax error */
123 if ((volname_conf = strtok( p, " \t" )) == NULL)
124 continue; /* syntax error: invalid name */
129 continue; /* syntax error */
131 if (strcmp(volname, volname_conf) != 0)
132 continue; /* another volume name */
134 while (p && isblank(*p))
137 if (sscanf(p, "%36s", uuid) == 1 ) {
138 for (int i=0; uuid[i]; i++)
139 uuid[i] = toupper(uuid[i]);
140 LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
150 /* not found or no file, reopen in append mode */
152 if (stat(obj->options.uuidconf, &tmpstat)) { /* no file */
153 if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
154 LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s).",
155 obj->options.uuidconf, strerror(errno));
158 if (( fp = fdopen( fd, "w" )) == NULL ) {
159 LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).",
160 obj->options.uuidconf, strerror(errno));
164 } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
165 LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
166 obj->options.uuidconf, strerror(errno));
169 fseek(fp, 0L, SEEK_END);
170 if(ftell(fp) == 0) { /* size = 0 */
171 fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
172 fprintf(fp, "# This file is auto-generated by afpd\n");
173 fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
175 fseek(fp, -1L, SEEK_END);
176 if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
179 /* generate uuid and write to file */
182 randombytes((void *)id, 16);
183 cp = uuid_bin2string(id);
185 LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
187 fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
194 Check if the underlying filesystem supports EAs.
195 If not, switch to ea:ad.
196 As we can't check (requires write access) on ro-volumes, we switch ea:auto
197 volumes that are options:ro to ea:none.
199 static int do_check_ea_support(const struct vol *vol)
202 char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
203 const char *eacontent = "yes";
205 if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
206 LOG(log_note, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
214 if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
215 sys_removexattr(vol->v_path, eaname);
218 LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes",
228 static void check_ea_support(struct vol *vol)
231 char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
232 const char *eacontent = "yes";
234 haseas = do_check_ea_support(vol);
236 if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
237 if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
238 LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
239 vol->v_vfs_ea = AFPVOL_EA_NONE;
244 vol->v_vfs_ea = AFPVOL_EA_SYS;
246 LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
248 vol->v_vfs_ea = AFPVOL_EA_AD;
252 if (vol->v_adouble == AD_VERSION_EA) {
254 vol->v_adouble = AD_VERSION2;
259 * Check whether a volume supports ACLs
261 * @param vol (r) volume
263 * @returns 0 if not, 1 if yes
265 static int check_vol_acl_support(const struct vol *vol)
269 #ifdef HAVE_SOLARIS_ACLS
272 if (get_nfsv4_acl(vol->v_path, &aces) == -1)
275 #ifdef HAVE_POSIX_ACLS
278 if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
282 #ifdef HAVE_SOLARIS_ACLS
283 if (aces) free(aces);
285 #ifdef HAVE_POSIX_ACLS
286 if (acl) acl_free(acl);
287 #endif /* HAVE_POSIX_ACLS */
289 LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
290 vol->v_path, ret ? "yes" : "no");
294 static void volfree(struct vol_option *options, const struct vol_option *save)
299 for (i = 0; i < VOLOPT_MAX; i++) {
300 if (options[i].c_value && (options[i].c_value != save[i].c_value))
301 free(options[i].c_value);
304 for (i = 0; i < VOLOPT_MAX; i++) {
305 if (options[i].c_value)
306 free(options[i].c_value);
312 * Handle variable substitutions. here's what we understand:
313 * $b -> basename of path
314 * $c -> client ip/appletalk address
315 * $d -> volume pathname on server
316 * $f -> full name (whatever's in the gecos field)
319 * $i -> client ip/appletalk address without port
320 * $s -> server name (hostname if it doesn't exist)
321 * $u -> username (guest is usually nobody)
322 * $v -> volume name or basename if null
325 * This get's called from readvolfile with
326 * path = NULL, volname = NULL for xlating the volumes path
327 * path = path, volname = NULL for xlating the volumes name
328 * ... and from volumes options parsing code when xlating eg dbpath with
329 * path = path, volname = volname
331 * Using this information we can reject xlation of any variable depeninding on a login
332 * context which is not given in the afp master, where we must evaluate this whole stuff
333 * too for the Zeroconf announcements.
335 static char *volxlate(const AFPObj *obj,
339 const struct passwd *pwd,
347 int xlatevolname = 0;
349 if (path && !volname)
357 dest = calloc(destlen +1, 1);
363 strlcpy(dest, src, destlen +1);
364 if ((p = strchr(src, '$')) == NULL) /* nothing to do */
367 /* first part of the path. just forward to the next variable. */
368 len = MIN((size_t)(p - src), destlen);
374 while (p && destlen > 0) {
375 /* now figure out what the variable is */
377 if (IS_VAR(p, "$b")) {
378 if (!obj->uid && xlatevolname)
381 if ((q = strrchr(path, '/')) == NULL)
383 else if (*(q + 1) != '\0')
386 } else if (IS_VAR(p, "$c")) {
387 if (!obj->uid && xlatevolname)
390 len = sprintf(dest, "%s:%u",
391 getip_string((struct sockaddr *)&dsi->client),
392 getip_port((struct sockaddr *)&dsi->client));
395 } else if (IS_VAR(p, "$d")) {
396 if (!obj->uid && xlatevolname)
399 } else if (pwd && IS_VAR(p, "$f")) {
400 if (!obj->uid && xlatevolname)
402 if ((r = strchr(pwd->pw_gecos, ',')))
405 } else if (pwd && IS_VAR(p, "$g")) {
406 if (!obj->uid && xlatevolname)
408 struct group *grp = getgrgid(pwd->pw_gid);
411 } else if (IS_VAR(p, "$h")) {
412 q = obj->options.hostname;
413 } else if (IS_VAR(p, "$i")) {
414 if (!obj->uid && xlatevolname)
417 q = getip_string((struct sockaddr *)&dsi->client);
418 } else if (IS_VAR(p, "$s")) {
419 q = obj->options.hostname;
420 } else if (obj->username && IS_VAR(p, "$u")) {
421 if (!obj->uid && xlatevolname)
424 if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
428 } else if (IS_VAR(p, "$v")) {
429 if (!obj->uid && xlatevolname)
435 if ((q = strrchr(path, '/')) == NULL)
437 else if (*(q + 1) != '\0')
440 } else if (IS_VAR(p, "$$")) {
445 /* copy the stuff over. if we don't understand something that we
446 * should, just skip it over. */
448 len = MIN(p == q ? 2 : strlen(q), destlen);
449 strncpy(dest, q, len);
454 /* stuff up to next $ */
456 p = strchr(src, '$');
457 len = p ? MIN((size_t)(p - src), destlen) : destlen;
459 strncpy(dest, src, len);
467 /* -------------------- */
468 static void setoption(struct vol_option *options, const struct vol_option *save, int opt, const char *val)
470 if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value))
471 free(options[opt].c_value);
472 options[opt].c_value = strdup(val);
475 /* Parse iniconfig and initalize volume options */
476 static void volset(const dictionary *conf, const char *vol, struct vol_option *options, const struct vol_option *save)
481 if (val = iniparser_getstring(conf, vol, "allow", NULL))
482 setoption(options, save, VOLOPT_ALLOW, val);
484 if (val = iniparser_getstring(conf, vol, "deny", NULL))
485 setoption(options, save, VOLOPT_DENY, val);
487 if (val = iniparser_getstring(conf, vol, "rwlist", NULL))
488 setoption(options, save, VOLOPT_RWLIST, val);
490 if (val = iniparser_getstring(conf, vol, "rolist", NULL))
491 setoption(options, save, VOLOPT_ROLIST, val);
493 if (val = iniparser_getstring(conf, vol, "volcharset", NULL))
494 setoption(options, save, VOLOPT_ENCODING, val);
496 if (val = iniparser_getstring(conf, vol, "maccharset", NULL))
497 setoption(options, save, VOLOPT_MACCHARSET, val);
499 if (val = iniparser_getstring(conf, vol, "veto", NULL))
500 setoption(options, save, VOLOPT_VETO, val);
502 if (val = iniparser_getstring(conf, vol, "cnidscheme", NULL))
503 setoption(options, save, VOLOPT_CNIDSCHEME, val);
505 if (val = iniparser_getstring(conf, vol, "dbpath", NULL))
506 setoption(options, save, VOLOPT_DBPATH, val);
508 if (val = iniparser_getstring(conf, vol, "password", NULL))
509 setoption(options, save, VOLOPT_PASSWORD, val);
511 if (val = iniparser_getstring(conf, vol, "root_preexec", NULL))
512 setoption(options, save, VOLOPT_ROOTPREEXEC, val);
514 if (val = iniparser_getstring(conf, vol, "preexec", NULL))
515 setoption(options, save, VOLOPT_PREEXEC, val);
517 if (val = iniparser_getstring(conf, vol, "root_postexec", NULL))
518 setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
520 if (val = iniparser_getstring(conf, vol, "postexec", NULL))
521 setoption(options, save, VOLOPT_POSTEXEC, val);
523 if (val = iniparser_getstring(conf, vol, "allowed_hosts", NULL))
524 setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
526 if (val = iniparser_getstring(conf, vol, "denied_hosts", NULL))
527 setoption(options, save, VOLOPT_DENIED_HOSTS, val);
529 if (val = iniparser_getstring(conf, vol, "umask", NULL))
530 options[VOLOPT_UMASK].i_value = (int)strtol(val, NULL, 8);
532 if (val = iniparser_getstring(conf, vol, "dperm", NULL))
533 options[VOLOPT_DPERM].i_value = (int)strtol(val, NULL, 8);
535 if (val = iniparser_getstring(conf, vol, "fperm", NULL))
536 options[VOLOPT_FPERM].i_value = (int)strtol(val, NULL, 8);
538 if (val = iniparser_getstring(conf, vol, "perm", NULL))
539 options[VOLOPT_DFLTPERM].i_value = (int)strtol(val, NULL, 8);
541 if (val = iniparser_getstring(conf, vol, "volsizelimit", NULL))
542 options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val, NULL, 10);
544 if (val = iniparser_getstring(conf, vol, "casefold", NULL)) {
545 if (strcasecmp(val, "tolower") == 0)
546 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
547 else if (strcasecmp(val, "toupper") == 0)
548 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
549 else if (strcasecmp(val, "xlatelower") == 0)
550 options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
551 else if (strcasecmp(val, "xlateupper") == 0)
552 options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
555 if (val = iniparser_getstring(conf, vol, "adouble", NULL)) {
556 if (strcasecmp(val, "v2") == 0)
557 options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
558 else if (strcasecmp(val, "ea") == 0)
559 options[VOLOPT_ADOUBLE].i_value = AD_VERSION_EA;
562 if (val = iniparser_getstring(conf, vol, "ea", NULL)) {
563 if (strcasecmp(val, "ad") == 0)
564 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
565 else if (strcasecmp(val, "sys") == 0)
566 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS;
567 else if (strcasecmp(val, "none") == 0)
568 options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
571 if (p = iniparser_getstrdup(conf, vol, "cnidserver", NULL)) {
572 if (q = strrchr(val, ':')) {
574 setoption(options, save, VOLOPT_CNIDPORT, q + 1);
576 setoption(options, save, VOLOPT_CNIDSERVER, p);
577 LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
578 vol, p, q ? q + 1 : "4700");
582 if (q = iniparser_getstrdup(conf, vol, "options", NULL)) {
583 if (p = strtok(q, ", ")) {
585 if (strcasecmp(p, "ro") == 0)
586 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
587 else if (strcasecmp(p, "nohex") == 0)
588 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
589 else if (strcasecmp(p, "nousedots") == 0)
590 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_USEDOTS;
591 else if (strcasecmp(p, "invisibledots") == 0)
592 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
593 else if (strcasecmp(p, "nostat") == 0)
594 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT;
595 else if (strcasecmp(p, "preexec_close") == 0)
596 options[VOLOPT_PREEXEC].i_value = 1;
597 else if (strcasecmp(p, "root_preexec_close") == 0)
598 options[VOLOPT_ROOTPREEXEC].i_value = 1;
599 else if (strcasecmp(p, "noupriv") == 0)
600 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_UNIX_PRIV;
601 else if (strcasecmp(p, "nodev") == 0)
602 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
603 else if (strcasecmp(p, "caseinsensitive") == 0)
604 options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN;
605 else if (strcasecmp(p, "illegalseq") == 0)
606 options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ;
607 else if (strcasecmp(p, "tm") == 0)
608 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
609 else if (strcasecmp(p, "searchdb") == 0)
610 options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
611 else if (strcasecmp(p, "nonetids") == 0)
612 options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
613 else if (strcasecmp(p, "noacls") == 0)
614 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
615 else if (strcasecmp(p, "nov2toeaconv") == 0)
616 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOV2TOEACONV;
617 p = strtok(NULL, ", ");
624 /* ------------------------------- */
625 static int creatvol(const AFPObj *obj, const struct passwd *pwd,
626 char *path, char *name,
627 struct vol_option *options)
630 int suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
632 char tmpname[AFPVOL_U8MNAMELEN+1];
633 ucs2_t u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
634 char suffix[6]; /* max is #FFFF */
637 LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
639 if ( name == NULL || *name == '\0' ) {
640 if ((name = strrchr( path, '/' )) == NULL) {
641 return -1; /* Obviously not a fully qualified path */
644 /* if you wish to share /, you need to specify a name. */
649 /* suffix for mangling use (lastvid + 1) */
650 /* because v_vid has not been decided yet. */
651 suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
653 vlen = strlen( name );
655 /* Unicode Volume Name */
656 /* Firstly convert name from unixcharset to UTF8-MAC */
658 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
660 strcpy(tmpname, "???");
664 /* Do we have to mangle ? */
665 if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
666 if (tmpvlen + suffixlen > obj->options.volnamelen) {
668 tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
669 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
671 strcat(tmpname, suffix);
672 tmpvlen = strlen(tmpname);
675 /* Secondly convert name from UTF8-MAC to UCS2 */
676 if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
679 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
681 /* Maccharset Volume Name */
682 /* Firsty convert name from unixcharset to maccharset */
684 tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
686 strcpy(tmpname, "???");
690 /* Do we have to mangle ? */
691 if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
692 if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
694 tmpvlen = convert_charset(obj->options.unixcharset,
695 obj->options.maccharset,
700 AFPVOL_MACNAMELEN - suffixlen,
702 tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
704 strcat(tmpname, suffix);
705 tmpvlen = strlen(tmpname);
708 /* Secondly convert name from maccharset to UCS2 */
709 if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
714 AFPVOL_U8MNAMELEN*2)) )
717 LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
719 /* check duplicate */
720 for ( volume = Volumes; volume; volume = volume->v_next ) {
721 if ((utf8_encoding(obj) && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
723 (!utf8_encoding(obj) && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) {
724 LOG (log_error, logtype_afpd,
725 "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"",
726 volume->v_localname, name);
727 if (volume->v_deleted) {
728 volume->v_new = hide = 1;
731 return -1; /* Won't be able to access it, anyway... */
736 if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
737 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
740 if ( NULL == ( volume->v_localname = strdup(name))) {
741 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
746 if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
747 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
752 if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
753 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
758 if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
759 LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
765 volume->v_name = utf8_encoding(obj)?volume->v_u8mname:volume->v_macname;
766 volume->v_hide = hide;
767 strcpy( volume->v_path, path );
771 #endif /* __svr4__ */
772 /* os X start at 1 and use network order ie. 1 2 3 */
773 volume->v_vid = ++lastvid;
774 volume->v_vid = htons(volume->v_vid);
776 if (!check_vol_acl_support(volume)) {
777 LOG(log_debug, logtype_afpd, "creatvol(\"%s\"): disabling ACL support", volume->v_path);
778 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
784 volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
785 volume->v_flags |= options[VOLOPT_FLAGS].i_value;
787 if (options[VOLOPT_EA_VFS].i_value)
788 volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
790 volume->v_ad_options = 0;
791 if ((volume->v_flags & AFPVOL_NODEV))
792 volume->v_ad_options |= ADVOL_NODEV;
793 if ((volume->v_flags & AFPVOL_UNIX_PRIV))
794 volume->v_ad_options |= ADVOL_UNIXPRIV;
795 if ((volume->v_flags & AFPVOL_INV_DOTS))
796 volume->v_ad_options |= ADVOL_INVDOTS;
798 if (options[VOLOPT_PASSWORD].c_value)
799 volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
801 if (options[VOLOPT_VETO].c_value)
802 volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
804 if (options[VOLOPT_ENCODING].c_value)
805 volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
807 if (options[VOLOPT_MACCHARSET].c_value)
808 volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
810 if (options[VOLOPT_DBPATH].c_value)
811 volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);
813 if (options[VOLOPT_CNIDSCHEME].c_value)
814 volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
816 if (options[VOLOPT_CNIDSERVER].c_value)
817 volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
819 if (options[VOLOPT_CNIDPORT].c_value)
820 volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
822 if (options[VOLOPT_UMASK].i_value)
823 volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
825 if (options[VOLOPT_DPERM].i_value)
826 volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value;
828 if (options[VOLOPT_FPERM].i_value)
829 volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value;
831 if (options[VOLOPT_DFLTPERM].i_value)
832 volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value;
834 if (options[VOLOPT_ADOUBLE].i_value)
835 volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
837 volume->v_adouble = AD_VERSION;
839 if (options[VOLOPT_LIMITSIZE].i_value)
840 volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value;
842 /* Mac to Unix conversion flags*/
843 volume->v_mtou_flags = 0;
844 if (!(volume->v_flags & AFPVOL_NOHEX))
845 volume->v_mtou_flags |= CONV_ESCAPEHEX;
846 if (!(volume->v_flags & AFPVOL_USEDOTS))
847 volume->v_mtou_flags |= CONV_ESCAPEDOTS;
848 if ((volume->v_flags & AFPVOL_EILSEQ))
849 volume->v_mtou_flags |= CONV__EILSEQ;
851 if ((volume->v_casefold & AFPVOL_MTOUUPPER))
852 volume->v_mtou_flags |= CONV_TOUPPER;
853 else if ((volume->v_casefold & AFPVOL_MTOULOWER))
854 volume->v_mtou_flags |= CONV_TOLOWER;
856 /* Unix to Mac conversion flags*/
857 volume->v_utom_flags = CONV_IGNORE | CONV_UNESCAPEHEX;
858 if ((volume->v_casefold & AFPVOL_UTOMUPPER))
859 volume->v_utom_flags |= CONV_TOUPPER;
860 else if ((volume->v_casefold & AFPVOL_UTOMLOWER))
861 volume->v_utom_flags |= CONV_TOLOWER;
863 if ((volume->v_flags & AFPVOL_EILSEQ))
864 volume->v_utom_flags |= CONV__EILSEQ;
866 if (options[VOLOPT_PREEXEC].c_value)
867 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path, name);
868 volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value;
870 if (options[VOLOPT_POSTEXEC].c_value)
871 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name);
873 if (options[VOLOPT_ROOTPREEXEC].c_value)
874 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path, name);
875 volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value;
877 if (options[VOLOPT_ROOTPOSTEXEC].c_value)
878 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path, name);
880 volume->v_dperm |= volume->v_perm;
881 volume->v_fperm |= volume->v_perm;
883 /* Check EA support on volume */
884 if (volume->v_vfs_ea == AFPVOL_EA_AUTO || volume->v_adouble == AD_VERSION_EA)
885 check_ea_support(volume);
888 /* get/store uuid from file in afpd master*/
889 if ((parent_or_child == 0) && (volume->v_flags & AFPVOL_TM)) {
890 char *uuid = get_vol_uuid(obj, volume->v_localname);
892 LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
893 volume->v_localname);
895 volume->v_uuid = uuid;
896 LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
897 volume->v_localname, volume->v_uuid);
901 volume->v_next = Volumes;
907 /* check access list. this function wants something of the following
909 * @group,name,name2,@group2,name3
911 * a NULL argument allows everybody to have access.
912 * we return three things:
914 * 0: list exists, but name isn't in it
918 #ifndef NO_REAL_USER_NAME
919 /* authentication is case insensitive
920 * FIXME should we do the same with group name?
922 #define access_strcmp strcasecmp
925 #define access_strcmp strcmp
929 static int accessvol(const AFPObj *obj, const char *args, const char *name)
931 char buf[MAXPATHLEN + 1], *p;
937 strlcpy(buf, args, sizeof(buf));
938 if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
942 if (*p == '@') { /* it's a group */
943 if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid, obj->ngroups, obj->groups))
945 } else if (access_strcmp(p, name) == 0) /* it's a user name */
947 p = strtok(NULL, ",");
953 static int hostaccessvol(const AFPObj *obj, int type, const char *volname, const char *args)
956 char buf[MAXPATHLEN + 1], *p, *b;
957 struct sockaddr_storage client;
958 const DSI *dsi = obj->dsi;
963 strlcpy(buf, args, sizeof(buf));
964 if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */
969 char *ipaddr, *mask_char;
970 struct addrinfo hints, *ai;
972 ipaddr = strtok(p, "/");
973 mask_char = strtok(NULL,"/");
975 /* Get address from string with getaddrinfo */
976 memset(&hints, 0, sizeof hints);
977 hints.ai_family = AF_UNSPEC;
978 hints.ai_socktype = SOCK_STREAM;
979 if ((ret = getaddrinfo(ipaddr, NULL, &hints, &ai)) != 0) {
980 LOG(log_error, logtype_afpd, "hostaccessvol: getaddrinfo: %s\n", gai_strerror(ret));
985 if (mask_char != NULL)
986 mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */
988 if (ai->ai_family == AF_INET) /* IPv4 */
994 /* Apply mask to addresses */
995 client = dsi->client;
996 apply_ip_mask((struct sockaddr *)&client, mask_int);
997 apply_ip_mask(ai->ai_addr, mask_int);
999 if (compare_ip((struct sockaddr *)&client, ai->ai_addr) == 0) {
1000 if (type == VOLOPT_DENIED_HOSTS)
1001 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s' by denied list",
1002 getip_string((struct sockaddr *)&client), volname);
1009 p = strtok_r(NULL, ",", &b);
1012 if (type == VOLOPT_ALLOWED_HOSTS)
1013 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s', not in allowed list",
1014 getip_string((struct sockaddr *)&dsi->client), volname);
1018 /* ----------------------
1020 static int volfile_changed(struct afp_options *p)
1024 if (!stat(p->configfile, &st) && st.st_mtime > p->volfile.mtime) {
1025 p->volfile.mtime = st.st_mtime;
1031 static int vol_section(const char *sec)
1033 if (STRCMP(sec, ==, INISEC_GLOBAL))
1035 if (STRCMP(sec, ==, INISEC_AFP))
1037 if (STRCMP(sec, ==, INISEC_CNID))
1042 /* ----------------------
1043 * Read volumes from iniconfig and add the volumes contained within to
1044 * the global volume list. This gets called from the forked afpd childs.
1045 * The master now reads this too for Zeroconf announcements.
1047 static int readvolfile(const AFPObj *obj, const struct passwd *pwent)
1049 char path[MAXPATHLEN + 1];
1050 char volname[AFPVOL_U8MNAMELEN + 1];
1051 char tmp[MAXPATHLEN + 1];
1057 struct vol_option save_options[VOLOPT_NUM];
1058 struct vol_option default_options[VOLOPT_NUM];
1059 struct vol_option options[VOLOPT_NUM];
1061 LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
1063 memset(default_options, 0, sizeof(default_options));
1065 /* Enable some default options for all volumes */
1066 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_UNIX_PRIV;
1068 default_options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
1070 default_options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AUTO;
1071 default_options[VOLOPT_UMASK].i_value = obj->options.umask;
1072 memcpy(save_options, default_options, sizeof(options));
1074 int secnum = iniparser_getnsec(obj->iniconfig);
1075 LOG(log_debug, logtype_afpd, "readvolfile: sections: %d", secnum);
1076 const char *secname;
1078 for (i = 0; i < secnum; i++) {
1079 secname = iniparser_getsecname(obj->iniconfig, i);
1080 if (!vol_section(secname))
1083 strlcpy(volname, secname, AFPVOL_U8MNAMELEN);
1084 LOG(log_debug, logtype_afpd, "readvolfile: volume: %s", volname);
1086 if ((p = iniparser_getstrdup(obj->iniconfig, secname, "path", NULL)) == NULL)
1088 strlcpy(path, p, MAXPATHLEN);
1091 if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
1094 memcpy(options, default_options, sizeof(options));
1095 volset(obj->iniconfig, secname, options, default_options);
1097 /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
1098 allow -> either no list (-1), or in list (1)
1099 deny -> either no list (-1), or not in list (0) */
1102 if (accessvol(obj, options[VOLOPT_DENY].c_value, pwent->pw_name) == 1)
1104 if (accessvol(obj, options[VOLOPT_ALLOW].c_value, pwent->pw_name) == 0)
1106 if (hostaccessvol(obj, VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value) == 1)
1108 if (hostaccessvol(obj, VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value) == 0)
1112 /* handle read-only behaviour. semantics:
1113 * 1) neither the rolist nor the rwlist exist -> rw
1114 * 2) rolist exists -> ro if user is in it.
1115 * 3) rwlist exists -> ro unless user is in it. */
1116 if (pwent && !(options[VOLOPT_FLAGS].i_value & AFPVOL_RO)) {
1117 if (accessvol(obj, options[VOLOPT_ROLIST].c_value, pwent->pw_name) == 1
1118 || accessvol(obj, options[VOLOPT_RWLIST].c_value, pwent->pw_name) == 0)
1119 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
1122 /* do variable substitution for volname */
1123 if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL) {
1124 volfree(options, default_options);
1128 creatvol(obj, pwent, path, tmp, options);
1129 volfree(options, default_options);
1131 volfree(save_options, NULL);
1135 /* ------------------------------- */
1136 static void free_volumes(void)
1139 struct vol *nvol, *ovol;
1141 for ( vol = Volumes; vol; vol = vol->v_next ) {
1142 if (( vol->v_flags & AFPVOL_OPEN ) ) {
1149 for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1152 if (vol->v_localname == NULL) {
1153 if (Volumes == vol) {
1158 ovol->v_next = nvol;
1168 /**************************************************************
1170 **************************************************************/
1173 * Remove a volume from the linked list of volumes
1175 void volume_unlink(struct vol *volume)
1177 struct vol *vol, *ovol, *nvol;
1179 if (volume == Volumes) {
1180 Volumes = Volumes->v_next;
1183 for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) {
1186 if (vol == volume) {
1187 ovol->v_next = nvol;
1197 * Free all resources allocated in a struct vol
1199 void volume_free(struct vol *vol)
1201 free(vol->v_localname);
1202 vol->v_localname = NULL;
1203 free(vol->v_u8mname);
1204 vol->v_u8mname = NULL;
1205 free(vol->v_macname);
1206 vol->v_macname = NULL;
1208 free(vol->v_password);
1210 free(vol->v_volcodepage);
1211 free(vol->v_maccodepage);
1212 free(vol->v_cnidscheme);
1213 free(vol->v_dbpath);
1220 * Initialize volumes and load ini configfile
1222 * Depending on the value of obj->uid either access checks are done (!=0) or skipped (=0)
1224 * @param obj (r) handle
1225 * @param delvol_fn (r) callback called for deleted volumes
1227 int load_volumes(AFPObj *obj, void (*delvol_fn)(const struct vol *))
1231 struct passwd *pwent = NULL;
1236 LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
1239 if (!volfile_changed(&obj->options))
1241 /* TODO: volume reloading */
1246 /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
1248 fd = open(obj->options.configfile, O_RDONLY);
1250 while (retries < 2) {
1251 if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
1254 LOG(log_error, logtype_afpd, "readvolfile: can't lock configfile \"%s\"",
1255 obj->options.configfile);
1265 pwent = getpwuid(obj->uid);
1268 iniparser_freedict(obj->iniconfig);
1269 LOG(log_debug, logtype_afpd, "load_volumes: reloading: %s", obj->options.configfile);
1270 obj->iniconfig = iniparser_load(obj->options.configfile);
1272 EC_ZERO_LOG( readvolfile(obj, pwent) );
1274 for ( vol = Volumes; vol; vol = vol->v_next ) {
1275 if (vol->v_deleted && !vol->v_new ) {
1287 void unload_volumes(void)
1289 LOG(log_debug, logtype_afpd, "unload_volumes");
1293 struct vol *getvolumes(void)
1298 struct vol *getvolbyvid(const uint16_t vid )
1302 for ( vol = Volumes; vol; vol = vol->v_next ) {
1303 if ( vid == vol->v_vid ) {
1307 if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1314 struct vol *getvolbypath(const char *path)
1316 struct vol *vol = NULL;
1319 for (tmp = Volumes; tmp; tmp = tmp->v_next) {
1320 if (strncmp(path, tmp->v_path, strlen(tmp->v_path)) == 0) {
1330 * Initialize an AFPObj and options from ini config file
1332 int afp_config_parse(AFPObj *AFPObj)
1336 struct afp_options *options = &AFPObj->options;
1338 const char *p, *tmp;
1342 AFPObj->afp_version = 11;
1343 options->configfile = AFPObj->cmdlineconfigfile ? strdup(AFPObj->cmdlineconfigfile) : strdup(_PATH_CONFDIR "afp.conf");
1344 options->sigconffile = strdup(_PATH_CONFDIR "afp_signature.conf");
1345 options->uuidconf = strdup(_PATH_CONFDIR "afp_voluuid.conf");
1346 options->flags = OPTION_ACL2MACCESS | OPTION_UUID | OPTION_SERVERNOTIF | AFPObj->cmdlineflags;
1348 if ((config = iniparser_load(AFPObj->options.configfile)) == NULL)
1350 AFPObj->iniconfig = config;
1353 options->logconfig = iniparser_getstrdup(config, INISEC_GLOBAL, "loglevel", "default:note");
1354 options->logfile = iniparser_getstrdup(config, INISEC_GLOBAL, "logfile", NULL);
1356 /* [AFP] "options" options wo values */
1357 if (p = iniparser_getstrdup(config, INISEC_AFP, "options", NULL)) {
1358 if (p = strtok(q, ", ")) {
1360 if (strcasecmp(p, "nozeroconf"))
1361 options->flags |= OPTION_NOZEROCONF;
1362 if (strcasecmp(p, "icon"))
1363 options->flags |= OPTION_CUSTOMICON;
1364 if (strcasecmp(p, "noicon"))
1365 options->flags &= ~OPTION_CUSTOMICON;
1366 if (strcasecmp(p, "advertise_ssh"))
1367 options->flags |= OPTION_ANNOUNCESSH;
1368 if (strcasecmp(p, "noacl2maccess"))
1369 options->flags &= ~OPTION_ACL2MACCESS;
1370 if (strcasecmp(p, "keepsessions"))
1371 options->flags |= OPTION_KEEPSESSIONS;
1372 if (strcasecmp(p, "closevol"))
1373 options->flags |= OPTION_CLOSEVOL;
1374 if (strcasecmp(p, "client_polling"))
1375 options->flags &= ~OPTION_SERVERNOTIF;
1376 if (strcasecmp(p, "nosavepassword"))
1377 options->passwdbits |= PASSWD_NOSAVE;
1378 if (strcasecmp(p, "savepassword"))
1379 options->passwdbits &= ~PASSWD_NOSAVE;
1380 if (strcasecmp(p, "nosetpassword"))
1381 options->passwdbits &= ~PASSWD_SET;
1382 if (strcasecmp(p, "setpassword"))
1383 options->passwdbits |= PASSWD_SET;
1384 p = strtok(NULL, ", ");
1388 /* figure out options w values */
1390 options->loginmesg = iniparser_getstrdup(config, INISEC_AFP, "loginmesg", "");
1391 options->guest = iniparser_getstrdup(config, INISEC_AFP, "guestname", "nobody");
1392 options->passwdfile = iniparser_getstrdup(config, INISEC_AFP, "passwdfile", _PATH_AFPDPWFILE);
1393 options->uampath = iniparser_getstrdup(config, INISEC_AFP, "uampath", _PATH_AFPDUAMPATH);
1394 options->uamlist = iniparser_getstrdup(config, INISEC_AFP, "uamlist", "uams_dhx.so,uams_dhx2.so");
1395 options->port = iniparser_getstrdup(config, INISEC_AFP, "port", "548");
1396 options->signatureopt = iniparser_getstrdup(config, INISEC_AFP, "signature", "auto");
1397 options->k5service = iniparser_getstrdup(config, INISEC_AFP, "k5service", NULL);
1398 options->k5realm = iniparser_getstrdup(config, INISEC_AFP, "k5realm", NULL);
1399 options->listen = iniparser_getstrdup(config, INISEC_AFP, "listen", NULL);
1400 options->ntdomain = iniparser_getstrdup(config, INISEC_AFP, "ntdomain", NULL);
1401 options->ntseparator = iniparser_getstrdup(config, INISEC_AFP, "ntseparator", NULL);
1402 options->mimicmodel = iniparser_getstrdup(config, INISEC_AFP, "mimicmodel", NULL);
1403 options->adminauthuser = iniparser_getstrdup(config, INISEC_AFP, "adminauthuser", NULL);
1404 options->connections = iniparser_getint (config, INISEC_AFP, "maxcon", 200);
1405 options->passwdminlen = iniparser_getint (config, INISEC_AFP, "passwdminlen", 0);
1406 options->tickleval = iniparser_getint (config, INISEC_AFP, "tickleval", 30);
1407 options->timeout = iniparser_getint (config, INISEC_AFP, "timeout", 4);
1408 options->dsireadbuf = iniparser_getint (config, INISEC_AFP, "dsireadbuf", 12);
1409 options->server_quantum = iniparser_getint (config, INISEC_AFP, "server_quantum", DSI_SERVQUANT_DEF);
1410 options->volnamelen = iniparser_getint (config, INISEC_AFP, "volnamelen", 80);
1411 options->dircachesize = iniparser_getint (config, INISEC_AFP, "dircachesize", DEFAULT_MAX_DIRCACHE_SIZE);
1412 options->tcp_sndbuf = iniparser_getint (config, INISEC_AFP, "tcpsndbuf", 0);
1413 options->tcp_rcvbuf = iniparser_getint (config, INISEC_AFP, "tcprcvbuf", 0);
1414 options->fce_fmodwait = iniparser_getint (config, INISEC_AFP, "fceholdfmod", 60);
1415 options->sleep = iniparser_getint (config, INISEC_AFP, "sleep", 10) * 60 * 2;
1416 options->disconnected = iniparser_getint (config, INISEC_AFP, "disconnect", 24) * 60 * 2;
1418 if ((p = iniparser_getstring(config, INISEC_AFP, "hostname", NULL))) {
1419 EC_NULL_LOG( options->hostname = strdup(p) );
1421 if (gethostname(val, sizeof(val)) < 0 ) {
1422 perror( "gethostname" );
1425 if ((q = strchr(val, '.')))
1427 options->hostname = strdup(val);
1430 if ((p = iniparser_getstring(config, INISEC_AFP, "k5keytab", NULL))) {
1431 EC_NULL_LOG( options->k5keytab = malloc(strlen(p) + 14) );
1432 snprintf(options->k5keytab, strlen(p) + 14, "KRB5_KTNAME=%s", p);
1433 putenv(options->k5keytab);
1437 if ((p = iniparser_getstring(config, INISEC_AFP, "admingroup", NULL))) {
1438 struct group *gr = getgrnam(p);
1440 options->admingid = gr->gr_gid;
1442 #endif /* ADMIN_GRP */
1444 q = iniparser_getstrdup(config, INISEC_AFP, "cnidserver", "localhost:4700");
1445 r = strrchr(q, ':');
1448 options->Cnid_srv = strdup(q);
1450 options->Cnid_port = strdup(r + 1);
1452 options->Cnid_port = strdup("4700");
1453 LOG(log_debug, logtype_afpd, "CNID Server: %s:%s", options->Cnid_srv, options->Cnid_port);
1457 if ((q = iniparser_getstrdup(config, INISEC_AFP, "fqdn", NULL))) {
1458 /* do a little checking for the domain name. */
1462 if (gethostbyname(q)) {
1465 EC_NULL_LOG( options->fqdn = strdup(q) );
1467 LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", c);
1472 if (!(p = iniparser_getstring(config, INISEC_AFP, "unixcodepage", NULL))) {
1473 options->unixcharset = CH_UNIX;
1474 options->unixcodepage = strdup("LOCALE");
1476 if ((options->unixcharset = add_charset(p)) == (charset_t)-1) {
1477 options->unixcharset = CH_UNIX;
1478 options->unixcodepage = strdup("LOCALE");
1479 LOG(log_warning, logtype_afpd, "Setting Unix codepage to '%s' failed", p);
1481 options->unixcodepage = strdup(p);
1485 if (!(p = iniparser_getstring(config, INISEC_AFP, "maccodepage", NULL))) {
1486 options->maccharset = CH_MAC;
1487 options->maccodepage = strdup("MAC_ROMAN");
1489 if ((options->maccharset = add_charset(p)) == (charset_t)-1) {
1490 options->maccharset = CH_MAC;
1491 options->maccodepage = strdup("MAC_ROMAN");
1492 LOG(log_warning, logtype_afpd, "Setting Unix codepage to '%s' failed", p);
1494 options->maccodepage = strdup(p);
1498 /* Check for sane values */
1499 if (options->tickleval <= 0)
1500 options->tickleval = 30;
1501 if (options->timeout <= 0)
1502 options->timeout = 4;
1503 if (options->sleep <= 4)
1504 options->disconnected = options->sleep = 4;
1505 if (options->dsireadbuf < 6)
1506 options->dsireadbuf = 6;
1507 if (options->volnamelen < 8)
1508 options->volnamelen = 8; /* max mangled volname "???#FFFF" */
1509 if (options->volnamelen > 255)
1510 options->volnamelen = 255; /* AFP3 spec */