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