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