]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/netatalk_conf.c
Add missing file
[netatalk.git] / libatalk / util / netatalk_conf.c
1 /*
2   Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
3
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.
8
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.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <pwd.h>
23 #include <grp.h>
24 #include <utime.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/param.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <inttypes.h>
32 #include <time.h>
33
34 #include <atalk/afp.h>
35 #include <atalk/util.h>
36 #include <atalk/logger.h>
37 #include <atalk/ea.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
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 */
55 /* Usable slot: 9 */
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 */
75
76 #define VOLOPT_MAX           31  /* <== IMPORTANT !!!!!! */
77 #define VOLOPT_NUM           (VOLOPT_MAX + 1)
78
79 #define VOLPASSLEN  8
80
81 #define IS_VAR(a, b) (strncmp((a), (b), 2) == 0)
82
83 struct vol_option {
84     char *c_value;
85     int i_value;
86 };
87
88 /**************************************************************
89  * Locals
90  **************************************************************/
91
92 static struct vol *Volumes = NULL;
93 static uint16_t    lastvid = 0;
94
95 /* 
96  * Get a volumes UUID from the config file.
97  * If there is none, it is generated and stored there.
98  *
99  * Returns pointer to allocated storage on success, NULL on error.
100  */
101 static char *get_vol_uuid(const AFPObj *obj, const char *volname)
102 {
103     char *volname_conf;
104     char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
105     FILE *fp;
106     struct stat tmpstat;
107     int fd;
108     
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) { 
112             p = buf;
113             while (p && isblank(*p))
114                 p++;
115             if (!p || (*p == '#') || (*p == '\n'))
116                 continue;                             /* invalid line */
117             if (*p == '"') {
118                 p++;
119                 if ((volname_conf = strtok( p, "\"" )) == NULL)
120                     continue;                         /* syntax error */
121             } else {
122                 if ((volname_conf = strtok( p, " \t" )) == NULL)
123                     continue;                         /* syntax error: invalid name */
124             }
125             p = strchr(p, '\0');
126             p++;
127             if (*p == '\0')
128                 continue;                             /* syntax error */
129             
130             if (strcmp(volname, volname_conf) != 0)
131                 continue;                             /* another volume name */
132                 
133             while (p && isblank(*p))
134                 p++;
135
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);
140                 fclose(fp);
141                 return strdup(uuid);
142             }
143         }
144     }
145
146     if (fp)
147         fclose(fp);
148
149     /*  not found or no file, reopen in append mode */
150
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));
155             return NULL;
156         }
157         if (( fp = fdopen( fd, "w" )) == NULL ) {
158             LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).",
159                 obj->options.uuidconf, strerror(errno));
160             close(fd);
161             return NULL;
162         }
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));
166         return NULL;
167     }
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");
173     } else {
174         fseek(fp, -1L, SEEK_END);
175         if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
176     }                    
177     
178     /* generate uuid and write to file */
179     atalk_uuid_t id;
180     const char *cp;
181     randombytes((void *)id, 16);
182     cp = uuid_bin2string(id);
183
184     LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
185
186     fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
187     fclose(fp);
188     
189     return strdup(cp);
190 }
191
192 /*
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.
197 */
198 static int do_check_ea_support(const struct vol *vol)
199 {
200     int haseas;
201     char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
202     const char *eacontent = "yes";
203
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);
206         return 1;
207     }
208
209     mktemp(eaname);
210
211     become_root();
212
213     if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
214         sys_removexattr(vol->v_path, eaname);
215         haseas = 1;
216     } else {
217         LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes",
218             vol->v_localname);
219         haseas = 0;
220     }
221
222     unbecome_root();
223
224     return haseas;
225 }
226
227 static void check_ea_support(struct vol *vol)
228 {
229     int haseas;
230     char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
231     const char *eacontent = "yes";
232
233     haseas = do_check_ea_support(vol);
234
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;
239             return;
240         }
241
242         if (haseas) {
243             vol->v_vfs_ea = AFPVOL_EA_SYS;
244         } else {
245             LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
246                 vol->v_localname);
247             vol->v_vfs_ea = AFPVOL_EA_AD;
248         }
249     }
250
251     if (vol->v_adouble == AD_VERSION_EA) {
252         if (!haseas)
253             vol->v_adouble = AD_VERSION2;
254     }
255 }
256
257 /*!
258  * Check whether a volume supports ACLs
259  *
260  * @param vol  (r) volume
261  *
262  * @returns        0 if not, 1 if yes
263  */
264 static int check_vol_acl_support(const struct vol *vol)
265 {
266     int ret = 0;
267
268 #ifdef HAVE_SOLARIS_ACLS
269     ace_t *aces = NULL;
270     ret = 1;
271     if (get_nfsv4_acl(vol->v_path, &aces) == -1)
272         ret = 0;
273 #endif
274 #ifdef HAVE_POSIX_ACLS
275     acl_t acl = NULL;
276     ret = 1;
277     if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
278         ret = 0;
279 #endif
280
281 #ifdef HAVE_SOLARIS_ACLS
282     if (aces) free(aces);
283 #endif
284 #ifdef HAVE_POSIX_ACLS
285     if (acl) acl_free(acl);
286 #endif /* HAVE_POSIX_ACLS */
287
288     LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
289         vol->v_path, ret ? "yes" : "no");
290     return ret;
291 }
292
293 static void volfree(struct vol_option *options, const struct vol_option *save)
294 {
295     int i;
296
297     if (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);
301         }
302     } else {
303         for (i = 0; i < VOLOPT_MAX; i++) {
304             if (options[i].c_value)
305                 free(options[i].c_value);
306         }
307     }
308 }
309
310 /*
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)
316  * $g   -> group
317  * $h   -> hostname
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
322  * $$   -> $
323  *
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
329  *
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.
333  */
334 static char *volxlate(const AFPObj *obj,
335                       char *dest,
336                       size_t destlen,
337                       char *src,
338                       const struct passwd *pwd,
339                       char *path,
340                       char *volname)
341 {
342     char *p, *r;
343     const char *q;
344     int len;
345     char *ret;
346     int xlatevolname = 0;
347
348     if (path && !volname)
349         /* cf above */
350         xlatevolname = 1;
351
352     if (!src) {
353         return NULL;
354     }
355     if (!dest) {
356         dest = calloc(destlen +1, 1);
357     }
358     ret = dest;
359     if (!ret) {
360         return NULL;
361     }
362     strlcpy(dest, src, destlen +1);
363     if ((p = strchr(src, '$')) == NULL) /* nothing to do */
364         return ret;
365
366     /* first part of the path. just forward to the next variable. */
367     len = MIN((size_t)(p - src), destlen);
368     if (len > 0) {
369         destlen -= len;
370         dest += len;
371     }
372
373     while (p && destlen > 0) {
374         /* now figure out what the variable is */
375         q = NULL;
376         if (IS_VAR(p, "$b")) {
377             if (!obj->uid && xlatevolname)
378                 return NULL;
379             if (path) {
380                 if ((q = strrchr(path, '/')) == NULL)
381                     q = path;
382                 else if (*(q + 1) != '\0')
383                     q++;
384             }
385         } else if (IS_VAR(p, "$c")) {
386             if (!obj->uid  && xlatevolname)
387                 return NULL;
388             DSI *dsi = obj->dsi;
389             len = sprintf(dest, "%s:%u",
390                           getip_string((struct sockaddr *)&dsi->client),
391                           getip_port((struct sockaddr *)&dsi->client));
392             dest += len;
393             destlen -= len;
394         } else if (IS_VAR(p, "$d")) {
395             if (!obj->uid  && xlatevolname)
396                 return NULL;
397             q = path;
398         } else if (pwd && IS_VAR(p, "$f")) {
399             if (!obj->uid  && xlatevolname)
400                 return NULL;
401             if ((r = strchr(pwd->pw_gecos, ',')))
402                 *r = '\0';
403             q = pwd->pw_gecos;
404         } else if (pwd && IS_VAR(p, "$g")) {
405             if (!obj->uid  && xlatevolname)
406                 return NULL;
407             struct group *grp = getgrgid(pwd->pw_gid);
408             if (grp)
409                 q = grp->gr_name;
410         } else if (IS_VAR(p, "$h")) {
411             q = obj->options.hostname;
412         } else if (IS_VAR(p, "$i")) {
413             if (!obj->uid  && xlatevolname)
414                 return NULL;
415             DSI *dsi = obj->dsi;
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)
421                 return NULL;
422             char* sep = NULL;
423             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
424                 q = sep+1;
425             else
426                 q = obj->username;
427         } else if (IS_VAR(p, "$v")) {
428             if (!obj->uid  && xlatevolname)
429                 return NULL;
430             if (volname) {
431                 q = volname;
432             }
433             else if (path) {
434                 if ((q = strrchr(path, '/')) == NULL)
435                     q = path;
436                 else if (*(q + 1) != '\0')
437                     q++;
438             }
439         } else if (IS_VAR(p, "$$")) {
440             q = "$";
441         } else
442             q = p;
443
444         /* copy the stuff over. if we don't understand something that we
445          * should, just skip it over. */
446         if (q) {
447             len = MIN(p == q ? 2 : strlen(q), destlen);
448             strncpy(dest, q, len);
449             dest += len;
450             destlen -= len;
451         }
452
453         /* stuff up to next $ */
454         src = p + 2;
455         p = strchr(src, '$');
456         len = p ? MIN((size_t)(p - src), destlen) : destlen;
457         if (len > 0) {
458             strncpy(dest, src, len);
459             dest += len;
460             destlen -= len;
461         }
462     }
463     return ret;
464 }
465
466 /* -------------------- */
467 static void setoption(struct vol_option *options, const struct vol_option *save, int opt, const char *val)
468 {
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);
472 }
473
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)
476 {
477     const char *val;
478     char *p, *q;
479
480     if (val = iniparser_getstring(conf, vol, "allow", NULL))
481         setoption(options, save, VOLOPT_ALLOW, val);
482
483     if (val = iniparser_getstring(conf, vol, "deny", NULL))
484         setoption(options, save, VOLOPT_DENY, val);
485
486     if (val = iniparser_getstring(conf, vol, "rwlist", NULL))
487         setoption(options, save, VOLOPT_RWLIST, val);
488
489     if (val = iniparser_getstring(conf, vol, "rolist", NULL))
490         setoption(options, save, VOLOPT_ROLIST, val);
491
492     if (val = iniparser_getstring(conf, vol, "volcharset", NULL))
493         setoption(options, save, VOLOPT_ENCODING, val);
494
495     if (val = iniparser_getstring(conf, vol, "maccharset", NULL))
496         setoption(options, save, VOLOPT_MACCHARSET, val);
497
498     if (val = iniparser_getstring(conf, vol, "veto", NULL))
499         setoption(options, save, VOLOPT_VETO, val);
500
501     if (val = iniparser_getstring(conf, vol, "cnidscheme", NULL))
502         setoption(options, save, VOLOPT_CNIDSCHEME, val);
503
504     if (val = iniparser_getstring(conf, vol, "dbpath", NULL))
505         setoption(options, save, VOLOPT_DBPATH, val);
506
507     if (val = iniparser_getstring(conf, vol, "password", NULL))
508         setoption(options, save, VOLOPT_PASSWORD, val);
509
510     if (val = iniparser_getstring(conf, vol, "root_preexec", NULL))
511         setoption(options, save, VOLOPT_ROOTPREEXEC, val);
512
513     if (val = iniparser_getstring(conf, vol, "preexec", NULL))
514         setoption(options, save, VOLOPT_PREEXEC, val);
515
516     if (val = iniparser_getstring(conf, vol, "root_postexec", NULL))
517         setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
518
519     if (val = iniparser_getstring(conf, vol, "postexec", NULL))
520         setoption(options, save, VOLOPT_POSTEXEC, val);
521
522     if (val = iniparser_getstring(conf, vol, "allowed_hosts", NULL))
523         setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
524
525     if (val = iniparser_getstring(conf, vol, "denied_hosts", NULL))
526         setoption(options, save, VOLOPT_DENIED_HOSTS, val);
527
528     if (val = iniparser_getstring(conf, vol, "umask", NULL))
529         options[VOLOPT_UMASK].i_value = (int)strtol(val, NULL, 8);
530
531     if (val = iniparser_getstring(conf, vol, "dperm", NULL))
532         options[VOLOPT_DPERM].i_value = (int)strtol(val, NULL, 8);
533
534     if (val = iniparser_getstring(conf, vol, "fperm", NULL))
535         options[VOLOPT_FPERM].i_value = (int)strtol(val, NULL, 8);
536
537     if (val = iniparser_getstring(conf, vol, "perm", NULL))
538         options[VOLOPT_DFLTPERM].i_value = (int)strtol(val, NULL, 8);
539
540     if (val = iniparser_getstring(conf, vol, "volsizelimit", NULL))
541         options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val, NULL, 10);
542
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;
552     }
553
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;
559     }
560
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;
568     }
569
570     if (p = iniparser_getstrdup(conf, vol, "cnidserver", NULL)) {
571         if (q = strrchr(val, ':')) {
572             *q = 0;
573             setoption(options, save, VOLOPT_CNIDPORT, q + 1);
574         }
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");
578         free(p);
579     }
580
581     if (q = iniparser_getstrdup(conf, vol, "options", NULL)) {
582         if (p = strtok(q, ", ")) {
583             while (p) {
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, ", ");
617             }
618         }
619         free(q);
620     }
621 }
622
623 /* ------------------------------- */
624 static int creatvol(const AFPObj *obj, const struct passwd *pwd,
625                     char *path, char *name,
626                     struct vol_option *options)
627 {
628     struct vol  *volume;
629     int         suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
630     int         hide = 0;
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 */
634     uint16_t   flags;
635
636     LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
637
638     if ( name == NULL || *name == '\0' ) {
639         if ((name = strrchr( path, '/' )) == NULL) {
640             return -1;  /* Obviously not a fully qualified path */
641         }
642
643         /* if you wish to share /, you need to specify a name. */
644         if (*++name == '\0')
645             return -1;
646     }
647
648     /* suffix for mangling use (lastvid + 1)   */
649     /* because v_vid has not been decided yet. */
650     suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
651
652     vlen = strlen( name );
653
654     /* Unicode Volume Name */
655     /* Firstly convert name from unixcharset to UTF8-MAC */
656     flags = CONV_IGNORE;
657     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
658     if (tmpvlen <= 0) {
659         strcpy(tmpname, "???");
660         tmpvlen = 3;
661     }
662
663     /* Do we have to mangle ? */
664     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
665         if (tmpvlen + suffixlen > obj->options.volnamelen) {
666             flags = CONV_FORCE;
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;
669         }
670         strcat(tmpname, suffix);
671         tmpvlen = strlen(tmpname);
672     }
673
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)) )
676         return -1;
677
678     LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
679
680     /* Maccharset Volume Name */
681     /* Firsty convert name from unixcharset to maccharset */
682     flags = CONV_IGNORE;
683     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
684     if (tmpvlen <= 0) {
685         strcpy(tmpname, "???");
686         tmpvlen = 3;
687     }
688
689     /* Do we have to mangle ? */
690     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
691         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
692             flags = CONV_FORCE;
693             tmpvlen = convert_charset(obj->options.unixcharset,
694                                       obj->options.maccharset,
695                                       0,
696                                       name,
697                                       vlen,
698                                       tmpname,
699                                       AFPVOL_MACNAMELEN - suffixlen,
700                                       &flags);
701             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
702         }
703         strcat(tmpname, suffix);
704         tmpvlen = strlen(tmpname);
705     }
706
707     /* Secondly convert name from maccharset to UCS2 */
708     if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
709                                          CH_UCS2,
710                                          tmpname,
711                                          tmpvlen,
712                                          mactmpname,
713                                          AFPVOL_U8MNAMELEN*2)) )
714         return -1;
715
716     LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
717
718     /* check duplicate */
719     for ( volume = Volumes; volume; volume = volume->v_next ) {
720         if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
721              ||
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;
728             }
729             else {
730                 return -1;  /* Won't be able to access it, anyway... */
731             }
732         }
733     }
734
735     if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
736         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
737         return -1;
738     }
739     if ( NULL == ( volume->v_localname = strdup(name))) {
740         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
741         free(volume);
742         return -1;
743     }
744
745     if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
746         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
747         volume_free(volume);
748         free(volume);
749         return -1;
750     }
751     if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
752         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
753         volume_free(volume);
754         free(volume);
755         return -1;
756     }
757     if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
758         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
759         volume_free(volume);
760         free(volume);
761         return -1;
762     }
763
764     volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname;
765     volume->v_hide = hide;
766     strcpy( volume->v_path, path );
767
768 #ifdef __svr4__
769     volume->v_qfd = -1;
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);
774 #ifdef HAVE_ACLS
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;
778     }
779 #endif
780
781     /* handle options */
782     if (options) {
783         volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
784         volume->v_flags |= options[VOLOPT_FLAGS].i_value;
785
786         if (options[VOLOPT_EA_VFS].i_value)
787             volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
788
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;
796
797         if (options[VOLOPT_PASSWORD].c_value)
798             volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
799
800         if (options[VOLOPT_VETO].c_value)
801             volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
802
803         if (options[VOLOPT_ENCODING].c_value)
804             volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
805
806         if (options[VOLOPT_MACCHARSET].c_value)
807             volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
808
809         if (options[VOLOPT_DBPATH].c_value)
810             volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);
811
812         if (options[VOLOPT_CNIDSCHEME].c_value)
813             volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
814
815         if (options[VOLOPT_CNIDSERVER].c_value)
816             volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
817
818         if (options[VOLOPT_CNIDPORT].c_value)
819             volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
820
821         if (options[VOLOPT_UMASK].i_value)
822             volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
823
824         if (options[VOLOPT_DPERM].i_value)
825             volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value;
826
827         if (options[VOLOPT_FPERM].i_value)
828             volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value;
829
830         if (options[VOLOPT_DFLTPERM].i_value)
831             volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value;
832
833         if (options[VOLOPT_ADOUBLE].i_value)
834             volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
835         else
836             volume->v_adouble = AD_VERSION;
837
838         if (options[VOLOPT_LIMITSIZE].i_value)
839             volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value;
840
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;
849
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;
854
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;
861
862         if ((volume->v_flags & AFPVOL_EILSEQ))
863             volume->v_utom_flags |= CONV__EILSEQ;
864
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;
868
869         if (options[VOLOPT_POSTEXEC].c_value)
870             volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name);
871
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;
875
876         if (options[VOLOPT_ROOTPOSTEXEC].c_value)
877             volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path,  name);
878     }
879     volume->v_dperm |= volume->v_perm;
880     volume->v_fperm |= volume->v_perm;
881
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);
885     initvol_vfs(volume);
886
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);
890         if (!uuid) {
891             LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
892                 volume->v_localname);
893         } else {
894             volume->v_uuid = uuid;
895             LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
896                 volume->v_localname, volume->v_uuid);
897         }
898     }
899
900     volume->v_next = Volumes;
901     Volumes = volume;
902     return 0;
903 }
904
905 /* check access list. this function wants something of the following
906  * form:
907  *        @group,name,name2,@group2,name3
908  *
909  * a NULL argument allows everybody to have access.
910  * we return three things:
911  *     -1: no list
912  *      0: list exists, but name isn't in it
913  *      1: in list
914  */
915
916 #ifndef NO_REAL_USER_NAME
917 /* authentication is case insensitive
918  * FIXME should we do the same with group name?
919  */
920 #define access_strcmp strcasecmp
921
922 #else
923 #define access_strcmp strcmp
924
925 #endif
926
927 static int accessvol(const AFPObj *obj, const char *args, const char *name)
928 {
929     char buf[MAXPATHLEN + 1], *p;
930     struct group *gr;
931
932     if (!args)
933         return -1;
934
935     strlcpy(buf, args, sizeof(buf));
936     if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
937         return -1;
938
939     while (p) {
940         if (*p == '@') { /* it's a group */
941             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid, obj->ngroups, obj->groups))
942                 return 1;
943         } else if (access_strcmp(p, name) == 0) /* it's a user name */
944             return 1;
945         p = strtok(NULL, ",");
946     }
947
948     return 0;
949 }
950
951 static int hostaccessvol(const AFPObj *obj, int type, const char *volname, const char *args)
952 {
953     int mask_int;
954     char buf[MAXPATHLEN + 1], *p, *b;
955     struct sockaddr_storage client;
956     const DSI *dsi = obj->dsi;
957
958     if (!args)
959         return -1;
960
961     strlcpy(buf, args, sizeof(buf));
962     if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */
963         return -1;
964
965     while (p) {
966         int ret;
967         char *ipaddr, *mask_char;
968         struct addrinfo hints, *ai;
969
970         ipaddr = strtok(p, "/");
971         mask_char = strtok(NULL,"/");
972
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));
979             continue;
980         }
981
982         /* netmask */
983         if (mask_char != NULL)
984             mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */
985         else {
986             if (ai->ai_family == AF_INET) /* IPv4 */
987                 mask_int = 32;
988             else                          /* IPv6 */
989                 mask_int = 128;
990         }
991
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);
996
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);
1001             freeaddrinfo(ai);
1002             return 1;
1003         }
1004
1005         /* next address */
1006         freeaddrinfo(ai);
1007         p = strtok_r(NULL, ",", &b);
1008     }
1009
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);
1013     return 0;
1014 }
1015
1016 /* ----------------------
1017  */
1018 static int volfile_changed(struct afp_options *p)
1019 {
1020     struct stat      st;
1021
1022     if (!stat(p->configfile, &st) && st.st_mtime > p->volfile.mtime) {
1023         p->volfile.mtime = st.st_mtime;
1024         return 1;
1025     }
1026     return 0;
1027 }
1028
1029 static int vol_section(const char *sec)
1030 {
1031     if (STRCMP(sec, ==, INISEC_GLOBAL))
1032         return 0;
1033     if (STRCMP(sec, ==, INISEC_AFP))
1034         return 0;
1035     if (STRCMP(sec, ==, INISEC_CNID))
1036         return 0;
1037     return 1;
1038 }
1039
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.
1044  */
1045 static int readvolfile(const AFPObj *obj, const struct passwd *pwent)
1046 {
1047     char        path[MAXPATHLEN + 1];
1048     char        volname[AFPVOL_U8MNAMELEN + 1];
1049     char        tmp[MAXPATHLEN + 1];
1050     char        *u;
1051     const char  *p;
1052     int         fd;
1053     int         i;
1054     struct passwd     *pw;
1055     struct vol_option save_options[VOLOPT_NUM];
1056     struct vol_option default_options[VOLOPT_NUM];
1057     struct vol_option options[VOLOPT_NUM];
1058
1059     LOG(log_debug, logtype_afpd, "readvolfile: BEGIN");
1060
1061     memset(default_options, 0, sizeof(default_options));
1062
1063     /* Enable some default options for all volumes */
1064     default_options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_UNIX_PRIV;
1065 #ifdef HAVE_ACLS
1066     default_options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
1067 #endif
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));
1071
1072     int secnum = iniparser_getnsec(obj->iniconfig);    
1073     LOG(log_debug, logtype_afpd, "readvolfile: sections: %d", secnum);
1074     const char *secname;
1075
1076     for (i = 0; i < secnum; i++) { 
1077         secname = iniparser_getsecname(obj->iniconfig, i);
1078         if (!vol_section(secname))
1079             continue;
1080
1081         strlcpy(volname, secname, AFPVOL_U8MNAMELEN);
1082         LOG(log_debug, logtype_afpd, "readvolfile: volume: %s", volname);
1083
1084         if ((p = iniparser_getstrdup(obj->iniconfig, secname, "path", NULL)) == NULL)
1085             continue;
1086         strlcpy(path, p, MAXPATHLEN);
1087         strcpy(tmp, path);
1088
1089         if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
1090             continue;
1091
1092         memcpy(options, default_options, sizeof(options));
1093         volset(obj->iniconfig, secname, options, default_options);
1094
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) */
1098
1099         if (pwent) {
1100             if (accessvol(obj, options[VOLOPT_DENY].c_value, pwent->pw_name) == 1)
1101                 continue;
1102             if (accessvol(obj, options[VOLOPT_ALLOW].c_value, pwent->pw_name) == 0)
1103                 continue;
1104             if (hostaccessvol(obj, VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value) == 1)
1105                 continue;
1106             if (hostaccessvol(obj, VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value) == 0)
1107                 continue;
1108         }
1109
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;
1118         }
1119
1120         /* do variable substitution for volname */
1121         if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL) {
1122             volfree(options, default_options);
1123             continue;
1124         }
1125
1126         creatvol(obj, pwent, path, tmp, options);
1127         volfree(options, default_options);
1128     }
1129     volfree(save_options, NULL);
1130     return( 0 );
1131 }
1132
1133 /* ------------------------------- */
1134 static void free_volumes(void)
1135 {
1136     struct vol  *vol;
1137     struct vol  *nvol, *ovol;
1138
1139     for ( vol = Volumes; vol; vol = vol->v_next ) {
1140         if (( vol->v_flags & AFPVOL_OPEN ) ) {
1141             vol->v_deleted = 1;
1142             continue;
1143         }
1144         volume_free(vol);
1145     }
1146
1147     for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1148         nvol = vol->v_next;
1149
1150         if (vol->v_localname == NULL) {
1151             if (Volumes == vol) {
1152                 Volumes = nvol;
1153                 ovol = Volumes;
1154             }
1155             else {
1156                 ovol->v_next = nvol;
1157             }
1158             free(vol);
1159         }
1160         else {
1161             ovol = vol;
1162         }
1163     }
1164 }
1165
1166 /**************************************************************
1167  * API functions
1168  **************************************************************/
1169
1170 /*!
1171  * Remove a volume from the linked list of volumes
1172  */
1173 void volume_unlink(struct vol *volume)
1174 {
1175     struct vol *vol, *ovol, *nvol;
1176
1177     if (volume == Volumes) {
1178         Volumes = Volumes->v_next;
1179         return;
1180     }
1181     for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) {
1182         nvol = vol->v_next;
1183
1184         if (vol == volume) {
1185             ovol->v_next = nvol;
1186             break;
1187         }
1188         else {
1189             ovol = vol;
1190         }
1191     }
1192 }
1193
1194 /*!
1195  * Free all resources allocated in a struct vol
1196  */
1197 void volume_free(struct vol *vol)
1198 {
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;
1205     free(vol->v_path);
1206     free(vol->v_password);
1207     free(vol->v_veto);
1208     free(vol->v_volcodepage);
1209     free(vol->v_maccodepage);
1210     free(vol->v_cnidscheme);
1211     free(vol->v_dbpath);
1212     free(vol->v_gvs);
1213     if (vol->v_uuid)
1214         free(vol->v_uuid);
1215 }
1216
1217 /*!
1218  * Initialize volumes and load ini configfile
1219  *
1220  * Depending on the value of obj->uid either access checks are done (!=0) or skipped (=0)
1221  *
1222  * @param obj       (r) handle
1223  * @param delvol_fn (r) callback called for deleted volumes
1224  */
1225 int load_volumes(AFPObj *obj, void (*delvol_fn)(const struct vol *))
1226 {
1227     EC_INIT;
1228     int fd = -1;
1229     struct passwd   *pwent = NULL;
1230     struct stat         st;
1231     int retries = 0;
1232     struct vol *vol;
1233
1234     LOG(log_debug, logtype_afpd, "load_volumes: BEGIN");
1235
1236     if (Volumes) {
1237         if (!volfile_changed(&obj->options))
1238             goto EC_CLEANUP;
1239         free_volumes();
1240     }
1241
1242     /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
1243
1244     fd = open(obj->options.configfile, O_RDONLY);
1245
1246     while (retries < 2) {
1247         if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
1248             retries++;
1249             if (!retries) {
1250                 LOG(log_error, logtype_afpd, "readvolfile: can't lock configfile \"%s\"",
1251                     obj->options.configfile);
1252                 EC_FAIL;
1253             }
1254             sleep(1);
1255             continue;
1256         }
1257         break;
1258     }
1259
1260     if (obj->uid)
1261         pwent = getpwuid(obj->uid);
1262
1263     if (obj->iniconfig)
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);
1267
1268     EC_ZERO_LOG( readvolfile(obj, pwent) );
1269
1270     for ( vol = Volumes; vol; vol = vol->v_next ) {
1271         if (vol->v_deleted && !vol->v_new ) {
1272             delvol_fn(vol);
1273             vol = Volumes;
1274         }
1275     }
1276
1277 EC_CLEANUP:
1278     if (fd != -1)
1279         (void)close(fd);
1280     EC_EXIT;
1281 }
1282
1283 void unload_volumes(void)
1284 {
1285     LOG(log_debug, logtype_afpd, "unload_volumes");
1286     free_volumes();
1287 }
1288
1289 const struct vol *getvolumes(void)
1290 {
1291     return Volumes;
1292 }
1293
1294 /* ------------------------- */
1295 struct vol *getvolbyvid(const uint16_t vid )
1296 {
1297     struct vol  *vol;
1298
1299     for ( vol = Volumes; vol; vol = vol->v_next ) {
1300         if ( vid == vol->v_vid ) {
1301             break;
1302         }
1303     }
1304     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1305         return( NULL );
1306     }
1307
1308     return( vol );
1309 }
1310
1311 #define MAXVAL 1024
1312 /*!
1313  * Initialize an AFPObj and options from ini config file
1314  */
1315 int afp_config_parse(AFPObj *AFPObj)
1316 {
1317     EC_INIT;
1318     dictionary *config;
1319     struct afp_options *options = &AFPObj->options;
1320     int i, c;
1321     const char *p, *tmp;
1322     char *q, *r;
1323     char val[MAXVAL];
1324
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;
1329
1330     if ((config = iniparser_load(AFPObj->options.configfile)) == NULL)
1331         return -1;
1332     AFPObj->iniconfig = config;
1333
1334     /* [Global] */
1335     options->logconfig = iniparser_getstrdup(config, INISEC_GLOBAL, "loglevel", "default:note");
1336     options->logfile   = iniparser_getstrdup(config, INISEC_GLOBAL, "logfile",  NULL);
1337
1338     /* [AFP] "options" options wo values */
1339     if (p = iniparser_getstrdup(config, INISEC_AFP, "options", NULL)) {
1340         if (p = strtok(q, ", ")) {
1341             while (p) {
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, ", ");
1367             }
1368         }
1369     }
1370     /* figure out options w values */
1371
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;
1399
1400     if ((p = iniparser_getstring(config, INISEC_AFP, "hostname", NULL))) {
1401         EC_NULL_LOG( options->hostname = strdup(p) );
1402     } else {
1403         if (gethostname(val, sizeof(val)) < 0 ) {
1404             perror( "gethostname" );
1405             EC_FAIL;
1406         }
1407         if ((q = strchr(val, '.')))
1408             *q = '\0';
1409         options->hostname = strdup(val);
1410     }
1411
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);
1416     }
1417
1418 #ifdef ADMIN_GRP
1419     if ((p = iniparser_getstring(config, INISEC_AFP, "admingroup",  NULL))) {
1420          struct group *gr = getgrnam(p);
1421          if (gr != NULL)
1422              options->admingid = gr->gr_gid;
1423     }
1424 #endif /* ADMIN_GRP */
1425
1426     q = iniparser_getstrdup(config, INISEC_AFP, "cnidserver", "localhost:4700");
1427     r = strrchr(q, ':');
1428     if (r)
1429         *r = 0;
1430     options->Cnid_srv = strdup(q);
1431     if (r)
1432         options->Cnid_port = strdup(r + 1);
1433     else
1434         options->Cnid_port = strdup("4700");
1435     LOG(log_debug, logtype_afpd, "CNID Server: %s:%s", options->Cnid_srv, options->Cnid_port);
1436     if (q)
1437         free(q);
1438
1439     if ((q = iniparser_getstrdup(config, INISEC_AFP, "fqdn", NULL))) {
1440         /* do a little checking for the domain name. */
1441         r = strchr(q, ':');
1442         if (r)
1443             *r = '\0';
1444         if (gethostbyname(q)) {
1445             if (r)
1446                 *r = ':';
1447             EC_NULL_LOG( options->fqdn = strdup(q) );
1448         } else {
1449             LOG(log_error, logtype_afpd, "error parsing -fqdn, gethostbyname failed for: %s", c);
1450         }
1451         free(q);
1452     }
1453
1454     if (!(p = iniparser_getstring(config, INISEC_AFP, "unixcodepage", NULL))) {
1455         options->unixcharset = CH_UNIX;
1456         options->unixcodepage = strdup("LOCALE");
1457     } else {
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);
1462         } else {
1463             options->unixcodepage = strdup(p);
1464         }
1465     }
1466         
1467     if (!(p = iniparser_getstring(config, INISEC_AFP, "maccodepage", NULL))) {
1468         options->maccharset = CH_MAC;
1469         options->maccodepage = strdup("MAC_ROMAN");
1470     } else {
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);
1475         } else {
1476             options->maccodepage = strdup(p);
1477         }
1478     }
1479
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 */
1493
1494 EC_CLEANUP:
1495     EC_EXIT;
1496 }
1497