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