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