]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
New option parsing
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <pwd.h>
14 #include <grp.h>
15 #include <utime.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <inttypes.h>
23 #include <time.h>
24
25 #include <atalk/dsi.h>
26 #include <atalk/adouble.h>
27 #include <atalk/afp.h>
28 #include <atalk/util.h>
29 #include <atalk/volinfo.h>
30 #include <atalk/logger.h>
31 #include <atalk/vfs.h>
32 #include <atalk/uuid.h>
33 #include <atalk/ea.h>
34 #include <atalk/bstrlib.h>
35 #include <atalk/bstradd.h>
36 #include <atalk/ftw.h>
37 #include <atalk/globals.h>
38 #include <atalk/fce_api.h>
39 #include <atalk/errchk.h>
40
41 #ifdef CNID_DB
42 #include <atalk/cnid.h>
43 #endif /* CNID_DB*/
44
45 #include "directory.h"
46 #include "file.h"
47 #include "volume.h"
48 #include "unix.h"
49 #include "mangle.h"
50 #include "fork.h"
51 #include "hash.h"
52 #include "acls.h"
53
54 extern int afprun(int root, char *cmd, int *outfd);
55
56 #ifndef MIN
57 #define MIN(a, b) ((a) < (b) ? (a) : (b))
58 #endif /* ! MIN */
59
60 #ifndef UUID_PRINTABLE_STRING_LENGTH
61 #define UUID_PRINTABLE_STRING_LENGTH 37
62 #endif
63
64 /* Globals */
65 struct vol *current_vol;        /* last volume from getvolbyvid() */
66
67 static struct vol *Volumes = NULL;
68 static uint16_t    lastvid = 0;
69 static char     *Trash = "\02\024Network Trash Folder";
70
71 static struct extmap    *Extmap = NULL, *Defextmap = NULL;
72 static int              Extmap_cnt;
73 static void             free_extmap(void);
74
75 #define VOLOPT_ALLOW      0  /* user allow list */
76 #define VOLOPT_DENY       1  /* user deny list */
77 #define VOLOPT_RWLIST     2  /* user rw list */
78 #define VOLOPT_ROLIST     3  /* user ro list */
79 #define VOLOPT_PASSWORD   4  /* volume password */
80 #define VOLOPT_CASEFOLD   5  /* character case mangling */
81 #define VOLOPT_FLAGS      6  /* various flags */
82 #define VOLOPT_DBPATH     7  /* path to database */
83 #define VOLOPT_LIMITSIZE  8  /* Limit the size of the volume */
84 /* Usable slot: 9 */
85 #define VOLOPT_VETO          10  /* list of veto filespec */
86 #define VOLOPT_PREEXEC       11  /* preexec command */
87 #define VOLOPT_ROOTPREEXEC   12  /* root preexec command */
88 #define VOLOPT_POSTEXEC      13  /* postexec command */
89 #define VOLOPT_ROOTPOSTEXEC  14  /* root postexec command */
90 #define VOLOPT_ENCODING      15  /* mac encoding (pre OSX)*/
91 #define VOLOPT_MACCHARSET    16
92 #define VOLOPT_CNIDSCHEME    17
93 #define VOLOPT_ADOUBLE       18  /* adouble version */
94 /* Usable slot: 19/20 */
95 #define VOLOPT_UMASK         21
96 #define VOLOPT_ALLOWED_HOSTS 22
97 #define VOLOPT_DENIED_HOSTS  23
98 #define VOLOPT_DPERM         24  /* dperm default directories perms */
99 #define VOLOPT_FPERM         25  /* fperm default files perms */
100 #define VOLOPT_DFLTPERM      26  /* perm */
101 #define VOLOPT_EA_VFS        27  /* Extended Attributes vfs indirection */
102 #define VOLOPT_CNIDSERVER    28  /* CNID Server ip address*/
103 #define VOLOPT_CNIDPORT      30  /* CNID server tcp port */
104
105 #define VOLOPT_MAX           31  /* <== IMPORTANT !!!!!! */
106 #define VOLOPT_NUM           (VOLOPT_MAX + 1)
107
108 #define VOLPASSLEN  8
109 #define VOLOPT_DEFAULT     ":DEFAULT:"
110 #define VOLOPT_DEFAULT_LEN 9
111
112 struct vol_option {
113     char *c_value;
114     int i_value;
115 };
116
117 typedef struct _special_folder {
118     const char *name;
119     int precreate;
120     mode_t mode;
121     int hide;
122 } _special_folder;
123
124 static const _special_folder special_folders[] = {
125     {"Network Trash Folder",     1,  0777,  1},
126     {".AppleDesktop",            1,  0777,  0},
127     {NULL, 0, 0, 0}};
128
129 /* Forward declarations */
130 static void handle_special_folders (const struct vol *);
131 static void deletevol(struct vol *vol);
132 static void volume_free(struct vol *vol);
133 static void check_ea_sys_support(struct vol *vol);
134 static char *get_vol_uuid(const AFPObj *obj, const char *volname);
135 static int readvolfile(AFPObj *obj, struct afp_volume_name *p1,char *p2, int user, struct passwd *pwent);
136
137 static void volfree(struct vol_option *options, const struct vol_option *save)
138 {
139     int i;
140
141     if (save) {
142         for (i = 0; i < VOLOPT_MAX; i++) {
143             if (options[i].c_value && (options[i].c_value != save[i].c_value))
144                 free(options[i].c_value);
145         }
146     } else {
147         for (i = 0; i < VOLOPT_MAX; i++) {
148             if (options[i].c_value)
149                 free(options[i].c_value);
150         }
151     }
152 }
153
154
155 #define is_var(a, b) (strncmp((a), (b), 2) == 0)
156
157 /*
158  * Handle variable substitutions. here's what we understand:
159  * $b   -> basename of path
160  * $c   -> client ip/appletalk address
161  * $d   -> volume pathname on server
162  * $f   -> full name (whatever's in the gecos field)
163  * $g   -> group
164  * $h   -> hostname
165  * $i   -> client ip/appletalk address without port
166  * $s   -> server name (hostname if it doesn't exist)
167  * $u   -> username (guest is usually nobody)
168  * $v   -> volume name or basename if null
169  * $z   -> zone (may not exist)
170  * $$   -> $
171  *
172  * This get's called from readvolfile with
173  * path = NULL, volname = NULL for xlating the volumes path
174  * path = path, volname = NULL for xlating the volumes name
175  * ... and from volumes options parsing code when xlating eg dbpath with
176  * path = path, volname = volname
177  *
178  * Using this information we can reject xlation of any variable depeninding on a login
179  * context which is not given in the afp master, where we must evaluate this whole stuff
180  * too for the Zeroconf announcements.
181  */
182 static char *volxlate(AFPObj *obj,
183                       char *dest,
184                       size_t destlen,
185                       char *src,
186                       struct passwd *pwd,
187                       char *path,
188                       char *volname)
189 {
190     char *p, *r;
191     const char *q;
192     int len;
193     char *ret;
194     int afpmaster = 0;
195     int xlatevolname = 0;
196
197     if (parent_or_child == 0)
198         afpmaster = 1;
199
200     if (path && !volname)
201         /* cf above */
202         xlatevolname = 1;
203
204     if (!src) {
205         return NULL;
206     }
207     if (!dest) {
208         dest = calloc(destlen +1, 1);
209     }
210     ret = dest;
211     if (!ret) {
212         return NULL;
213     }
214     strlcpy(dest, src, destlen +1);
215     if ((p = strchr(src, '$')) == NULL) /* nothing to do */
216         return ret;
217
218     /* first part of the path. just forward to the next variable. */
219     len = MIN((size_t)(p - src), destlen);
220     if (len > 0) {
221         destlen -= len;
222         dest += len;
223     }
224
225     while (p && destlen > 0) {
226         /* now figure out what the variable is */
227         q = NULL;
228         if (is_var(p, "$b")) {
229             if (afpmaster && xlatevolname)
230                 return NULL;
231             if (path) {
232                 if ((q = strrchr(path, '/')) == NULL)
233                     q = path;
234                 else if (*(q + 1) != '\0')
235                     q++;
236             }
237         } else if (is_var(p, "$c")) {
238             if (afpmaster && xlatevolname)
239                 return NULL;
240             DSI *dsi = obj->dsi;
241             len = sprintf(dest, "%s:%u",
242                           getip_string((struct sockaddr *)&dsi->client),
243                           getip_port((struct sockaddr *)&dsi->client));
244             dest += len;
245             destlen -= len;
246         } else if (is_var(p, "$d")) {
247             if (afpmaster && xlatevolname)
248                 return NULL;
249             q = path;
250         } else if (pwd && is_var(p, "$f")) {
251             if (afpmaster && xlatevolname)
252                 return NULL;
253             if ((r = strchr(pwd->pw_gecos, ',')))
254                 *r = '\0';
255             q = pwd->pw_gecos;
256         } else if (pwd && is_var(p, "$g")) {
257             if (afpmaster && xlatevolname)
258                 return NULL;
259             struct group *grp = getgrgid(pwd->pw_gid);
260             if (grp)
261                 q = grp->gr_name;
262         } else if (is_var(p, "$h")) {
263             q = obj->options.hostname;
264         } else if (is_var(p, "$i")) {
265             if (afpmaster && xlatevolname)
266                 return NULL;
267             DSI *dsi = obj->dsi;
268             q = getip_string((struct sockaddr *)&dsi->client);
269         } else if (is_var(p, "$s")) {
270             if (obj->Obj)
271                 q = obj->Obj;
272             else if (obj->options.server) {
273                 q = obj->options.server;
274             } else
275                 q = obj->options.hostname;
276         } else if (obj->username && is_var(p, "$u")) {
277             if (afpmaster && xlatevolname)
278                 return NULL;
279             char* sep = NULL;
280             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
281                 q = sep+1;
282             else
283                 q = obj->username;
284         } else if (is_var(p, "$v")) {
285             if (afpmaster && xlatevolname)
286                 return NULL;
287             if (volname) {
288                 q = volname;
289             }
290             else if (path) {
291                 if ((q = strrchr(path, '/')) == NULL)
292                     q = path;
293                 else if (*(q + 1) != '\0')
294                     q++;
295             }
296         } else if (is_var(p, "$z")) {
297             q = obj->Zone;
298         } else if (is_var(p, "$$")) {
299             q = "$";
300         } else
301             q = p;
302
303         /* copy the stuff over. if we don't understand something that we
304          * should, just skip it over. */
305         if (q) {
306             len = MIN(p == q ? 2 : strlen(q), destlen);
307             strncpy(dest, q, len);
308             dest += len;
309             destlen -= len;
310         }
311
312         /* stuff up to next $ */
313         src = p + 2;
314         p = strchr(src, '$');
315         len = p ? MIN((size_t)(p - src), destlen) : destlen;
316         if (len > 0) {
317             strncpy(dest, src, len);
318             dest += len;
319             destlen -= len;
320         }
321     }
322     return ret;
323 }
324
325 /* to make sure that val is valid, make sure to select an opt that
326    includes val */
327 static int optionok(const char *buf, const char *opt, const char *val)
328 {
329     if (!strstr(buf,opt))
330         return 0;
331     if (!val[1])
332         return 0;
333     return 1;
334 }
335
336
337 /* -------------------- */
338 static void setoption(struct vol_option *options, struct vol_option *save, int opt, const char *val)
339 {
340     if (options[opt].c_value && (!save || options[opt].c_value != save[opt].c_value))
341         free(options[opt].c_value);
342     options[opt].c_value = strdup(val + 1);
343 }
344
345 /* ------------------------------------------
346    handle all the options. tmp can't be NULL. */
347 static void volset(struct vol_option *options, struct vol_option *save,
348                    char *volname, int vlen,
349                    const char *tmp)
350 {
351     char *val;
352
353     val = strchr(tmp, ':');
354     if (!val) {
355         /* we'll assume it's a volume name. */
356         strncpy(volname, tmp, vlen);
357         volname[vlen] = 0;
358         return;
359     }
360 #if 0
361     LOG(log_debug, logtype_afpd, "Parsing volset %s", val);
362 #endif
363     if (optionok(tmp, "allow:", val)) {
364         setoption(options, save, VOLOPT_ALLOW, val);
365
366     } else if (optionok(tmp, "deny:", val)) {
367         setoption(options, save, VOLOPT_DENY, val);
368
369     } else if (optionok(tmp, "rwlist:", val)) {
370         setoption(options, save, VOLOPT_RWLIST, val);
371
372     } else if (optionok(tmp, "rolist:", val)) {
373         setoption(options, save, VOLOPT_ROLIST, val);
374
375     } else if (optionok(tmp, "codepage:", val)) {
376         LOG (log_error, logtype_afpd, "The old codepage system has been removed. Please make sure to read the documentation !!!!");
377         /* Make sure we don't screw anything */
378         exit (EXITERR_CONF);
379     } else if (optionok(tmp, "volcharset:", val)) {
380         setoption(options, save, VOLOPT_ENCODING, val);
381     } else if (optionok(tmp, "maccharset:", val)) {
382         setoption(options, save, VOLOPT_MACCHARSET, val);
383     } else if (optionok(tmp, "veto:", val)) {
384         setoption(options, save, VOLOPT_VETO, val);
385     } else if (optionok(tmp, "cnidscheme:", val)) {
386         setoption(options, save, VOLOPT_CNIDSCHEME, val);
387     } else if (optionok(tmp, "casefold:", val)) {
388         if (strcasecmp(val + 1, "tolower") == 0)
389             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
390         else if (strcasecmp(val + 1, "toupper") == 0)
391             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
392         else if (strcasecmp(val + 1, "xlatelower") == 0)
393             options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
394         else if (strcasecmp(val + 1, "xlateupper") == 0)
395             options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
396     } else if (optionok(tmp, "adouble:", val)) {
397         if (strcasecmp(val + 1, "v2") == 0)
398             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
399         else if (strcasecmp(val + 1, "ea") == 0)
400             options[VOLOPT_ADOUBLE].i_value = AD_VERSION_EA;
401     } else if (optionok(tmp, "options:", val)) {
402         char *p;
403
404         if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
405             return;
406
407         while (p) {
408             if (strcasecmp(p, "ro") == 0)
409                 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
410             else if (strcasecmp(p, "nohex") == 0)
411                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
412             else if (strcasecmp(p, "usedots") == 0)
413                 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
414             else if (strcasecmp(p, "invisibledots") == 0)
415                 options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS | AFPVOL_INV_DOTS;
416             else if (strcasecmp(p, "nostat") == 0)
417                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOSTAT;
418             else if (strcasecmp(p, "preexec_close") == 0)
419                 options[VOLOPT_PREEXEC].i_value = 1;
420             else if (strcasecmp(p, "root_preexec_close") == 0)
421                 options[VOLOPT_ROOTPREEXEC].i_value = 1;
422             else if (strcasecmp(p, "upriv") == 0)
423                 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
424             else if (strcasecmp(p, "nodev") == 0)
425                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
426             else if (strcasecmp(p, "caseinsensitive") == 0)
427                 options[VOLOPT_FLAGS].i_value |= AFPVOL_CASEINSEN;
428             else if (strcasecmp(p, "illegalseq") == 0)
429                 options[VOLOPT_FLAGS].i_value |= AFPVOL_EILSEQ;
430             else if (strcasecmp(p, "tm") == 0)
431                 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
432             else if (strcasecmp(p, "searchdb") == 0)
433                 options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
434             else if (strcasecmp(p, "nonetids") == 0)
435                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
436             else if (strcasecmp(p, "noacls") == 0)
437                 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
438             else if (strcasecmp(p, "nov2toeaconv") == 0)
439                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NOV2TOEACONV;
440             p = strtok(NULL, ",");
441         }
442
443     } else if (optionok(tmp, "cnidserver:", val)) {
444         setoption(options, save, VOLOPT_CNIDSERVER, val);
445
446         char *p = strrchr(val + 1, ':');
447         if (p) {
448             *p = 0;
449             setoption(options, save, VOLOPT_CNIDPORT, p);
450         }
451
452         LOG(log_debug, logtype_afpd, "CNID Server for volume '%s': %s:%s",
453             volname, val + 1, p ? p + 1 : Cnid_port);
454
455     } else if (optionok(tmp, "dbpath:", val)) {
456         setoption(options, save, VOLOPT_DBPATH, val);
457
458     } else if (optionok(tmp, "umask:", val)) {
459         options[VOLOPT_UMASK].i_value = (int)strtol(val +1, NULL, 8);
460     } else if (optionok(tmp, "dperm:", val)) {
461         options[VOLOPT_DPERM].i_value = (int)strtol(val+1, NULL, 8);
462     } else if (optionok(tmp, "fperm:", val)) {
463         options[VOLOPT_FPERM].i_value = (int)strtol(val+1, NULL, 8);
464     } else if (optionok(tmp, "perm:", val)) {
465         options[VOLOPT_DFLTPERM].i_value = (int)strtol(val+1, NULL, 8);
466     } else if (optionok(tmp, "password:", val)) {
467         setoption(options, save, VOLOPT_PASSWORD, val);
468     } else if (optionok(tmp, "root_preexec:", val)) {
469         setoption(options, save, VOLOPT_ROOTPREEXEC, val);
470
471     } else if (optionok(tmp, "preexec:", val)) {
472         setoption(options, save, VOLOPT_PREEXEC, val);
473
474     } else if (optionok(tmp, "root_postexec:", val)) {
475         setoption(options, save, VOLOPT_ROOTPOSTEXEC, val);
476
477     } else if (optionok(tmp, "postexec:", val)) {
478         setoption(options, save, VOLOPT_POSTEXEC, val);
479
480     } else if (optionok(tmp, "allowed_hosts:", val)) {
481         setoption(options, save, VOLOPT_ALLOWED_HOSTS, val);
482
483     } else if (optionok(tmp, "denied_hosts:", val)) {
484         setoption(options, save, VOLOPT_DENIED_HOSTS, val);
485
486     } else if (optionok(tmp, "ea:", val)) {
487         if (strcasecmp(val + 1, "ad") == 0)
488             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
489         else if (strcasecmp(val + 1, "sys") == 0)
490             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SYS;
491         else if (strcasecmp(val + 1, "none") == 0)
492             options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_NONE;
493
494     } else if (optionok(tmp, "volsizelimit:", val)) {
495         options[VOLOPT_LIMITSIZE].i_value = (uint32_t)strtoul(val + 1, NULL, 10);
496
497     } else {
498         /* ignore unknown options */
499         LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
500
501     }
502 }
503
504 /* ----------------- */
505 static void showvol(const ucs2_t *name)
506 {
507     struct vol  *volume;
508     for ( volume = Volumes; volume; volume = volume->v_next ) {
509         if (volume->v_hide && !strcasecmp_w( volume->v_name, name ) ) {
510             volume->v_hide = 0;
511             return;
512         }
513     }
514 }
515
516 /* ------------------------------- */
517 static int creatvol(AFPObj *obj, struct passwd *pwd,
518                     char *path, char *name,
519                     struct vol_option *options,
520                     const int user /* user defined volume */
521     )
522 {
523     struct vol  *volume;
524     int         suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
525     int         hide = 0;
526     char        tmpname[AFPVOL_U8MNAMELEN+1];
527     ucs2_t      u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
528     char        suffix[6]; /* max is #FFFF */
529     uint16_t   flags;
530
531     LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
532
533     if ( name == NULL || *name == '\0' ) {
534         if ((name = strrchr( path, '/' )) == NULL) {
535             return -1;  /* Obviously not a fully qualified path */
536         }
537
538         /* if you wish to share /, you need to specify a name. */
539         if (*++name == '\0')
540             return -1;
541     }
542
543     /* suffix for mangling use (lastvid + 1)   */
544     /* because v_vid has not been decided yet. */
545     suffixlen = sprintf(suffix, "%c%X", MANGLE_CHAR, lastvid + 1 );
546
547     vlen = strlen( name );
548
549     /* Unicode Volume Name */
550     /* Firstly convert name from unixcharset to UTF8-MAC */
551     flags = CONV_IGNORE;
552     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
553     if (tmpvlen <= 0) {
554         strcpy(tmpname, "???");
555         tmpvlen = 3;
556     }
557
558     /* Do we have to mangle ? */
559     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
560         if (tmpvlen + suffixlen > obj->options.volnamelen) {
561             flags = CONV_FORCE;
562             tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
563             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
564         }
565         strcat(tmpname, suffix);
566         tmpvlen = strlen(tmpname);
567     }
568
569     /* Secondly convert name from UTF8-MAC to UCS2 */
570     if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
571         return -1;
572
573     LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
574
575     /* Maccharset Volume Name */
576     /* Firsty convert name from unixcharset to maccharset */
577     flags = CONV_IGNORE;
578     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
579     if (tmpvlen <= 0) {
580         strcpy(tmpname, "???");
581         tmpvlen = 3;
582     }
583
584     /* Do we have to mangle ? */
585     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
586         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
587             flags = CONV_FORCE;
588             tmpvlen = convert_charset(obj->options.unixcharset,
589                                       obj->options.maccharset,
590                                       0,
591                                       name,
592                                       vlen,
593                                       tmpname,
594                                       AFPVOL_MACNAMELEN - suffixlen,
595                                       &flags);
596             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
597         }
598         strcat(tmpname, suffix);
599         tmpvlen = strlen(tmpname);
600     }
601
602     /* Secondly convert name from maccharset to UCS2 */
603     if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
604                                          CH_UCS2,
605                                          tmpname,
606                                          tmpvlen,
607                                          mactmpname,
608                                          AFPVOL_U8MNAMELEN*2)) )
609         return -1;
610
611     LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
612
613     /* check duplicate */
614     for ( volume = Volumes; volume; volume = volume->v_next ) {
615         if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
616              ||
617             (!utf8_encoding() && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) {
618             LOG (log_error, logtype_afpd,
619                  "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"",
620                  volume->v_localname, name);
621             if (volume->v_deleted) {
622                 volume->v_new = hide = 1;
623             }
624             else {
625                 return -1;  /* Won't be able to access it, anyway... */
626             }
627         }
628     }
629
630     if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
631         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
632         return -1;
633     }
634     if ( NULL == ( volume->v_localname = strdup(name))) {
635         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
636         free(volume);
637         return -1;
638     }
639
640     if ( NULL == ( volume->v_u8mname = strdup_w(u8mtmpname))) {
641         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
642         volume_free(volume);
643         free(volume);
644         return -1;
645     }
646     if ( NULL == ( volume->v_macname = strdup_w(mactmpname))) {
647         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
648         volume_free(volume);
649         free(volume);
650         return -1;
651     }
652     if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
653         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
654         volume_free(volume);
655         free(volume);
656         return -1;
657     }
658
659     volume->v_name = utf8_encoding()?volume->v_u8mname:volume->v_macname;
660     volume->v_hide = hide;
661     strcpy( volume->v_path, path );
662
663 #ifdef __svr4__
664     volume->v_qfd = -1;
665 #endif /* __svr4__ */
666     /* os X start at 1 and use network order ie. 1 2 3 */
667     volume->v_vid = ++lastvid;
668     volume->v_vid = htons(volume->v_vid);
669 #ifdef HAVE_ACLS
670     if (!check_vol_acl_support(volume)) {
671         LOG(log_debug, logtype_afpd, "creatvol(\"%s\"): disabling ACL support", volume->v_path);
672         options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
673     }
674 #endif
675
676     /* handle options */
677     if (options) {
678         volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
679         volume->v_flags |= options[VOLOPT_FLAGS].i_value;
680
681         if (options[VOLOPT_EA_VFS].i_value)
682             volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
683
684         volume->v_ad_options = 0;
685         if ((volume->v_flags & AFPVOL_NODEV))
686             volume->v_ad_options |= ADVOL_NODEV;
687         if ((volume->v_flags & AFPVOL_UNIX_PRIV))
688             volume->v_ad_options |= ADVOL_UNIXPRIV;
689         if ((volume->v_flags & AFPVOL_INV_DOTS))
690             volume->v_ad_options |= ADVOL_INVDOTS;
691
692         if (options[VOLOPT_PASSWORD].c_value)
693             volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
694
695         if (options[VOLOPT_VETO].c_value)
696             volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
697
698         if (options[VOLOPT_ENCODING].c_value)
699             volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
700
701         if (options[VOLOPT_MACCHARSET].c_value)
702             volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
703
704         if (options[VOLOPT_DBPATH].c_value)
705             volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path, name);
706
707         if (options[VOLOPT_CNIDSCHEME].c_value)
708             volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
709
710         if (options[VOLOPT_CNIDSERVER].c_value)
711             volume->v_cnidserver = strdup(options[VOLOPT_CNIDSERVER].c_value);
712
713         if (options[VOLOPT_CNIDPORT].c_value)
714             volume->v_cnidport = strdup(options[VOLOPT_CNIDPORT].c_value);
715
716         if (options[VOLOPT_UMASK].i_value)
717             volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
718
719         if (options[VOLOPT_DPERM].i_value)
720             volume->v_dperm = (mode_t)options[VOLOPT_DPERM].i_value;
721
722         if (options[VOLOPT_FPERM].i_value)
723             volume->v_fperm = (mode_t)options[VOLOPT_FPERM].i_value;
724
725         if (options[VOLOPT_DFLTPERM].i_value)
726             volume->v_perm = (mode_t)options[VOLOPT_DFLTPERM].i_value;
727
728         if (options[VOLOPT_ADOUBLE].i_value)
729             volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
730         else
731             volume->v_adouble = AD_VERSION;
732
733         if (options[VOLOPT_LIMITSIZE].i_value)
734             volume->v_limitsize = options[VOLOPT_LIMITSIZE].i_value;
735
736         /* Mac to Unix conversion flags*/
737         volume->v_mtou_flags = 0;
738         if (!(volume->v_flags & AFPVOL_NOHEX))
739             volume->v_mtou_flags |= CONV_ESCAPEHEX;
740         if (!(volume->v_flags & AFPVOL_USEDOTS))
741             volume->v_mtou_flags |= CONV_ESCAPEDOTS;
742         if ((volume->v_flags & AFPVOL_EILSEQ))
743             volume->v_mtou_flags |= CONV__EILSEQ;
744
745         if ((volume->v_casefold & AFPVOL_MTOUUPPER))
746             volume->v_mtou_flags |= CONV_TOUPPER;
747         else if ((volume->v_casefold & AFPVOL_MTOULOWER))
748             volume->v_mtou_flags |= CONV_TOLOWER;
749
750         /* Unix to Mac conversion flags*/
751         volume->v_utom_flags = CONV_IGNORE | CONV_UNESCAPEHEX;
752         if ((volume->v_casefold & AFPVOL_UTOMUPPER))
753             volume->v_utom_flags |= CONV_TOUPPER;
754         else if ((volume->v_casefold & AFPVOL_UTOMLOWER))
755             volume->v_utom_flags |= CONV_TOLOWER;
756
757         if ((volume->v_flags & AFPVOL_EILSEQ))
758             volume->v_utom_flags |= CONV__EILSEQ;
759
760         if (!user) {
761             if (options[VOLOPT_PREEXEC].c_value)
762                 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path, name);
763             volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value;
764
765             if (options[VOLOPT_POSTEXEC].c_value)
766                 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path, name);
767
768             if (options[VOLOPT_ROOTPREEXEC].c_value)
769                 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path,  name);
770             volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value;
771
772             if (options[VOLOPT_ROOTPOSTEXEC].c_value)
773                 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path,  name);
774         }
775     }
776     volume->v_dperm |= volume->v_perm;
777     volume->v_fperm |= volume->v_perm;
778
779     /* Check EA support on volume */
780     if (volume->v_vfs_ea == AFPVOL_EA_AUTO)
781         check_ea_sys_support(volume);
782     initvol_vfs(volume);
783
784     /* get/store uuid from file in afpd master*/
785     if ((parent_or_child == 0) && (volume->v_flags & AFPVOL_TM)) {
786         char *uuid = get_vol_uuid(obj, volume->v_localname);
787         if (!uuid) {
788             LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
789                 volume->v_localname);
790         } else {
791             volume->v_uuid = uuid;
792             LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
793                 volume->v_localname, volume->v_uuid);
794         }
795     }
796
797     volume->v_next = Volumes;
798     Volumes = volume;
799     return 0;
800 }
801
802 /* ---------------- */
803 static char *myfgets( char *buf, int size, FILE *fp)
804 {
805     char    *p;
806     int     c;
807
808     p = buf;
809     while ((EOF != ( c = getc( fp )) ) && ( size > 1 )) {
810         if ( c == '\n' || c == '\r' ) {
811             if (p != buf && *(p -1) == '\\') {
812                 p--;
813                 size++;
814                 continue;
815             }
816             *p++ = '\n';
817             break;
818         } else {
819             *p++ = c;
820         }
821         size--;
822     }
823
824     if ( p == buf ) {
825         return( NULL );
826     } else {
827         *p = '\0';
828         return( buf );
829     }
830 }
831
832
833 /* check access list. this function wants something of the following
834  * form:
835  *        @group,name,name2,@group2,name3
836  *
837  * a NULL argument allows everybody to have access.
838  * we return three things:
839  *     -1: no list
840  *      0: list exists, but name isn't in it
841  *      1: in list
842  */
843
844 #ifndef NO_REAL_USER_NAME
845 /* authentication is case insensitive
846  * FIXME should we do the same with group name?
847  */
848 #define access_strcmp strcasecmp
849
850 #else
851 #define access_strcmp strcmp
852
853 #endif
854
855 static int accessvol(const char *args, const char *name)
856 {
857     char buf[MAXPATHLEN + 1], *p;
858     struct group *gr;
859
860     if (!args)
861         return -1;
862
863     strlcpy(buf, args, sizeof(buf));
864     if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
865         return -1;
866
867     while (p) {
868         if (*p == '@') { /* it's a group */
869             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
870                 return 1;
871         } else if (access_strcmp(p, name) == 0) /* it's a user name */
872             return 1;
873         p = strtok(NULL, ",");
874     }
875
876     return 0;
877 }
878
879 static int hostaccessvol(int type, const char *volname, const char *args, const AFPObj *obj)
880 {
881     int mask_int;
882     char buf[MAXPATHLEN + 1], *p, *b;
883     DSI *dsi = obj->dsi;
884     struct sockaddr_storage client;
885
886     if (!args)
887         return -1;
888
889     strlcpy(buf, args, sizeof(buf));
890     if ((p = strtok_r(buf, ",", &b)) == NULL) /* nothing, return okay */
891         return -1;
892
893     if (obj->proto != AFPPROTO_DSI)
894         return -1;
895
896     while (p) {
897         int ret;
898         char *ipaddr, *mask_char;
899         struct addrinfo hints, *ai;
900
901         ipaddr = strtok(p, "/");
902         mask_char = strtok(NULL,"/");
903
904         /* Get address from string with getaddrinfo */
905         memset(&hints, 0, sizeof hints);
906         hints.ai_family = AF_UNSPEC;
907         hints.ai_socktype = SOCK_STREAM;
908         if ((ret = getaddrinfo(ipaddr, NULL, &hints, &ai)) != 0) {
909             LOG(log_error, logtype_afpd, "hostaccessvol: getaddrinfo: %s\n", gai_strerror(ret));
910             continue;
911         }
912
913         /* netmask */
914         if (mask_char != NULL)
915             mask_int = atoi(mask_char); /* apply_ip_mask does range checking on it */
916         else {
917             if (ai->ai_family == AF_INET) /* IPv4 */
918                 mask_int = 32;
919             else                          /* IPv6 */
920                 mask_int = 128;
921         }
922
923         /* Apply mask to addresses */
924         client = dsi->client;
925         apply_ip_mask((struct sockaddr *)&client, mask_int);
926         apply_ip_mask(ai->ai_addr, mask_int);
927
928         if (compare_ip((struct sockaddr *)&client, ai->ai_addr) == 0) {
929             if (type == VOLOPT_DENIED_HOSTS)
930                 LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s' by denied list",
931                     getip_string((struct sockaddr *)&client), volname);
932             freeaddrinfo(ai);
933             return 1;
934         }
935
936         /* next address */
937         freeaddrinfo(ai);
938         p = strtok_r(NULL, ",", &b);
939     }
940
941     if (type == VOLOPT_ALLOWED_HOSTS)
942         LOG(log_info, logtype_afpd, "AFP access denied for client IP '%s' to volume '%s', not in allowed list",
943             getip_string((struct sockaddr *)&dsi->client), volname);
944     return 0;
945 }
946
947 static void setextmap(char *ext, char *type, char *creator, int user)
948 {
949     struct extmap   *em;
950     int                 cnt;
951
952     if (Extmap == NULL) {
953         if (( Extmap = calloc(1, sizeof( struct extmap ))) == NULL ) {
954             LOG(log_error, logtype_afpd, "setextmap: calloc: %s", strerror(errno) );
955             return;
956         }
957     }
958     ext++;
959     for ( em = Extmap, cnt = 0; em->em_ext; em++, cnt++) {
960         if ( (strdiacasecmp( em->em_ext, ext )) == 0 ) {
961             break;
962         }
963     }
964
965     if ( em->em_ext == NULL ) {
966         if (!(Extmap  = realloc( Extmap, sizeof( struct extmap ) * (cnt +2))) ) {
967             LOG(log_error, logtype_afpd, "setextmap: realloc: %s", strerror(errno) );
968             return;
969         }
970         (Extmap +cnt +1)->em_ext = NULL;
971         em = Extmap +cnt;
972     } else if ( !user ) {
973         return;
974     }
975     if (em->em_ext)
976         free(em->em_ext);
977
978     if (!(em->em_ext = strdup(  ext))) {
979         LOG(log_error, logtype_afpd, "setextmap: strdup: %s", strerror(errno) );
980         return;
981     }
982
983     if ( *type == '\0' ) {
984         memcpy(em->em_type, "\0\0\0\0", sizeof( em->em_type ));
985     } else {
986         memcpy(em->em_type, type, sizeof( em->em_type ));
987     }
988     if ( *creator == '\0' ) {
989         memcpy(em->em_creator, "\0\0\0\0", sizeof( em->em_creator ));
990     } else {
991         memcpy(em->em_creator, creator, sizeof( em->em_creator ));
992     }
993 }
994
995 /* -------------------------- */
996 static int extmap_cmp(const void *map1, const void *map2)
997 {
998     const struct extmap *em1 = map1;
999     const struct extmap *em2 = map2;
1000     return strdiacasecmp(em1->em_ext, em2->em_ext);
1001 }
1002
1003 static void sortextmap( void)
1004 {
1005     struct extmap   *em;
1006
1007     Extmap_cnt = 0;
1008     if ((em = Extmap) == NULL) {
1009         return;
1010     }
1011     while (em->em_ext) {
1012         em++;
1013         Extmap_cnt++;
1014     }
1015     if (Extmap_cnt) {
1016         qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp);
1017         if (*Extmap->em_ext == 0) {
1018             /* the first line is really "." the default entry,
1019              * we remove the leading '.' in setextmap
1020              */
1021             Defextmap = Extmap;
1022         }
1023     }
1024 }
1025
1026 /* ----------------------
1027  */
1028 static void free_extmap( void)
1029 {
1030     struct extmap   *em;
1031
1032     if (Extmap) {
1033         for ( em = Extmap; em->em_ext; em++) {
1034             free (em->em_ext);
1035         }
1036         free(Extmap);
1037         Extmap = NULL;
1038         Defextmap = Extmap;
1039         Extmap_cnt = 0;
1040     }
1041 }
1042
1043 /* ----------------------
1044  */
1045 static int volfile_changed(struct afp_volume_name *p)
1046 {
1047     struct stat      st;
1048     char *name;
1049
1050     if (p->full_name)
1051         name = p->full_name;
1052     else
1053         name = p->name;
1054
1055     if (!stat( name, &st) && st.st_mtime > p->mtime) {
1056         p->mtime = st.st_mtime;
1057         return 1;
1058     }
1059     return 0;
1060 }
1061
1062 /* ----------------------
1063  * Read a volume configuration file and add the volumes contained within to
1064  * the global volume list. This gets called from the forked afpd childs.
1065  * The master now reads this too for Zeroconf announcements.
1066  *
1067  * If p2 is non-NULL, the file that is opened is
1068  * p1/p2
1069  *
1070  * Lines that begin with # and blank lines are ignored.
1071  * Volume lines are of the form:
1072  *      <unix path> [<volume name>] [allow:<user>,<@group>,...] \
1073  *                           [codepage:<file>] [casefold:<num>]
1074  *      <extension> TYPE [CREATOR]
1075  *
1076  */
1077 static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
1078 {
1079     FILE        *fp;
1080     char        path[MAXPATHLEN + 1];
1081     char        tmp[MAXPATHLEN + 1];
1082     char        volname[AFPVOL_U8MNAMELEN + 1];
1083     char        buf[BUFSIZ];
1084     char        type[5], creator[5];
1085     char        *u, *p;
1086     int         fd;
1087     int         i;
1088     struct passwd   *pw;
1089     struct vol_option   save_options[VOLOPT_NUM];
1090     struct vol_option   default_options[VOLOPT_NUM];
1091     struct vol_option   options[VOLOPT_NUM];
1092     struct stat         st;
1093
1094     if (!p1->name)
1095         return -1;
1096     p1->mtime = 0;
1097     strcpy( path, p1->name );
1098     if ( p2 != NULL ) {
1099         strcat( path, "/" );
1100         strcat( path, p2 );
1101         if (p1->full_name) {
1102             free(p1->full_name);
1103         }
1104         p1->full_name = strdup(path);
1105     }
1106
1107     if (NULL == ( fp = fopen( path, "r" )) ) {
1108         return( -1 );
1109     }
1110     fd = fileno(fp);
1111     if (fd != -1 && !fstat( fd, &st) ) {
1112         p1->mtime = st.st_mtime;
1113     }
1114
1115     /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
1116     int retries = 2;
1117     while (1) {
1118         if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
1119             retries--;
1120             if (!retries) {
1121                 LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
1122                 if ( fclose( fp ) != 0 ) {
1123                     LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
1124                 }
1125                 return -1;
1126             }
1127             sleep(1);
1128             continue;
1129         }
1130         break;
1131     }
1132
1133     memset(default_options, 0, sizeof(default_options));
1134
1135     /* Enable some default options for all volumes */
1136 #ifdef HAVE_ACLS
1137     default_options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
1138 #endif
1139     default_options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AUTO;
1140     LOG(log_maxdebug, logtype_afpd, "readvolfile: seeding default umask: %04o",
1141         obj->options.umask);
1142     default_options[VOLOPT_UMASK].i_value = obj->options.umask;
1143     memcpy(save_options, default_options, sizeof(options));
1144
1145     LOG(log_debug, logtype_afpd, "readvolfile: \"%s\"", path);
1146
1147     while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
1148         initline( strlen( buf ), buf );
1149         parseline( sizeof( path ) - 1, path );
1150         switch ( *path ) {
1151         case '\0' :
1152         case '#' :
1153             continue;
1154
1155         case ':':
1156             /* change the default options for this file */
1157             if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
1158                 volfree(default_options, save_options);
1159                 memcpy(default_options, save_options, sizeof(options));
1160                 *tmp = '\0';
1161                 for (i = 0; i < VOLOPT_NUM; i++) {
1162                     if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1,
1163                                    path + VOLOPT_DEFAULT_LEN) < 0)
1164                         break;
1165                     volset(default_options, NULL, tmp, sizeof(tmp) - 1,
1166                            path + VOLOPT_DEFAULT_LEN);
1167                 }
1168             }
1169             break;
1170
1171         case '~' :
1172             if (( p = strchr( path, '/' )) != NULL ) {
1173                 *p++ = '\0';
1174             }
1175             u = path;
1176             u++;
1177             if ( *u == '\0' ) {
1178                 u = obj->username;
1179             }
1180             if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
1181                 continue;
1182             }
1183             strcpy( tmp, pw->pw_dir );
1184             if ( p != NULL && *p != '\0' ) {
1185                 strcat( tmp, "/" );
1186                 strcat( tmp, p );
1187             }
1188             /* fall through */
1189
1190         case '/' :
1191             /* send path through variable substitution */
1192             if (*path != '~') /* need to copy path to tmp */
1193                 strcpy(tmp, path);
1194             if (!pwent && obj->username)
1195                 pwent = getpwnam(obj->username);
1196
1197             if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
1198                 continue;
1199
1200             /* this is sort of braindead. basically, i want to be
1201              * able to specify things in any order, but i don't want to
1202              * re-write everything. */
1203
1204             memcpy(options, default_options, sizeof(options));
1205             *volname = '\0';
1206
1207             /* read in up to VOLOP_NUM possible options */
1208             for (i = 0; i < VOLOPT_NUM; i++) {
1209                 if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
1210                     break;
1211
1212                 volset(options, default_options, volname, sizeof(volname) - 1, tmp);
1213             }
1214
1215             /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
1216                allow -> either no list (-1), or in list (1)
1217                deny -> either no list (-1), or not in list (0) */
1218             if (parent_or_child == 0
1219                 ||
1220                 (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
1221                  (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
1222                  hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
1223                  (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1))) {
1224
1225                 /* handle read-only behaviour. semantics:
1226                  * 1) neither the rolist nor the rwlist exist -> rw
1227                  * 2) rolist exists -> ro if user is in it.
1228                  * 3) rwlist exists -> ro unless user is in it. */
1229                 if (parent_or_child == 1
1230                     &&
1231                     ((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0)
1232                     &&
1233                     ((accessvol(options[VOLOPT_ROLIST].c_value, obj->username) == 1) ||
1234                      !accessvol(options[VOLOPT_RWLIST].c_value, obj->username)))
1235                     options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
1236
1237                 /* do variable substitution for volname */
1238                 if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL)
1239                     continue;
1240
1241                 creatvol(obj, pwent, path, tmp, options, p2 != NULL);
1242             }
1243             volfree(options, default_options);
1244             break;
1245
1246         case '.' :
1247             parseline( sizeof( type ) - 1, type );
1248             parseline( sizeof( creator ) - 1, creator );
1249             setextmap( path, type, creator, user);
1250             break;
1251
1252         default :
1253             break;
1254         }
1255     }
1256     volfree(save_options, NULL);
1257     sortextmap();
1258     if ( fclose( fp ) != 0 ) {
1259         LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
1260     }
1261     p1->loaded = 1;
1262     return( 0 );
1263 }
1264
1265 /* ------------------------------- */
1266 static void volume_free(struct vol *vol)
1267 {
1268     free(vol->v_localname);
1269     vol->v_localname = NULL;
1270     free(vol->v_u8mname);
1271     vol->v_u8mname = NULL;
1272     free(vol->v_macname);
1273     vol->v_macname = NULL;
1274     free(vol->v_path);
1275     free(vol->v_password);
1276     free(vol->v_veto);
1277     free(vol->v_volcodepage);
1278     free(vol->v_maccodepage);
1279     free(vol->v_cnidscheme);
1280     free(vol->v_dbpath);
1281     free(vol->v_gvs);
1282     if (vol->v_uuid)
1283         free(vol->v_uuid);
1284 }
1285
1286 /* ------------------------------- */
1287 static void free_volumes(void )
1288 {
1289     struct vol  *vol;
1290     struct vol  *nvol, *ovol;
1291
1292     for ( vol = Volumes; vol; vol = vol->v_next ) {
1293         if (( vol->v_flags & AFPVOL_OPEN ) ) {
1294             vol->v_deleted = 1;
1295             continue;
1296         }
1297         volume_free(vol);
1298     }
1299
1300     for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1301         nvol = vol->v_next;
1302
1303         if (vol->v_localname == NULL) {
1304             if (Volumes == vol) {
1305                 Volumes = nvol;
1306                 ovol = Volumes;
1307             }
1308             else {
1309                 ovol->v_next = nvol;
1310             }
1311             free(vol);
1312         }
1313         else {
1314             ovol = vol;
1315         }
1316     }
1317 }
1318
1319 /* ------------------------------- */
1320 static void volume_unlink(struct vol *volume)
1321 {
1322     struct vol *vol, *ovol, *nvol;
1323
1324     if (volume == Volumes) {
1325         Volumes = Volumes->v_next;
1326         return;
1327     }
1328     for ( vol = Volumes->v_next, ovol = Volumes; vol; vol = nvol) {
1329         nvol = vol->v_next;
1330
1331         if (vol == volume) {
1332             ovol->v_next = nvol;
1333             break;
1334         }
1335         else {
1336             ovol = vol;
1337         }
1338     }
1339 }
1340 /*!
1341  * Read band-size info from Info.plist XML file of an TM sparsebundle
1342  *
1343  * @param path   (r) path to Info.plist file
1344  * @return           band-size in bytes, -1 on error
1345  */
1346 static long long int get_tm_bandsize(const char *path)
1347 {
1348     EC_INIT;
1349     FILE *file = NULL;
1350     char buf[512];
1351     long long int bandsize = -1;
1352
1353     EC_NULL_LOGSTR( file = fopen(path, "r"),
1354                     "get_tm_bandsize(\"%s\"): %s",
1355                     path, strerror(errno) );
1356
1357     while (fgets(buf, sizeof(buf), file) != NULL) {
1358         if (strstr(buf, "band-size") == NULL)
1359             continue;
1360
1361         if (fscanf(file, " <integer>%lld</integer>", &bandsize) != 1) {
1362             LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\"): can't parse band-size", path);
1363             EC_FAIL;
1364         }
1365         break;
1366     }
1367
1368 EC_CLEANUP:
1369     if (file)
1370         fclose(file);
1371     LOG(log_debug, logtype_afpd, "get_tm_bandsize(\"%s\"): bandsize: %lld", path, bandsize);
1372     return bandsize;
1373 }
1374
1375 /*!
1376  * Return number on entries in a directory
1377  *
1378  * @param path   (r) path to dir
1379  * @return           number of entries, -1 on error
1380  */
1381 static long long int get_tm_bands(const char *path)
1382 {
1383     EC_INIT;
1384     long long int count = 0;
1385     DIR *dir = NULL;
1386     const struct dirent *entry;
1387
1388     EC_NULL( dir = opendir(path) );
1389
1390     while ((entry = readdir(dir)) != NULL)
1391         count++;
1392     count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */
1393         
1394 EC_CLEANUP:
1395     if (dir)
1396         closedir(dir);
1397     if (ret != 0)
1398         return -1;
1399     return count;
1400 }
1401
1402 /*!
1403  * Calculate used size of a TimeMachine volume
1404  *
1405  * This assumes that the volume is used only for TimeMachine.
1406  *
1407  * 1) readdir(path of volume)
1408  * 2) for every element that matches regex "\(.*\)\.sparsebundle$" :
1409  * 3) parse "\1.sparsebundle/Info.plist" and read the band-size XML key integer value
1410  * 4) readdir "\1.sparsebundle/bands/" counting files
1411  * 5) calculate used size as: (file_count - 1) * band-size
1412  *
1413  * The result of the calculation is returned in "volume->v_tm_used".
1414  * "volume->v_appended" gets reset to 0.
1415  * "volume->v_tm_cachetime" is updated with the current time from time(NULL).
1416  *
1417  * "volume->v_tm_used" is cached for TM_USED_CACHETIME seconds and updated by
1418  * "volume->v_appended". The latter is increased by X every time the client
1419  * appends X bytes to a file (in fork.c).
1420  *
1421  * @param vol     (rw) volume to calculate
1422  * @return             0 on success, -1 on error
1423  */
1424 #define TM_USED_CACHETIME 60    /* cache for 60 seconds */
1425 static int get_tm_used(struct vol * restrict vol)
1426 {
1427     EC_INIT;
1428     long long int bandsize;
1429     VolSpace used = 0;
1430     bstring infoplist = NULL;
1431     bstring bandsdir = NULL;
1432     DIR *dir = NULL;
1433     const struct dirent *entry;
1434     const char *p;
1435     struct stat st;
1436     long int links;
1437     time_t now = time(NULL);
1438
1439     if (vol->v_tm_cachetime
1440         && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) {
1441         if (vol->v_tm_used == -1)
1442             EC_FAIL;
1443         vol->v_tm_used += vol->v_appended;
1444         vol->v_appended = 0;
1445         LOG(log_debug, logtype_afpd, "getused(\"%s\"): cached: %" PRIu64 " bytes",
1446             vol->v_path, vol->v_tm_used);
1447         return 0;
1448     }
1449
1450     vol->v_tm_cachetime = now;
1451
1452     EC_NULL( dir = opendir(vol->v_path) );
1453
1454     while ((entry = readdir(dir)) != NULL) {
1455         if (((p = strstr(entry->d_name, "sparsebundle")) != NULL)
1456             && (strlen(entry->d_name) == (p + strlen("sparsebundle") - entry->d_name))) {
1457
1458             EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") );
1459             
1460             if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1)
1461                 continue;
1462
1463             EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") );
1464
1465             if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1)
1466                 continue;
1467
1468             used += (links - 1) * bandsize;
1469             LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes",
1470                 cfrombstr(bandsdir), used);
1471         }
1472     }
1473
1474     vol->v_tm_used = used;
1475
1476 EC_CLEANUP:
1477     if (infoplist)
1478         bdestroy(infoplist);
1479     if (bandsdir)
1480         bdestroy(bandsdir);
1481     if (dir)
1482         closedir(dir);
1483
1484     LOG(log_debug, logtype_afpd, "getused(\"%s\"): %" PRIu64 " bytes", vol->v_path, vol->v_tm_used);
1485
1486     EC_EXIT;
1487 }
1488
1489 static int getvolspace(struct vol *vol,
1490                        uint32_t *bfree, uint32_t *btotal,
1491                        VolSpace *xbfree, VolSpace *xbtotal, uint32_t *bsize)
1492 {
1493     int         spaceflag, rc;
1494     uint32_t   maxsize;
1495     VolSpace    used;
1496 #ifndef NO_QUOTA_SUPPORT
1497     VolSpace    qfree, qtotal;
1498 #endif
1499
1500     spaceflag = AFPVOL_GVSMASK & vol->v_flags;
1501     /* report up to 2GB if afp version is < 2.2 (4GB if not) */
1502     maxsize = (afp_version < 22) ? 0x7fffffffL : 0xffffffffL;
1503
1504 #ifdef AFS
1505     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
1506         if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
1507             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
1508             goto getvolspace_done;
1509         }
1510     }
1511 #endif
1512
1513     if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) {
1514         return( rc );
1515     }
1516
1517 #ifndef NO_QUOTA_SUPPORT
1518     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
1519         if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
1520             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
1521             *xbfree = MIN(*xbfree, qfree);
1522             *xbtotal = MIN(*xbtotal, qtotal);
1523             goto getvolspace_done;
1524         }
1525     }
1526 #endif
1527     vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
1528
1529 getvolspace_done:
1530     if (vol->v_limitsize) {
1531         if (get_tm_used(vol) != 0)
1532             return AFPERR_MISC;
1533
1534         *xbtotal = MIN(*xbtotal, (vol->v_limitsize * 1024 * 1024));
1535         *xbfree = MIN(*xbfree, *xbtotal < vol->v_tm_used ? 0 : *xbtotal - vol->v_tm_used);
1536
1537         LOG(log_debug, logtype_afpd,
1538             "volparams: total: %" PRIu64 ", used: %" PRIu64 ", free: %" PRIu64 " bytes",
1539             *xbtotal, vol->v_tm_used, *xbfree);
1540     }
1541
1542     *bfree = MIN(*xbfree, maxsize);
1543     *btotal = MIN(*xbtotal, maxsize);
1544     return( AFP_OK );
1545 }
1546
1547 #define FCE_TM_DELTA 10  /* send notification every 10 seconds */
1548 void vol_fce_tm_event(void)
1549 {
1550     static time_t last;
1551     time_t now = time(NULL);
1552     struct vol  *vol = Volumes;
1553
1554     if ((last + FCE_TM_DELTA) < now) {
1555         last = now;
1556         for ( ; vol; vol = vol->v_next ) {
1557             if (vol->v_flags & AFPVOL_TM)
1558                 (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_appended);
1559         }
1560     }
1561 }
1562
1563 /* -----------------------
1564  * set volume creation date
1565  * avoid duplicate, well at least it tries
1566  */
1567 static void vol_setdate(uint16_t id, struct adouble *adp, time_t date)
1568 {
1569     struct vol  *volume;
1570     struct vol  *vol = Volumes;
1571
1572     for ( volume = Volumes; volume; volume = volume->v_next ) {
1573         if (volume->v_vid == id) {
1574             vol = volume;
1575         }
1576         else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) {
1577             date = date+1;
1578             volume = Volumes; /* restart */
1579         }
1580     }
1581     vol->v_ctime = AD_DATE_FROM_UNIX(date);
1582     ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date);
1583 }
1584
1585 /* ----------------------- */
1586 static int getvolparams( uint16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen)
1587 {
1588     struct adouble  ad;
1589     int         bit = 0, isad = 1;
1590     uint32_t       aint;
1591     u_short     ashort;
1592     uint32_t       bfree, btotal, bsize;
1593     VolSpace            xbfree, xbtotal; /* extended bytes */
1594     char        *data, *nameoff = NULL;
1595     char                *slash;
1596
1597     LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname);
1598
1599     /* courtesy of jallison@whistle.com:
1600      * For MacOS8.x support we need to create the
1601      * .Parent file here if it doesn't exist. */
1602
1603     /* Convert adouble:v2 to adouble:ea on the fly */
1604     (void)ad_convert(vol->v_path, st, vol);
1605
1606     ad_init(&ad, vol);
1607     if (ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0 ) {
1608         isad = 0;
1609         vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1610
1611     } else if (ad_get_MD_flags( &ad ) & O_CREAT) {
1612         slash = strrchr( vol->v_path, '/' );
1613         if(slash)
1614             slash++;
1615         else
1616             slash = vol->v_path;
1617         if (ad_getentryoff(&ad, ADEID_NAME)) {
1618             ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
1619             memcpy(ad_entry( &ad, ADEID_NAME ), slash,
1620                    ad_getentrylen( &ad, ADEID_NAME ));
1621         }
1622         vol_setdate(vol->v_vid, &ad, st->st_mtime);
1623         ad_flush(&ad);
1624     }
1625     else {
1626         if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)
1627             vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1628         else
1629             vol->v_ctime = aint;
1630     }
1631
1632     if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
1633                      (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
1634                      (1<<VOLPBIT_BSIZE)) ) != 0 ) {
1635         if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
1636                           &bsize) != AFP_OK ) {
1637             if ( isad ) {
1638                 ad_close( &ad, ADFLAGS_HF );
1639             }
1640             return( AFPERR_PARAM );
1641         }
1642     }
1643
1644     data = buf;
1645     while ( bitmap != 0 ) {
1646         while (( bitmap & 1 ) == 0 ) {
1647             bitmap = bitmap>>1;
1648             bit++;
1649         }
1650
1651         switch ( bit ) {
1652         case VOLPBIT_ATTR :
1653             ashort = 0;
1654             /* check for read-only.
1655              * NOTE: we don't actually set the read-only flag unless
1656              *       it's passed in that way as it's possible to mount
1657              *       a read-write filesystem under a read-only one. */
1658             if ((vol->v_flags & AFPVOL_RO) ||
1659                 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) {
1660                 ashort |= VOLPBIT_ATTR_RO;
1661             }
1662             /* prior 2.1 only VOLPBIT_ATTR_RO is defined */
1663             if (afp_version > 20) {
1664                 if (vol->v_cdb != NULL && (vol->v_cdb->flags & CNID_FLAG_PERSISTENT))
1665                     ashort |= VOLPBIT_ATTR_FILEID;
1666                 ashort |= VOLPBIT_ATTR_CATSEARCH;
1667
1668                 if (afp_version >= 30) {
1669                     ashort |= VOLPBIT_ATTR_UTF8;
1670                     if (vol->v_flags & AFPVOL_UNIX_PRIV)
1671                         ashort |= VOLPBIT_ATTR_UNIXPRIV;
1672                     if (vol->v_flags & AFPVOL_TM)
1673                         ashort |= VOLPBIT_ATTR_TM;
1674                     if (vol->v_flags & AFPVOL_NONETIDS)
1675                         ashort |= VOLPBIT_ATTR_NONETIDS;
1676                     if (afp_version >= 32) {
1677                         if (vol->v_vfs_ea)
1678                             ashort |= VOLPBIT_ATTR_EXT_ATTRS;
1679                         if (vol->v_flags & AFPVOL_ACLS)
1680                             ashort |= VOLPBIT_ATTR_ACLS;
1681                     }
1682                 }
1683             }
1684             ashort = htons(ashort);
1685             memcpy(data, &ashort, sizeof( ashort ));
1686             data += sizeof( ashort );
1687             break;
1688
1689         case VOLPBIT_SIG :
1690             ashort = htons( AFPVOLSIG_DEFAULT );
1691             memcpy(data, &ashort, sizeof( ashort ));
1692             data += sizeof( ashort );
1693             break;
1694
1695         case VOLPBIT_CDATE :
1696             aint = vol->v_ctime;
1697             memcpy(data, &aint, sizeof( aint ));
1698             data += sizeof( aint );
1699             break;
1700
1701         case VOLPBIT_MDATE :
1702             if ( st->st_mtime > vol->v_mtime ) {
1703                 vol->v_mtime = st->st_mtime;
1704             }
1705             aint = AD_DATE_FROM_UNIX(vol->v_mtime);
1706             memcpy(data, &aint, sizeof( aint ));
1707             data += sizeof( aint );
1708             break;
1709
1710         case VOLPBIT_BDATE :
1711             if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1712                 aint = AD_DATE_START;
1713             memcpy(data, &aint, sizeof( aint ));
1714             data += sizeof( aint );
1715             break;
1716
1717         case VOLPBIT_VID :
1718             memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
1719             data += sizeof( vol->v_vid );
1720             break;
1721
1722         case VOLPBIT_BFREE :
1723             bfree = htonl( bfree );
1724             memcpy(data, &bfree, sizeof( bfree ));
1725             data += sizeof( bfree );
1726             break;
1727
1728         case VOLPBIT_BTOTAL :
1729             btotal = htonl( btotal );
1730             memcpy(data, &btotal, sizeof( btotal ));
1731             data += sizeof( btotal );
1732             break;
1733
1734 #ifndef NO_LARGE_VOL_SUPPORT
1735         case VOLPBIT_XBFREE :
1736             xbfree = hton64( xbfree );
1737             memcpy(data, &xbfree, sizeof( xbfree ));
1738             data += sizeof( xbfree );
1739             break;
1740
1741         case VOLPBIT_XBTOTAL :
1742             xbtotal = hton64( xbtotal );
1743             memcpy(data, &xbtotal, sizeof( xbtotal ));
1744             data += sizeof( xbfree );
1745             break;
1746 #endif /* ! NO_LARGE_VOL_SUPPORT */
1747
1748         case VOLPBIT_NAME :
1749             nameoff = data;
1750             data += sizeof( uint16_t );
1751             break;
1752
1753         case VOLPBIT_BSIZE:  /* block size */
1754             bsize = htonl(bsize);
1755             memcpy(data, &bsize, sizeof(bsize));
1756             data += sizeof(bsize);
1757             break;
1758
1759         default :
1760             if ( isad ) {
1761                 ad_close( &ad, ADFLAGS_HF );
1762             }
1763             return( AFPERR_BITMAP );
1764         }
1765         bitmap = bitmap>>1;
1766         bit++;
1767     }
1768     if ( nameoff ) {
1769         ashort = htons( data - buf );
1770         memcpy(nameoff, &ashort, sizeof( ashort ));
1771         /* name is always in mac charset */
1772         aint = ucs2_to_charset( vol->v_maccharset, vol->v_macname, data+1, AFPVOL_MACNAMELEN + 1);
1773         if ( aint <= 0 ) {
1774             *buflen = 0;
1775             return AFPERR_MISC;
1776         }
1777
1778         *data++ = aint;
1779         data += aint;
1780     }
1781     if ( isad ) {
1782         ad_close(&ad, ADFLAGS_HF);
1783     }
1784     *buflen = data - buf;
1785     return( AFP_OK );
1786 }
1787
1788 /* ------------------------- */
1789 static int stat_vol(uint16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen)
1790 {
1791     struct stat st;
1792     int     ret;
1793     size_t  buflen;
1794
1795     if ( stat( vol->v_path, &st ) < 0 ) {
1796         *rbuflen = 0;
1797         return( AFPERR_PARAM );
1798     }
1799     /* save the volume device number */
1800     vol->v_dev = st.st_dev;
1801
1802     buflen = *rbuflen - sizeof( bitmap );
1803     if (( ret = getvolparams( bitmap, vol, &st,
1804                               rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
1805         *rbuflen = 0;
1806         return( ret );
1807     }
1808     *rbuflen = buflen + sizeof( bitmap );
1809     bitmap = htons( bitmap );
1810     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1811     return( AFP_OK );
1812
1813 }
1814
1815 /* ------------------------------- */
1816 void load_volumes(AFPObj *obj)
1817 {
1818     struct passwd   *pwent;
1819
1820     if (Volumes) {
1821         int changed = 0;
1822
1823         /* check files date */
1824         if (obj->options.defaultvol.loaded) {
1825             changed = volfile_changed(&obj->options.defaultvol);
1826         }
1827         if (obj->options.systemvol.loaded) {
1828             changed |= volfile_changed(&obj->options.systemvol);
1829         }
1830         if (obj->options.uservol.loaded) {
1831             changed |= volfile_changed(&obj->options.uservol);
1832         }
1833         if (!changed)
1834             return;
1835
1836         free_extmap();
1837         free_volumes();
1838     }
1839
1840     if (parent_or_child == 0) {
1841         LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
1842     } else {
1843         LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
1844     }
1845
1846     pwent = getpwnam(obj->username);
1847     if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
1848         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
1849     }
1850
1851     if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
1852         readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent);
1853     } else if (pwent) {
1854         /*
1855          * Read user's AppleVolumes or .AppleVolumes file
1856          * If neither are readable, read the default volumes file. if
1857          * that doesn't work, create a user share.
1858          */
1859         if (obj->options.uservol.name) {
1860             free(obj->options.uservol.name);
1861         }
1862         obj->options.uservol.name = strdup(pwent->pw_dir);
1863         if ( readvolfile(obj, &obj->options.uservol,    "AppleVolumes", 1, pwent) < 0 &&
1864              readvolfile(obj, &obj->options.uservol, ".AppleVolumes", 1, pwent) < 0 &&
1865              readvolfile(obj, &obj->options.uservol, "applevolumes", 1, pwent) < 0 &&
1866              readvolfile(obj, &obj->options.uservol, ".applevolumes", 1, pwent) < 0 &&
1867              obj->options.defaultvol.name != NULL ) {
1868             if (readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent) < 0)
1869                 creatvol(obj, pwent, pwent->pw_dir, NULL, NULL, 1);
1870         }
1871     }
1872     if ( obj->options.flags & OPTION_USERVOLFIRST ) {
1873         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent );
1874     }
1875
1876     if ( obj->options.closevol ) {
1877         struct vol *vol;
1878
1879         for ( vol = Volumes; vol; vol = vol->v_next ) {
1880             if (vol->v_deleted && !vol->v_new ) {
1881                 of_closevol(vol);
1882                 deletevol(vol);
1883                 vol = Volumes;
1884             }
1885         }
1886     }
1887 }
1888
1889 /* ------------------------------- */
1890 int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1891 {
1892     struct timeval  tv;
1893     struct stat     st;
1894     struct vol      *volume;
1895     char    *data;
1896     char        *namebuf;
1897     int         vcnt;
1898     size_t      len;
1899
1900     load_volumes(obj);
1901
1902     data = rbuf + 5;
1903     for ( vcnt = 0, volume = Volumes; volume; volume = volume->v_next ) {
1904         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
1905             struct maccess ma;
1906
1907             if ( stat( volume->v_path, &st ) < 0 ) {
1908                 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s",
1909                     volume->v_path, strerror(errno) );
1910                 continue;       /* can't access directory */
1911             }
1912             if (!S_ISDIR(st.st_mode)) {
1913                 continue;       /* not a dir */
1914             }
1915             accessmode(volume, volume->v_path, &ma, NULL, &st);
1916             if ((ma.ma_user & (AR_UREAD | AR_USEARCH)) != (AR_UREAD | AR_USEARCH)) {
1917                 continue;   /* no r-x access */
1918             }
1919         }
1920         if (volume->v_hide) {
1921             continue;       /* config file changed but the volume was mounted */
1922         }
1923
1924         if (utf8_encoding()) {
1925             len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname);
1926         } else {
1927             len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname);
1928         }
1929
1930         if (len == (size_t)-1)
1931             continue;
1932
1933         /* set password bit if there's a volume password */
1934         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
1935
1936         *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
1937         *data++ = len;
1938         memcpy(data, namebuf, len );
1939         data += len;
1940         free(namebuf);
1941         vcnt++;
1942     }
1943
1944     *rbuflen = data - rbuf;
1945     data = rbuf;
1946     if ( gettimeofday( &tv, NULL ) < 0 ) {
1947         LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
1948         *rbuflen = 0;
1949         return AFPERR_PARAM;
1950     }
1951     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
1952     memcpy(data, &tv.tv_sec, sizeof( uint32_t));
1953     data += sizeof( uint32_t);
1954     *data = vcnt;
1955     return( AFP_OK );
1956 }
1957
1958 /* ------------------------- */
1959 static int volume_codepage(AFPObj *obj, struct vol *volume)
1960 {
1961     struct charset_functions *charset;
1962     /* Codepages */
1963
1964     if (!volume->v_volcodepage)
1965         volume->v_volcodepage = strdup("UTF8");
1966
1967     if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) {
1968         LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage);
1969         return -1;
1970     }
1971
1972     if ( NULL == (charset = find_charset_functions(volume->v_volcodepage)) || charset->flags & CHARSET_ICONV ) {
1973         LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage);
1974     }
1975
1976     if (!volume->v_maccodepage)
1977         volume->v_maccodepage = strdup(obj->options.maccodepage);
1978
1979     if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) {
1980         LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage);
1981         return -1;
1982     }
1983
1984     if ( NULL == ( charset = find_charset_functions(volume->v_maccodepage)) || ! (charset->flags & CHARSET_CLIENT) ) {
1985         LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage);
1986         return -1;
1987     }
1988     volume->v_kTextEncoding = htonl(charset->kTextEncoding);
1989     return 0;
1990 }
1991
1992 /* ------------------------- */
1993 static int volume_openDB(struct vol *volume)
1994 {
1995     int flags = 0;
1996
1997     if ((volume->v_flags & AFPVOL_NODEV)) {
1998         flags |= CNID_FLAG_NODEV;
1999     }
2000
2001     if (volume->v_cnidscheme == NULL) {
2002         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
2003         LOG(log_info, logtype_afpd, "Volume %s use CNID scheme %s.",
2004             volume->v_path, volume->v_cnidscheme);
2005     }
2006
2007     LOG(log_info, logtype_afpd, "CNID server: %s:%s",
2008         volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
2009         volume->v_cnidport ? volume->v_cnidport : Cnid_port);
2010
2011 #if 0
2012 /* Found this in branch dir-rewrite, maybe we want to use it sometimes */
2013
2014     /* Legacy pre 2.1 way of sharing eg CD-ROM */
2015     if (strcmp(volume->v_cnidscheme, "last") == 0) {
2016         /* "last" is gone. We support it by switching to in-memory "tdb" */
2017         volume->v_cnidscheme = strdup("tdb");
2018         flags |= CNID_FLAG_MEMORY;
2019     }
2020
2021     /* New way of sharing CD-ROM */
2022     if (volume->v_flags & AFPVOL_CDROM) {
2023         flags |= CNID_FLAG_MEMORY;
2024         if (strcmp(volume->v_cnidscheme, "tdb") != 0) {
2025             free(volume->v_cnidscheme);
2026             volume->v_cnidscheme = strdup("tdb");
2027             LOG(log_info, logtype_afpd, "Volume %s is ejectable, switching to scheme %s.",
2028                 volume->v_path, volume->v_cnidscheme);
2029         }
2030     }
2031 #endif
2032
2033     volume->v_cdb = cnid_open(volume->v_path,
2034                               volume->v_umask,
2035                               volume->v_cnidscheme,
2036                               flags,
2037                               volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
2038                               volume->v_cnidport ? volume->v_cnidport : Cnid_port);
2039
2040     if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
2041         /* The first attempt failed and it wasn't yet an attempt to open in-memory */
2042         LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ",
2043             volume->v_path, volume->v_cnidscheme);
2044         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
2045             volume->v_path);
2046         flags |= CNID_FLAG_MEMORY;
2047         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
2048 #ifdef SERVERTEXT
2049         /* kill ourself with SIGUSR2 aka msg pending */
2050         if (volume->v_cdb) {
2051             setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
2052                        "Check server messages for details!");
2053             kill(getpid(), SIGUSR2);
2054             /* deactivate cnid caching/storing in AppleDouble files */
2055         }
2056 #endif
2057     }
2058
2059     return (!volume->v_cdb)?-1:0;
2060 }
2061
2062 /*
2063   Check if the underlying filesystem supports EAs for ea:sys volumes.
2064   If not, switch to ea:ad.
2065   As we can't check (requires write access) on ro-volumes, we switch ea:auto
2066   volumes that are options:ro to ea:none.
2067 */
2068 static void check_ea_sys_support(struct vol *vol)
2069 {
2070     uid_t process_uid = 0;
2071     char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
2072     const char *eacontent = "yes";
2073
2074     if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
2075
2076         if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
2077             LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
2078             vol->v_vfs_ea = AFPVOL_EA_NONE;
2079             return;
2080         }
2081
2082         mktemp(eaname);
2083
2084         process_uid = geteuid();
2085         if (process_uid)
2086             if (seteuid(0) == -1) {
2087                 LOG(log_error, logtype_afpd, "check_ea_sys_support: can't seteuid(0): %s", strerror(errno));
2088                 exit(EXITERR_SYS);
2089             }
2090
2091         if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
2092             sys_removexattr(vol->v_path, eaname);
2093             vol->v_vfs_ea = AFPVOL_EA_SYS;
2094         } else {
2095             LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
2096                 vol->v_localname);
2097             vol->v_vfs_ea = AFPVOL_EA_AD;
2098         }
2099
2100         if (process_uid) {
2101             if (seteuid(process_uid) == -1) {
2102                 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2103                 exit(EXITERR_SYS);
2104             }
2105         }
2106     }
2107 }
2108
2109 /* -------------------------
2110  * we are the user here
2111  */
2112 int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2113 {
2114     struct stat st;
2115     char    *volname;
2116     char        *p;
2117
2118     struct vol  *volume;
2119     struct dir  *dir;
2120     int     len, ret;
2121     size_t  namelen;
2122     uint16_t   bitmap;
2123     char        path[ MAXPATHLEN + 1];
2124     char        *vol_uname;
2125     char        *vol_mname;
2126     char        *volname_tmp;
2127
2128     ibuf += 2;
2129     memcpy(&bitmap, ibuf, sizeof( bitmap ));
2130     bitmap = ntohs( bitmap );
2131     ibuf += sizeof( bitmap );
2132
2133     *rbuflen = 0;
2134     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
2135         return AFPERR_BITMAP;
2136     }
2137
2138     len = (unsigned char)*ibuf++;
2139     volname = obj->oldtmp;
2140
2141     if ((volname_tmp = strchr(volname,'+')) != NULL)
2142         volname = volname_tmp+1;
2143
2144     if (utf8_encoding()) {
2145         namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
2146     } else {
2147         namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
2148     }
2149
2150     if ( namelen <= 0) {
2151         return AFPERR_PARAM;
2152     }
2153
2154     ibuf += len;
2155     if ((len + 1) & 1) /* pad to an even boundary */
2156         ibuf++;
2157
2158     load_volumes(obj);
2159
2160     for ( volume = Volumes; volume; volume = volume->v_next ) {
2161         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
2162             break;
2163         }
2164     }
2165
2166     if ( volume == NULL ) {
2167         return AFPERR_PARAM;
2168     }
2169
2170     /* check for a volume password */
2171     if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
2172         return AFPERR_ACCESS;
2173     }
2174
2175     if (( volume->v_flags & AFPVOL_OPEN  ) ) {
2176         /* the volume is already open */
2177         return stat_vol(bitmap, volume, rbuf, rbuflen);
2178     }
2179
2180     if (volume->v_root_preexec) {
2181         if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) {
2182             LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret );
2183             return AFPERR_MISC;
2184         }
2185     }
2186
2187     if (volume->v_preexec) {
2188         if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) {
2189             LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret );
2190             return AFPERR_MISC;
2191         }
2192     }
2193
2194     if ( stat( volume->v_path, &st ) < 0 ) {
2195         return AFPERR_PARAM;
2196     }
2197
2198     if ( chdir( volume->v_path ) < 0 ) {
2199         return AFPERR_PARAM;
2200     }
2201
2202     if ( NULL == getcwd(path, MAXPATHLEN)) {
2203         /* shouldn't be fatal but it will fail later */
2204         LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
2205         return AFPERR_MISC;
2206     }
2207
2208     /* Normalize volume path */
2209 #ifdef REALPATH_TAKES_NULL
2210     if ((volume->v_path = realpath(path, NULL)) == NULL)
2211         return AFPERR_MISC;
2212 #else
2213     if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
2214         return AFPERR_MISC;
2215     if (realpath(path, volume->v_path) == NULL) {
2216         free(volume->v_path);
2217         return AFPERR_MISC;
2218     }
2219     /* Safe some memory */
2220     char *tmp;
2221     if ((tmp = strdup(volume->v_path)) == NULL) {
2222         free(volume->v_path);
2223         return AFPERR_MISC;
2224     }
2225     free(volume->v_path);
2226     volume->v_path = tmp;
2227 #endif
2228
2229     if (volume_codepage(obj, volume) < 0) {
2230         ret = AFPERR_MISC;
2231         goto openvol_err;
2232     }
2233
2234     /* initialize volume variables
2235      * FIXME file size
2236      */
2237     if (utf8_encoding()) {
2238         volume->max_filename = UTF8FILELEN_EARLY;
2239     }
2240     else {
2241         volume->max_filename = MACFILELEN;
2242     }
2243
2244     volume->v_flags |= AFPVOL_OPEN;
2245     volume->v_cdb = NULL;
2246
2247     if (utf8_encoding()) {
2248         len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname);
2249     } else {
2250         len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname);
2251     }
2252     if ( !vol_mname || len <= 0) {
2253         ret = AFPERR_MISC;
2254         goto openvol_err;
2255     }
2256
2257     if ((vol_uname = strrchr(path, '/')) == NULL)
2258         vol_uname = path;
2259     else if (*(vol_uname + 1) != '\0')
2260         vol_uname++;
2261
2262     if ((dir = dir_new(vol_mname,
2263                        vol_uname,
2264                        volume,
2265                        DIRDID_ROOT_PARENT,
2266                        DIRDID_ROOT,
2267                        bfromcstr(volume->v_path),
2268                        &st)
2269             ) == NULL) {
2270         free(vol_mname);
2271         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
2272         ret = AFPERR_MISC;
2273         goto openvol_err;
2274     }
2275     free(vol_mname);
2276     volume->v_root = dir;
2277     curdir = dir;
2278
2279     if (volume_openDB(volume) < 0) {
2280         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
2281             volume->v_path, volume->v_cnidscheme);
2282         ret = AFPERR_MISC;
2283         goto openvol_err;
2284     }
2285
2286     ret  = stat_vol(bitmap, volume, rbuf, rbuflen);
2287
2288     if (ret == AFP_OK) {
2289         handle_special_folders(volume);
2290         savevolinfo(volume,
2291                     volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
2292                     volume->v_cnidport   ? volume->v_cnidport   : Cnid_port);
2293
2294
2295         /*
2296          * If you mount a volume twice, the second time the trash appears on
2297          * the desk-top.  That's because the Mac remembers the DID for the
2298          * trash (even for volumes in different zones, on different servers).
2299          * Just so this works better, we prime the DID cache with the trash,
2300          * fixing the trash at DID 17.
2301          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
2302          */
2303         if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2304
2305             /* FIXME find db time stamp */
2306             if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) {
2307                 LOG (log_error, logtype_afpd,
2308                      "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend",
2309                      volume->v_path);
2310                 ret = AFPERR_MISC;
2311                 goto openvol_err;
2312             }
2313         }
2314         else {
2315             p = Trash;
2316             cname( volume, volume->v_root, &p );
2317         }
2318         return( AFP_OK );
2319     }
2320
2321 openvol_err:
2322     if (volume->v_root) {
2323         dir_free( volume->v_root );
2324         volume->v_root = NULL;
2325     }
2326
2327     volume->v_flags &= ~AFPVOL_OPEN;
2328     if (volume->v_cdb != NULL) {
2329         cnid_close(volume->v_cdb);
2330         volume->v_cdb = NULL;
2331     }
2332     *rbuflen = 0;
2333     return ret;
2334 }
2335
2336 /* ------------------------- */
2337 static void closevol(struct vol *vol)
2338 {
2339     if (!vol)
2340         return;
2341
2342     dir_free( vol->v_root );
2343     vol->v_root = NULL;
2344     if (vol->v_cdb != NULL) {
2345         cnid_close(vol->v_cdb);
2346         vol->v_cdb = NULL;
2347     }
2348
2349     if (vol->v_postexec) {
2350         afprun(0, vol->v_postexec, NULL);
2351     }
2352     if (vol->v_root_postexec) {
2353         afprun(1, vol->v_root_postexec, NULL);
2354     }
2355 }
2356
2357 /* ------------------------- */
2358 void close_all_vol(void)
2359 {
2360     struct vol  *ovol;
2361     curdir = NULL;
2362     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
2363         if ( (ovol->v_flags & AFPVOL_OPEN) ) {
2364             ovol->v_flags &= ~AFPVOL_OPEN;
2365             closevol(ovol);
2366         }
2367     }
2368 }
2369
2370 /* ------------------------- */
2371 static void deletevol(struct vol *vol)
2372 {
2373     struct vol  *ovol;
2374
2375     vol->v_flags &= ~AFPVOL_OPEN;
2376     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
2377         if ( (ovol->v_flags & AFPVOL_OPEN) ) {
2378             break;
2379         }
2380     }
2381     if ( ovol != NULL ) {
2382         /* Even if chdir fails, we can't say afp_closevol fails. */
2383         if ( chdir( ovol->v_path ) == 0 ) {
2384             curdir = ovol->v_root;
2385         }
2386     }
2387
2388     closevol(vol);
2389     if (vol->v_deleted) {
2390         showvol(vol->v_name);
2391         volume_free(vol);
2392         volume_unlink(vol);
2393         free(vol);
2394     }
2395 }
2396
2397 /* ------------------------- */
2398 int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2399 {
2400     struct vol  *vol;
2401     uint16_t   vid;
2402
2403     *rbuflen = 0;
2404     ibuf += 2;
2405     memcpy(&vid, ibuf, sizeof( vid ));
2406     if (NULL == ( vol = getvolbyvid( vid )) ) {
2407         return( AFPERR_PARAM );
2408     }
2409
2410     deletevol(vol);
2411     current_vol = NULL;
2412
2413     return( AFP_OK );
2414 }
2415
2416 /* ------------------------- */
2417 struct vol *getvolbyvid(const uint16_t vid )
2418 {
2419     struct vol  *vol;
2420
2421     for ( vol = Volumes; vol; vol = vol->v_next ) {
2422         if ( vid == vol->v_vid ) {
2423             break;
2424         }
2425     }
2426     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
2427         return( NULL );
2428     }
2429
2430     current_vol = vol;
2431
2432     return( vol );
2433 }
2434
2435 /* ------------------------ */
2436 static int ext_cmp_key(const void *key, const void *obj)
2437 {
2438     const char          *p = key;
2439     const struct extmap *em = obj;
2440     return strdiacasecmp(p, em->em_ext);
2441 }
2442 struct extmap *getextmap(const char *path)
2443 {
2444     char      *p;
2445     struct extmap *em;
2446
2447     if (!Extmap_cnt || NULL == ( p = strrchr( path, '.' )) ) {
2448         return( Defextmap );
2449     }
2450     p++;
2451     if (!*p) {
2452         return( Defextmap );
2453     }
2454     em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
2455     if (em) {
2456         return( em );
2457     } else {
2458         return( Defextmap );
2459     }
2460 }
2461
2462 /* ------------------------- */
2463 struct extmap *getdefextmap(void)
2464 {
2465     return( Defextmap );
2466 }
2467
2468 /* --------------------------
2469    poll if a volume is changed by other processes.
2470    return
2471    0 no attention msg sent
2472    1 attention msg sent
2473    -1 error (socket closed)
2474
2475    Note: if attention return -1 no packet has been
2476    sent because the buffer is full, we don't care
2477    either there's no reader or there's a lot of
2478    traffic and another pollvoltime will follow
2479 */
2480 int  pollvoltime(AFPObj *obj)
2481
2482 {
2483     struct vol       *vol;
2484     struct timeval   tv;
2485     struct stat      st;
2486
2487     if (!(afp_version > 21 && obj->options.server_notif))
2488         return 0;
2489
2490     if ( gettimeofday( &tv, NULL ) < 0 )
2491         return 0;
2492
2493     for ( vol = Volumes; vol; vol = vol->v_next ) {
2494         if ( (vol->v_flags & AFPVOL_OPEN)  && vol->v_mtime + 30 < tv.tv_sec) {
2495             if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) {
2496                 vol->v_mtime = st.st_mtime;
2497                 if (!obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED))
2498                     return -1;
2499                 return 1;
2500             }
2501         }
2502     }
2503     return 0;
2504 }
2505
2506 /* ------------------------- */
2507 void setvoltime(AFPObj *obj, struct vol *vol)
2508 {
2509     struct timeval  tv;
2510
2511     if ( gettimeofday( &tv, NULL ) < 0 ) {
2512         LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
2513         return;
2514     }
2515     if( utime( vol->v_path, NULL ) < 0 ) {
2516         /* write of time failed ... probably a read only filesys,
2517          * where no other users can interfere, so there's no issue here
2518          */
2519     }
2520
2521     /* a little granularity */
2522     if (vol->v_mtime < tv.tv_sec) {
2523         vol->v_mtime = tv.tv_sec;
2524         /* or finder doesn't update free space
2525          * AFP 3.2 and above clients seem to be ok without so many notification
2526          */
2527         if (afp_version < 32 && obj->options.server_notif) {
2528             obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
2529         }
2530     }
2531 }
2532
2533 /* ------------------------- */
2534 int afp_getvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen)
2535 {
2536     struct vol  *vol;
2537     uint16_t   vid, bitmap;
2538
2539     ibuf += 2;
2540     memcpy(&vid, ibuf, sizeof( vid ));
2541     ibuf += sizeof( vid );
2542     memcpy(&bitmap, ibuf, sizeof( bitmap ));
2543     bitmap = ntohs( bitmap );
2544
2545     if (NULL == ( vol = getvolbyvid( vid )) ) {
2546         *rbuflen = 0;
2547         return( AFPERR_PARAM );
2548     }
2549
2550     return stat_vol(bitmap, vol, rbuf, rbuflen);
2551 }
2552
2553 /* ------------------------- */
2554 int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,  size_t *rbuflen)
2555 {
2556     struct adouble ad;
2557     struct vol  *vol;
2558     uint16_t   vid, bitmap;
2559     uint32_t   aint;
2560
2561     ibuf += 2;
2562     *rbuflen = 0;
2563
2564     memcpy(&vid, ibuf, sizeof( vid ));
2565     ibuf += sizeof( vid );
2566     memcpy(&bitmap, ibuf, sizeof( bitmap ));
2567     bitmap = ntohs( bitmap );
2568     ibuf += sizeof(bitmap);
2569
2570     if (( vol = getvolbyvid( vid )) == NULL ) {
2571         return( AFPERR_PARAM );
2572     }
2573
2574     if ((vol->v_flags & AFPVOL_RO))
2575         return AFPERR_VLOCK;
2576
2577     /* we can only set the backup date. */
2578     if (bitmap != (1 << VOLPBIT_BDATE))
2579         return AFPERR_BITMAP;
2580
2581     ad_init(&ad, vol);
2582     if ( ad_open(&ad,  vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) < 0 ) {
2583         if (errno == EROFS)
2584             return AFPERR_VLOCK;
2585
2586         return AFPERR_ACCESS;
2587     }
2588
2589     memcpy(&aint, ibuf, sizeof(aint));
2590     ad_setdate(&ad, AD_DATE_BACKUP, aint);
2591     ad_flush(&ad);
2592     ad_close(&ad, ADFLAGS_HF);
2593     return( AFP_OK );
2594 }
2595
2596 /*
2597  * precreate a folder
2598  * this is only intended for folders in the volume root
2599  * It will *not* work if the folder name contains extended characters
2600  */
2601 static int create_special_folder (const struct vol *vol, const struct _special_folder *folder)
2602 {
2603     char        *p,*q,*r;
2604     struct adouble  ad;
2605     uint16_t   attr;
2606     struct stat st;
2607     int     ret;
2608
2609
2610     p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2);
2611     if ( p == NULL) {
2612         LOG(log_error, logtype_afpd,"malloc failed");
2613         exit (EXITERR_SYS);
2614     }
2615
2616     q=strdup(folder->name);
2617     if ( q == NULL) {
2618         LOG(log_error, logtype_afpd,"malloc failed");
2619         exit (EXITERR_SYS);
2620     }
2621
2622     strcpy(p, vol->v_path);
2623     strcat(p, "/");
2624
2625     r=q;
2626     while (*r) {
2627         if ((vol->v_casefold & AFPVOL_MTOUUPPER))
2628             *r=toupper(*r);
2629         else if ((vol->v_casefold & AFPVOL_MTOULOWER))
2630             *r=tolower(*r);
2631         r++;
2632     }
2633     strcat(p, q);
2634
2635     if ( (ret = stat( p, &st )) < 0 ) {
2636         if (folder->precreate) {
2637             if (ad_mkdir(p, folder->mode)) {
2638                 LOG(log_debug, logtype_afpd,"Creating '%s' failed in %s: %s", p, vol->v_path, strerror(errno));
2639                 free(p);
2640                 free(q);
2641                 return -1;
2642             }
2643             ret = 0;
2644         }
2645     }
2646
2647     if ( !ret && folder->hide) {
2648         /* Hide it */
2649         ad_init(&ad, vol);
2650         if (ad_open(&ad, p, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
2651             free(p);
2652             free(q);
2653             return (-1);
2654         }
2655
2656         ad_setname(&ad, folder->name);
2657
2658         ad_getattr(&ad, &attr);
2659         attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
2660         ad_setattr(&ad, attr);
2661
2662         /* do the same with the finder info */
2663         if (ad_entry(&ad, ADEID_FINDERI)) {
2664             memcpy(&attr, ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, sizeof(attr));
2665             attr   |= htons(FINDERINFO_INVISIBLE);
2666             memcpy(ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,&attr, sizeof(attr));
2667         }
2668
2669         ad_flush(&ad);
2670         ad_close(&ad, ADFLAGS_HF);
2671     }
2672     free(p);
2673     free(q);
2674     return 0;
2675 }
2676
2677 static void handle_special_folders (const struct vol * vol)
2678 {
2679     const _special_folder *p = &special_folders[0];
2680     uid_t process_uid;
2681
2682     process_uid = geteuid();
2683     if (process_uid) {
2684         if (seteuid(0) == -1) {
2685             process_uid = 0;
2686         }
2687     }
2688
2689     for (; p->name != NULL; p++) {
2690         create_special_folder (vol, p);
2691     }
2692
2693     if (process_uid) {
2694         if (seteuid(process_uid) == -1) {
2695             LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno));
2696             exit(EXITERR_SYS);
2697         }
2698     }
2699 }
2700
2701 const struct vol *getvolumes(void)
2702 {
2703     return Volumes;
2704 }
2705
2706 void unload_volumes_and_extmap(void)
2707 {
2708     LOG(log_debug, logtype_afpd, "unload_volumes_and_extmap");
2709     free_extmap();
2710     free_volumes();
2711 }
2712
2713 /* 
2714  * Get a volumes UUID from the config file.
2715  * If there is none, it is generated and stored there.
2716  *
2717  * Returns pointer to allocated storage on success, NULL on error.
2718  */
2719 static char *get_vol_uuid(const AFPObj *obj, const char *volname)
2720 {
2721     char *volname_conf;
2722     char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
2723     FILE *fp;
2724     struct stat tmpstat;
2725     int fd;
2726     
2727     if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) {  /* read open? */
2728         /* scan in the conf file */
2729         while (fgets(buf, sizeof(buf), fp) != NULL) { 
2730             p = buf;
2731             while (p && isblank(*p))
2732                 p++;
2733             if (!p || (*p == '#') || (*p == '\n'))
2734                 continue;                             /* invalid line */
2735             if (*p == '"') {
2736                 p++;
2737                 if ((volname_conf = strtok( p, "\"" )) == NULL)
2738                     continue;                         /* syntax error */
2739             } else {
2740                 if ((volname_conf = strtok( p, " \t" )) == NULL)
2741                     continue;                         /* syntax error: invalid name */
2742             }
2743             p = strchr(p, '\0');
2744             p++;
2745             if (*p == '\0')
2746                 continue;                             /* syntax error */
2747             
2748             if (strcmp(volname, volname_conf) != 0)
2749                 continue;                             /* another volume name */
2750                 
2751             while (p && isblank(*p))
2752                 p++;
2753
2754             if (sscanf(p, "%36s", uuid) == 1 ) {
2755                 for (int i=0; uuid[i]; i++)
2756                     uuid[i] = toupper(uuid[i]);
2757                 LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
2758                 fclose(fp);
2759                 return strdup(uuid);
2760             }
2761         }
2762     }
2763
2764     if (fp)
2765         fclose(fp);
2766
2767     /*  not found or no file, reopen in append mode */
2768
2769     if (stat(obj->options.uuidconf, &tmpstat)) {                /* no file */
2770         if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
2771             LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s).",
2772                 obj->options.uuidconf, strerror(errno));
2773             return NULL;
2774         }
2775         if (( fp = fdopen( fd, "w" )) == NULL ) {
2776             LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).",
2777                 obj->options.uuidconf, strerror(errno));
2778             close(fd);
2779             return NULL;
2780         }
2781     } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
2782         LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
2783             obj->options.uuidconf, strerror(errno));
2784         return NULL;
2785     }
2786     fseek(fp, 0L, SEEK_END);
2787     if(ftell(fp) == 0) {                     /* size = 0 */
2788         fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
2789         fprintf(fp, "# This file is auto-generated by afpd\n");
2790         fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
2791     } else {
2792         fseek(fp, -1L, SEEK_END);
2793         if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
2794     }                    
2795     
2796     /* generate uuid and write to file */
2797     atalk_uuid_t id;
2798     const char *cp;
2799     randombytes((void *)id, 16);
2800     cp = uuid_bin2string(id);
2801
2802     LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
2803
2804     fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
2805     fclose(fp);
2806     
2807     return strdup(cp);
2808 }