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