]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
allow extended characters in volume names
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * $Id: volume.c,v 1.51.2.7.2.10 2003-11-15 00:00:35 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 ucs2_t *name)
481 {
482     struct vol  *volume;
483     for ( volume = Volumes; volume; volume = volume->v_next ) {
484         if ( !strcasecmp_w( 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     ucs2_t      tmpname[512];
502
503     if ( name == NULL || *name == '\0' ) {
504         if ((name = strrchr( path, '/' )) == NULL) {
505             return -1;  /* Obviously not a fully qualified path */
506         }
507
508         /* if you wish to share /, you need to specify a name. */
509         if (*++name == '\0')
510             return -1;
511     }
512
513     vlen = strlen( name );
514     if ( vlen > AFPVOL_NAMELEN ) {
515         vlen = AFPVOL_NAMELEN;
516         name[AFPVOL_NAMELEN] = '\0';
517     }
518
519     /* convert name to UCS2 first */
520     if ( 0 >= ( vlen = convert_string(obj->options.unixcharset, CH_UCS2, name, vlen, tmpname, 512)) )
521         return -1;
522
523     for ( volume = Volumes; volume; volume = volume->v_next ) {
524         if ( strcasecmp_w( volume->v_name, tmpname ) == 0 ) {
525            if (volume->v_deleted) {
526                hide = 1;
527            }
528            else {
529                return -1;       /* Won't be able to access it, anyway... */
530            }
531         }
532     }
533
534
535     if (!( volume = (struct vol *)calloc(1, sizeof( struct vol ))) ) {
536         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
537         return -1;
538     }
539     if ( NULL == ( volume->v_name = strdup_w(tmpname))) {
540         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
541         free(volume);
542         return -1;
543     }
544     if (!( volume->v_path = (char *)malloc( strlen( path ) + 1 )) ) {
545         LOG(log_error, logtype_afpd, "creatvol: malloc: %s", strerror(errno) );
546         free(volume->v_name);
547         free(volume);
548         return -1;
549     }
550     volume->v_hide = hide;
551     strcpy( volume->v_path, path );
552
553 #ifdef __svr4__
554     volume->v_qfd = -1;
555 #endif /* __svr4__ */
556     /* os X start at 1 and use network order ie. 1 2 3 */
557     volume->v_vid = ++lastvid;
558     volume->v_vid = htons(volume->v_vid);
559
560     /* handle options */
561     if (options) {
562         /* should we casefold? */
563         volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
564
565         /* shift in some flags */
566         volume->v_flags = options[VOLOPT_FLAGS].i_value;
567
568         if (options[VOLOPT_PASSWORD].c_value)
569             volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
570
571         if (options[VOLOPT_VETO].c_value)
572             volume->v_veto = strdup(options[VOLOPT_VETO].c_value);
573
574         if (options[VOLOPT_ENCODING].c_value)
575             volume->v_volcodepage = strdup(options[VOLOPT_ENCODING].c_value);
576
577         if (options[VOLOPT_MACCHARSET].c_value)
578             volume->v_maccodepage = strdup(options[VOLOPT_MACCHARSET].c_value);
579
580         if (options[VOLOPT_DBPATH].c_value)
581             volume->v_dbpath = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_DBPATH].c_value, pwd, path);
582
583        if (options[VOLOPT_CNIDSCHEME].c_value)
584            volume->v_cnidscheme = strdup(options[VOLOPT_CNIDSCHEME].c_value);
585
586         if (options[VOLOPT_UMASK].i_value)
587             volume->v_umask = (mode_t)options[VOLOPT_UMASK].i_value;
588
589         if (options[VOLOPT_ADOUBLE].i_value)
590             volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
591         else 
592             volume->v_adouble = AD_VERSION;
593 #ifdef FORCE_UIDGID
594         if (options[VOLOPT_FORCEUID].c_value) {
595             volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
596         } else {
597             volume->v_forceuid = NULL; /* set as null so as to return 0 later on */
598         }
599
600         if (options[VOLOPT_FORCEGID].c_value) {
601             volume->v_forcegid = strdup(options[VOLOPT_FORCEGID].c_value);
602         } else {
603             volume->v_forcegid = NULL; /* set as null so as to return 0 later on */
604         }
605 #endif
606         if (!user) {
607             if (options[VOLOPT_PREEXEC].c_value)
608                 volume->v_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_PREEXEC].c_value, pwd, path);
609             volume->v_preexec_close = options[VOLOPT_PREEXEC].i_value;
610
611             if (options[VOLOPT_POSTEXEC].c_value)
612                 volume->v_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_POSTEXEC].c_value, pwd, path);
613
614             if (options[VOLOPT_ROOTPREEXEC].c_value)
615                 volume->v_root_preexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPREEXEC].c_value, pwd, path);
616             volume->v_root_preexec_close = options[VOLOPT_ROOTPREEXEC].i_value;
617
618             if (options[VOLOPT_ROOTPOSTEXEC].c_value)
619                 volume->v_root_postexec = volxlate(obj, NULL, MAXPATHLEN, options[VOLOPT_ROOTPOSTEXEC].c_value, pwd, path);
620         }
621     }
622
623     volume->v_next = Volumes;
624     Volumes = volume;
625     return 0;
626 }
627
628 /* ---------------- */
629 static char *myfgets( buf, size, fp )
630 char    *buf;
631 int             size;
632 FILE    *fp;
633 {
634     char        *p;
635     int         c;
636
637     p = buf;
638     while ((EOF != ( c = getc( fp )) ) && ( size > 0 )) {
639         if ( c == '\n' || c == '\r' ) {
640             *p++ = '\n';
641             break;
642         } else {
643             *p++ = c;
644         }
645         size--;
646     }
647
648     if ( p == buf ) {
649         return( NULL );
650     } else {
651         *p = '\0';
652         return( buf );
653     }
654 }
655
656
657 /* check access list. this function wants something of the following
658  * form:
659  *        @group,name,name2,@group2,name3
660  *
661  * a NULL argument allows everybody to have access.
662  * we return three things:
663  *     -1: no list
664  *      0: list exists, but name isn't in it
665  *      1: in list
666  */
667
668 #ifndef NO_REAL_USER_NAME
669 /* authentication is case insensitive 
670  * FIXME should we do the same with group name?
671 */
672 #define access_strcmp strcasecmp
673
674 #else
675 #define access_strcmp strcmp
676
677 #endif
678
679 static int accessvol(args, name)
680 const char *args;
681 const char *name;
682 {
683     char buf[MAXPATHLEN + 1], *p;
684     struct group *gr;
685
686     if (!args)
687         return -1;
688
689     strncpy(buf, args, sizeof(buf));
690     if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
691         return -1;
692
693     while (p) {
694         if (*p == '@') { /* it's a group */
695             if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
696                 return 1;
697         } else if (access_strcmp(p, name) == 0) /* it's a user name */
698             return 1;
699         p = strtok(NULL, ",");
700     }
701
702     return 0;
703 }
704
705 static void setextmap( ext, type, creator, user)
706 char            *ext, *type, *creator;
707 int                     user;
708 {
709     struct extmap       *em;
710     int                 cnt;
711
712     if (Extmap == NULL) {
713         if (( Extmap = calloc(1, sizeof( struct extmap ))) == NULL ) {
714             LOG(log_error, logtype_afpd, "setextmap: calloc: %s", strerror(errno) );
715             return;
716         }
717     }
718     ext++;
719     for ( em = Extmap, cnt = 0; em->em_ext; em++, cnt++) {
720         if ( (strdiacasecmp( em->em_ext, ext )) == 0 ) {
721             break;
722         }
723     }
724
725     if ( em->em_ext == NULL ) {
726         if (!(Extmap  = realloc( Extmap, sizeof( struct extmap ) * (cnt +2))) ) {
727             LOG(log_error, logtype_afpd, "setextmap: realloc: %s", strerror(errno) );
728             return;
729         }
730         (Extmap +cnt +1)->em_ext = NULL;
731         em = Extmap +cnt;
732     } else if ( !user ) {
733         return;
734     }
735     if (em->em_ext)
736         free(em->em_ext);
737
738     if (!(em->em_ext = strdup(  ext))) {
739         LOG(log_error, logtype_afpd, "setextmap: strdup: %s", strerror(errno) );
740         return;
741     }
742
743     if ( *type == '\0' ) {
744         memcpy(em->em_type, "????", sizeof( em->em_type ));
745     } else {
746         memcpy(em->em_type, type, sizeof( em->em_type ));
747     }
748     if ( *creator == '\0' ) {
749         memcpy(em->em_creator, "UNIX", sizeof( em->em_creator ));
750     } else {
751         memcpy(em->em_creator, creator, sizeof( em->em_creator ));
752     }
753 }
754
755 /* -------------------------- */
756 static int extmap_cmp(const void *map1, const void *map2)
757 {
758     const struct extmap *em1 = map1;
759     const struct extmap *em2 = map2;
760     return strdiacasecmp(em1->em_ext, em2->em_ext);
761 }
762
763 static void sortextmap( void)
764 {
765     struct extmap       *em;
766
767     Extmap_cnt = 0;
768     if ((em = Extmap) == NULL) {
769         return;
770     }
771     while (em->em_ext) {
772         em++;
773         Extmap_cnt++;
774     }
775     if (Extmap_cnt) {
776         qsort(Extmap, Extmap_cnt, sizeof(struct extmap), extmap_cmp);
777         Defextmap = Extmap;
778     }
779 }
780
781 /* ----------------------
782 */
783 static void free_extmap( void)
784 {
785     if (Extmap) {
786         free(Extmap);
787         Extmap = NULL;
788         Defextmap = Extmap;
789         Extmap_cnt = 0;
790     }
791 }
792
793 /* ----------------------
794 */
795 static int volfile_changed(struct afp_volume_name *p) 
796 {
797     struct stat      st;
798     char *name;
799     
800     if (p->full_name) 
801         name = p->full_name;
802     else
803         name = p->name;
804         
805     if (!stat( name, &st) && st.st_mtime > p->mtime) {
806         p->mtime = st.st_mtime;
807         return 1;
808     }
809     return 0;
810 }
811
812 /* ----------------------
813  * Read a volume configuration file and add the volumes contained within to
814  * the global volume list.  If p2 is non-NULL, the file that is opened is
815  * p1/p2
816  * 
817  * Lines that begin with # and blank lines are ignored.
818  * Volume lines are of the form:
819  *              <unix path> [<volume name>] [allow:<user>,<@group>,...] \
820  *                           [codepage:<file>] [casefold:<num>]
821  *              <extension> TYPE [CREATOR]
822  */
823 static int readvolfile(obj, p1, p2, user, pwent)
824 AFPObj      *obj;
825 struct afp_volume_name  *p1;
826 char        *p2;
827 int             user;
828 struct passwd *pwent;
829 {
830     FILE                *fp;
831     char                path[ MAXPATHLEN + 1], tmp[ MAXPATHLEN + 1],
832     volname[ AFPVOL_NAMELEN + 1 ], buf[ BUFSIZ ],
833     type[ 5 ], creator[ 5 ];
834     char                *u, *p;
835     struct passwd       *pw;
836     struct vol_option   options[VOLOPT_NUM], save_options[VOLOPT_NUM];
837     int                 i;
838     struct stat         st;
839     int                 fd;
840
841     if (!p1->name)
842         return -1;
843     p1->mtime = 0;
844     strcpy( path, p1->name );
845     if ( p2 != NULL ) {
846         strcat( path, "/" );
847         strcat( path, p2 );
848         if (p1->full_name) {
849             free(p1->full_name);
850         }
851         p1->full_name = strdup(path);
852     }
853
854     if (NULL == ( fp = fopen( path, "r" )) ) {
855         return( -1 );
856     }
857     fd = fileno(fp);
858     if (fd != -1 && !fstat( fd, &st) ) {
859         p1->mtime = st.st_mtime;
860     }
861
862     memset(save_options, 0, sizeof(save_options));
863     while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
864         initline( strlen( buf ), buf );
865         parseline( sizeof( path ) - 1, path );
866         switch ( *path ) {
867         case '\0' :
868         case '#' :
869             continue;
870
871         case ':':
872             /* change the default options for this file */
873             if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
874                 *tmp = '\0';
875                 for (i = 0; i < VOLOPT_NUM; i++) {
876                     if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1,
877                                    path + VOLOPT_DEFAULT_LEN) < 0)
878                         break;
879                     volset(save_options, NULL, tmp, sizeof(tmp) - 1,
880                            path + VOLOPT_DEFAULT_LEN);
881                 }
882             }
883             break;
884
885         case '~' :
886             if (( p = strchr( path, '/' )) != NULL ) {
887                 *p++ = '\0';
888             }
889             u = path;
890             u++;
891             if ( *u == '\0' ) {
892                 u = obj->username;
893             }
894             if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
895                 continue;
896             }
897             strcpy( tmp, pw->pw_dir );
898             if ( p != NULL && *p != '\0' ) {
899                 strcat( tmp, "/" );
900                 strcat( tmp, p );
901             }
902             /* Tag a user's home directory with their umask.  Note, this will
903              * be overwritten if the user actually specifies a umask: option
904              * for a '~' volume. */
905             save_options[VOLOPT_UMASK].i_value = obj->options.save_mask;
906             /* fall through */
907
908         case '/' :
909             /* send path through variable substitution */
910             if (*path != '~') /* need to copy path to tmp */
911                 strcpy(tmp, path);
912             if (!pwent)
913                 pwent = getpwnam(obj->username);
914             volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL);
915
916             /* this is sort of braindead. basically, i want to be
917              * able to specify things in any order, but i don't want to 
918              * re-write everything. 
919              *
920              * currently we have options: 
921              *   volname
922              *   codepage:x
923              *   casefold:x
924              *   allow:x,y,@z
925              *   deny:x,y,@z
926              *   rwlist:x,y,@z
927              *   rolist:x,y,@z
928              *   options:prodos,crlf,noadouble,ro...
929              *   dbpath:x
930              *   password:x
931              *   preexec:x
932              *
933              *   namemask:x,y,!z  (not implemented yet)
934              */
935             memcpy(options, save_options, sizeof(options));
936             *volname = '\0';
937
938             /* read in up to VOLOP_NUM possible options */
939             for (i = 0; i < VOLOPT_NUM; i++) {
940                 if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
941                     break;
942
943                 volset(options, save_options, volname, sizeof(volname) - 1, tmp);
944             }
945
946             /* check allow/deny lists:
947                allow -> either no list (-1), or in list (1)
948                deny -> either no list (-1), or not in list (0) */
949             if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
950                     (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1)) {
951
952                 /* handle read-only behaviour. semantics:
953                  * 1) neither the rolist nor the rwlist exist -> rw
954                  * 2) rolist exists -> ro if user is in it.
955                  * 3) rwlist exists -> ro unless user is in it. */
956                 if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) &&
957                         ((accessvol(options[VOLOPT_ROLIST].c_value,
958                                     obj->username) == 1) ||
959                          !accessvol(options[VOLOPT_RWLIST].c_value,
960                                     obj->username)))
961                     options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
962
963                 /* do variable substitution for volname */
964                 volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path);
965                 creatvol(obj, pwent, path, tmp, options, p2 != NULL);
966             }
967             volfree(options, save_options);
968             break;
969
970         case '.' :
971             parseline( sizeof( type ) - 1, type );
972             parseline( sizeof( creator ) - 1, creator );
973             setextmap( path, type, creator, user);
974             break;
975
976         default :
977             break;
978         }
979     }
980     volfree(save_options, NULL);
981     sortextmap();
982     if ( fclose( fp ) != 0 ) {
983         LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
984     }
985     p1->loaded = 1;
986     return( 0 );
987 }
988
989 /* ------------------------------- */
990 static void volume_free(struct vol *vol)
991 {
992     free(vol->v_name);
993     vol->v_name = NULL;
994     free(vol->v_path);
995     free(vol->v_password);
996     free(vol->v_veto);
997     free(vol->v_volcodepage);
998     free(vol->v_maccodepage);
999     free(vol->v_dbpath);
1000 #ifdef FORCE_UIDGID
1001     free(vol->v_forceuid);
1002     free(vol->v_forcegid);
1003 #endif /* FORCE_UIDGID */
1004 }
1005
1006 /* ------------------------------- */
1007 static void free_volumes(void )
1008 {
1009     struct vol  *vol;
1010     struct vol  *nvol, *ovol;
1011
1012     for ( vol = Volumes; vol; vol = vol->v_next ) {
1013         if (( vol->v_flags & AFPVOL_OPEN ) ) {
1014             vol->v_deleted = 1;
1015             continue;
1016         }
1017         volume_free(vol);
1018     }
1019
1020     for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1021         nvol = vol->v_next;
1022
1023         if (vol->v_name == NULL) {
1024            if (Volumes == vol) {
1025                Volumes = nvol;
1026            }
1027            if (!ovol) {
1028                ovol = Volumes;
1029            }
1030            else {
1031               ovol->v_next = nvol;
1032            }
1033            free(vol);
1034         }
1035         else {
1036            ovol = vol;
1037         }
1038     }
1039 }
1040
1041 /* ------------------------------- */
1042 static void volume_unlink(struct vol *volume)
1043 {
1044 struct vol *vol, *ovol, *nvol;
1045
1046     if (volume == Volumes) {
1047         Volumes = Volumes->v_next;
1048         return;
1049     }
1050     for ( vol = Volumes, ovol = NULL; vol; vol = nvol) {
1051         nvol = vol->v_next;
1052
1053         if (vol == volume) {
1054            if (!ovol) {
1055                ovol = Volumes;
1056            }
1057            else {
1058               ovol->v_next = nvol;
1059            }
1060            break;
1061         }
1062         else {
1063            ovol = vol;
1064         }
1065     }
1066 }
1067
1068
1069 static int getvolspace( vol, bfree, btotal, xbfree, xbtotal, bsize )
1070 struct vol      *vol;
1071 u_int32_t       *bfree, *btotal, *bsize;
1072 VolSpace    *xbfree, *xbtotal;
1073 {
1074     int         spaceflag, rc;
1075     u_int32_t   maxsize;
1076 #ifndef NO_QUOTA_SUPPORT
1077     VolSpace    qfree, qtotal;
1078 #endif
1079
1080     spaceflag = AFPVOL_GVSMASK & vol->v_flags;
1081     /* report up to 2GB if afp version is < 2.2 (4GB if not) */
1082     maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 :
1083               (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE))
1084                ? 0x7fffffffL : 0xffffffffL);
1085
1086 #ifdef AFS
1087     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
1088         if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
1089             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
1090             goto getvolspace_done;
1091         }
1092     }
1093 #endif
1094
1095     if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
1096                                     bsize)) != AFP_OK ) {
1097         return( rc );
1098     }
1099
1100 #define min(a,b)        ((a)<(b)?(a):(b))
1101 #ifndef NO_QUOTA_SUPPORT
1102     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
1103         if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
1104             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
1105             *xbfree = min(*xbfree, qfree);
1106             *xbtotal = min( *xbtotal, qtotal);
1107             goto getvolspace_done;
1108         }
1109     }
1110 #endif
1111     vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
1112
1113 getvolspace_done:
1114     *bfree = min( *xbfree, maxsize);
1115     *btotal = min( *xbtotal, maxsize);
1116     return( AFP_OK );
1117 }
1118
1119 /* ----------------------- 
1120  * set volume creation date
1121  * avoid duplicate, well at least it tries
1122 */
1123 static void vol_setdate(u_int16_t id, struct adouble *adp, time_t date)
1124 {
1125     struct vol  *volume;
1126     struct vol  *vol = Volumes;
1127
1128     for ( volume = Volumes; volume; volume = volume->v_next ) {
1129         if (volume->v_vid == id) {
1130             vol = volume;
1131         }
1132         else if (AD_DATE_FROM_UNIX(date) == volume->v_ctime) {
1133             date = date+1;
1134             volume = Volumes; /* restart */
1135         }
1136     }
1137     vol->v_ctime = AD_DATE_FROM_UNIX(date);
1138     ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date);
1139 }
1140
1141 /* ----------------------- */
1142 static int getvolparams( bitmap, vol, st, buf, buflen )
1143 u_int16_t       bitmap;
1144 struct vol      *vol;
1145 struct stat     *st;
1146 char    *buf;
1147 int             *buflen;
1148 {
1149     struct adouble      ad;
1150     int                 bit = 0, isad = 1;
1151     u_int32_t           aint;
1152     u_short             ashort;
1153     u_int32_t           bfree, btotal, bsize;
1154     VolSpace            xbfree, xbtotal; /* extended bytes */
1155     char                *data, *nameoff = NULL;
1156     char                *slash;
1157
1158     /* courtesy of jallison@whistle.com:
1159      * For MacOS8.x support we need to create the
1160      * .Parent file here if it doesn't exist. */
1161
1162     ad_init(&ad, vol->v_adouble);
1163     if ( ad_open( vol->v_path, vol_noadouble(vol) |
1164                   ADFLAGS_HF|ADFLAGS_DIR, O_RDWR | O_CREAT,
1165                   0666, &ad) < 0 ) {
1166         isad = 0;
1167         vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1168
1169     } else if (ad_get_HF_flags( &ad ) & O_CREAT) {
1170         slash = strrchr( vol->v_path, '/' );
1171         if(slash)
1172             slash++;
1173         else
1174             slash = vol->v_path;
1175
1176         ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
1177         memcpy(ad_entry( &ad, ADEID_NAME ), slash,
1178                ad_getentrylen( &ad, ADEID_NAME ));
1179         vol_setdate(vol->v_vid, &ad, st->st_mtime);
1180         ad_flush(&ad, ADFLAGS_HF);
1181     }
1182     else {
1183         if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)
1184             vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
1185         else 
1186             vol->v_ctime = aint;
1187     }
1188
1189     if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
1190                      (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
1191                      (1<<VOLPBIT_BSIZE)) ) != 0 ) {
1192         if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
1193                           &bsize) < 0 ) {
1194             if ( isad ) {
1195                 ad_close( &ad, ADFLAGS_HF );
1196             }
1197             return( AFPERR_PARAM );
1198         }
1199     }
1200
1201     data = buf;
1202     while ( bitmap != 0 ) {
1203         while (( bitmap & 1 ) == 0 ) {
1204             bitmap = bitmap>>1;
1205             bit++;
1206         }
1207
1208         switch ( bit ) {
1209         case VOLPBIT_ATTR :
1210             ashort = 0;
1211             if (0 == (vol->v_flags & AFPVOL_NOFILEID) && vol->v_cdb != NULL &&
1212                            (vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1213                 ashort = VOLPBIT_ATTR_FILEID;
1214             }
1215             /* check for read-only.
1216              * NOTE: we don't actually set the read-only flag unless
1217              *       it's passed in that way as it's possible to mount
1218              *       a read-write filesystem under a read-only one. */
1219             if ((vol->v_flags & AFPVOL_RO) ||
1220                     ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) {
1221                 ashort |= VOLPBIT_ATTR_RO;
1222             }
1223             ashort |= VOLPBIT_ATTR_CATSEARCH;
1224             if (afp_version >= 30) {
1225                 ashort |= VOLPBIT_ATTR_UTF8;
1226                 if (vol->v_flags & AFPVOL_UNIX_PRIV)
1227                     ashort |= VOLPBIT_ATTR_UNIXPRIV;
1228             }
1229             ashort = htons(ashort);
1230             memcpy(data, &ashort, sizeof( ashort ));
1231             data += sizeof( ashort );
1232             break;
1233
1234         case VOLPBIT_SIG :
1235             ashort = htons( AFPVOLSIG_DEFAULT );
1236             memcpy(data, &ashort, sizeof( ashort ));
1237             data += sizeof( ashort );
1238             break;
1239
1240         case VOLPBIT_CDATE :
1241             aint = vol->v_ctime;
1242             memcpy(data, &aint, sizeof( aint ));
1243             data += sizeof( aint );
1244             break;
1245
1246         case VOLPBIT_MDATE :
1247             if ( st->st_mtime > vol->v_mtime ) {
1248                 vol->v_mtime = st->st_mtime;
1249             }
1250             aint = AD_DATE_FROM_UNIX(vol->v_mtime);
1251             memcpy(data, &aint, sizeof( aint ));
1252             data += sizeof( aint );
1253             break;
1254
1255         case VOLPBIT_BDATE :
1256             if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1257                 aint = AD_DATE_START;
1258             memcpy(data, &aint, sizeof( aint ));
1259             data += sizeof( aint );
1260             break;
1261
1262         case VOLPBIT_VID :
1263             memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
1264             data += sizeof( vol->v_vid );
1265             break;
1266
1267         case VOLPBIT_BFREE :
1268             bfree = htonl( bfree );
1269             memcpy(data, &bfree, sizeof( bfree ));
1270             data += sizeof( bfree );
1271             break;
1272
1273         case VOLPBIT_BTOTAL :
1274             btotal = htonl( btotal );
1275             memcpy(data, &btotal, sizeof( btotal ));
1276             data += sizeof( btotal );
1277             break;
1278
1279 #ifndef NO_LARGE_VOL_SUPPORT
1280         case VOLPBIT_XBFREE :
1281             xbfree = hton64( xbfree );
1282 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1283             bcopy(&xbfree, data, sizeof(xbfree));
1284 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1285             memcpy(data, &xbfree, sizeof( xbfree ));
1286 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1287             data += sizeof( xbfree );
1288             break;
1289
1290         case VOLPBIT_XBTOTAL :
1291             xbtotal = hton64( xbtotal );
1292 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1293             bcopy(&xbtotal, data, sizeof(xbtotal));
1294 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1295             memcpy(data, &xbtotal, sizeof( xbtotal ));
1296 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1297             data += sizeof( xbfree );
1298             break;
1299 #endif /* ! NO_LARGE_VOL_SUPPORT */
1300
1301         case VOLPBIT_NAME :
1302             nameoff = data;
1303             data += sizeof( u_int16_t );
1304             break;
1305
1306         case VOLPBIT_BSIZE:  /* block size */
1307             bsize = htonl(bsize);
1308             memcpy(data, &bsize, sizeof(bsize));
1309             data += sizeof(bsize);
1310             break;
1311
1312         default :
1313             if ( isad ) {
1314                 ad_close( &ad, ADFLAGS_HF );
1315             }
1316             return( AFPERR_BITMAP );
1317         }
1318         bitmap = bitmap>>1;
1319         bit++;
1320     }
1321     if ( nameoff ) {
1322         ashort = htons( data - buf );
1323         memcpy(nameoff, &ashort, sizeof( ashort ));
1324         aint = ucs2_to_charset( (utf8_encoding()?CH_UTF8_MAC:vol->v_maccharset), vol->v_name, data+1, 255);
1325         if ( aint <= 0 ) {
1326             *buflen = 0;
1327             return AFPERR_MISC;
1328         }
1329                 
1330         *data++ = aint;
1331         data += aint;
1332     }
1333     if ( isad ) {
1334         ad_close( &ad, ADFLAGS_HF );
1335     }
1336     *buflen = data - buf;
1337     return( AFP_OK );
1338 }
1339
1340 /* ------------------------- */
1341 int static stat_vol(u_int16_t bitmap, struct vol *vol, char *rbuf, int *rbuflen)
1342 {
1343     struct stat st;
1344     int         buflen, ret;
1345
1346     if ( stat( vol->v_path, &st ) < 0 ) {
1347         *rbuflen = 0;
1348         return( AFPERR_PARAM );
1349     }
1350
1351     buflen = *rbuflen - sizeof( bitmap );
1352     if (( ret = getvolparams(bitmap, vol, &st,
1353                               rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
1354         *rbuflen = 0;
1355         return( ret );
1356     }
1357     *rbuflen = buflen + sizeof( bitmap );
1358     bitmap = htons( bitmap );
1359     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1360     return( AFP_OK );
1361
1362 }
1363
1364 /* ------------------------------- */
1365 void load_volumes(AFPObj *obj)
1366 {
1367     struct passwd       *pwent;
1368
1369     if (Volumes) {
1370         int changed = 0;
1371         
1372         /* check files date */
1373         if (obj->options.defaultvol.loaded) {
1374             changed = volfile_changed(&obj->options.defaultvol);
1375         }
1376         if (obj->options.systemvol.loaded) {
1377             changed |= volfile_changed(&obj->options.systemvol);
1378         }
1379         if (obj->options.uservol.loaded) {
1380             changed |= volfile_changed(&obj->options.uservol);
1381         }
1382         if (!changed)
1383             return;
1384         
1385         free_extmap();
1386         free_volumes();
1387     }
1388     
1389     pwent = getpwnam(obj->username);
1390     if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
1391         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
1392     }
1393
1394     if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
1395         readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent);
1396     } else if (pwent) {
1397         /*
1398         * Read user's AppleVolumes or .AppleVolumes file
1399         * If neither are readable, read the default volumes file. if
1400         * that doesn't work, create a user share.
1401         */
1402         obj->options.uservol.name = strdup(pwent->pw_dir);
1403         if ( readvolfile(obj, &obj->options.uservol,    "AppleVolumes", 1, pwent) < 0 &&
1404                 readvolfile(obj, &obj->options.uservol, ".AppleVolumes", 1, pwent) < 0 &&
1405                 readvolfile(obj, &obj->options.uservol, "applevolumes", 1, pwent) < 0 &&
1406                 readvolfile(obj, &obj->options.uservol, ".applevolumes", 1, pwent) < 0 &&
1407                 obj->options.defaultvol.name != NULL ) {
1408             if (readvolfile(obj, &obj->options.defaultvol, NULL, 1, pwent) < 0)
1409                 creatvol(obj, pwent, pwent->pw_dir, NULL, NULL, 1);
1410         }
1411     }
1412     if ( obj->options.flags & OPTION_USERVOLFIRST ) {
1413         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent );
1414     }
1415 }
1416
1417 /* ------------------------------- */
1418 int afp_getsrvrparms(obj, ibuf, ibuflen, rbuf, rbuflen )
1419 AFPObj      *obj;
1420 char    *ibuf, *rbuf;
1421 int     ibuflen, *rbuflen;
1422 {
1423     struct timeval      tv;
1424     struct stat         st;
1425     struct vol          *volume;
1426     char        *data;
1427     char                *namebuf;
1428     int                 vcnt, len;
1429
1430     load_volumes(obj);
1431
1432     data = rbuf + 5;
1433     for ( vcnt = 0, volume = Volumes; volume; volume = volume->v_next ) {
1434         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
1435             if ( stat( volume->v_path, &st ) < 0 ) {
1436                 LOG(log_info, logtype_afpd, "afp_getsrvrparms: stat %s: %s",
1437                         volume->v_path, strerror(errno) );
1438                 continue;               /* can't access directory */
1439             }
1440             if (!S_ISDIR(st.st_mode)) {
1441                 continue;               /* not a dir */
1442             }
1443         }
1444         if (volume->v_hide) {
1445             continue;           /* config file changed but the volume was mounted */
1446         }
1447         /* set password bit if there's a volume password */
1448         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
1449
1450         /* Apple 2 clients running ProDOS-8 expect one volume to have
1451            bit 0 of this byte set.  They will not recognize anything
1452            on the server unless this is the case.  I have not
1453            completely worked this out, but it's related to booting
1454            from the server.  Support for that function is a ways
1455            off.. <shirsch@ibm.net> */
1456         *data |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
1457         *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
1458
1459         len = ucs2_to_charset_allocate((utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset),
1460                                         &namebuf, volume->v_name);
1461         if (len <= 0)
1462                 continue;
1463         *data++ = len;
1464         memcpy(data, namebuf, len );
1465         data += len;
1466         free(namebuf);
1467         vcnt++;
1468     }
1469
1470     *rbuflen = data - rbuf;
1471     data = rbuf;
1472     if ( gettimeofday( &tv, 0 ) < 0 ) {
1473         LOG(log_error, logtype_afpd, "afp_getsrvrparms: gettimeofday: %s", strerror(errno) );
1474         *rbuflen = 0;
1475         return AFPERR_PARAM;
1476     }
1477     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
1478     memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
1479     data += sizeof( u_int32_t);
1480     *data = vcnt;
1481     return( AFP_OK );
1482 }
1483
1484 /* ------------------------- 
1485  * we are the user here
1486 */
1487 int afp_openvol(obj, ibuf, ibuflen, rbuf, rbuflen )
1488 AFPObj      *obj;
1489 char    *ibuf, *rbuf;
1490 int             ibuflen, *rbuflen;
1491 {
1492     struct stat st;
1493     char        *volname;
1494     char        *p;
1495     struct vol  *volume;
1496     struct dir  *dir;
1497     int         len, ret;
1498     size_t      namelen;
1499     u_int16_t   bitmap;
1500
1501     ibuf += 2;
1502     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1503     bitmap = ntohs( bitmap );
1504     ibuf += sizeof( bitmap );
1505     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
1506         *rbuflen = 0;
1507         return AFPERR_BITMAP;
1508     }
1509
1510     len = (unsigned char)*ibuf++;
1511     volname = obj->oldtmp;
1512     namelen = convert_string( (utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset), CH_UCS2,
1513                               ibuf, len, volname, sizeof(obj->oldtmp));
1514     if ( namelen <= 0){
1515         *rbuflen = 0;
1516         return AFPERR_PARAM;
1517     }
1518
1519     ibuf += len;
1520     if ((len + 1) & 1) /* pad to an even boundary */
1521         ibuf++;
1522
1523     load_volumes(obj);
1524
1525     for ( volume = Volumes; volume; volume = volume->v_next ) {
1526         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
1527             break;
1528         }
1529     }
1530
1531     if ( volume == NULL ) {
1532         *rbuflen = 0;
1533         return AFPERR_PARAM;
1534     }
1535
1536     /* check for a volume password */
1537     if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
1538         *rbuflen = 0;
1539         return AFPERR_ACCESS;
1540     }
1541
1542     if (( volume->v_flags & AFPVOL_OPEN  ) ) {
1543         /* the volume is already open */
1544 #ifdef FORCE_UIDGID
1545         set_uidgid ( volume );
1546 #endif
1547         return stat_vol(bitmap, volume, rbuf, rbuflen);
1548     }
1549
1550     /* initialize volume variables
1551      * FIXME file size
1552     */
1553     if (afp_version >= 30) {
1554         volume->max_filename = 255;
1555     }
1556     else {
1557         volume->max_filename = MACFILELEN;
1558     }
1559
1560     volume->v_dir = volume->v_root = NULL;
1561
1562     /* FIXME unix name != mac name */
1563     char *vol_uname;
1564     char *vol_mname;
1565
1566     len = convert_string_allocate( CH_UCS2, (utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset),
1567                                        volume->v_name, namelen, &vol_mname);
1568     if ( !vol_mname || len <= 0) {
1569         ret = AFPERR_MISC;
1570         goto openvol_err;
1571     }
1572         
1573     len = convert_string_allocate( CH_UCS2 , volume->v_volcharset, volume->v_name, namelen, &vol_uname);
1574
1575     if ( !vol_uname || len <= 0) {
1576         free(vol_mname);
1577         ret = AFPERR_MISC;
1578         goto openvol_err;
1579     }
1580         
1581     if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
1582         free(vol_mname);
1583         free(vol_uname);
1584         LOG(log_error, logtype_afpd, "afp_openvol: malloc: %s", strerror(errno) );
1585         ret = AFPERR_MISC;
1586         goto openvol_err;
1587     }
1588     free(vol_mname);
1589     free(vol_uname);
1590
1591     dir->d_did = DIRDID_ROOT;
1592     dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
1593     volume->v_dir = volume->v_root = dir;
1594     volume->v_flags |= AFPVOL_OPEN;
1595     volume->v_cdb = NULL;  
1596
1597     if (volume->v_root_preexec) {
1598         if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) {
1599             LOG(log_error, logtype_afpd, "afp_openvol: root preexec : %d", ret );
1600             ret = AFPERR_MISC;
1601             goto openvol_err;
1602         }
1603     }
1604
1605 #ifdef FORCE_UIDGID
1606     set_uidgid ( volume );
1607 #endif
1608
1609     if (volume->v_preexec) {
1610         if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) {
1611             LOG(log_error, logtype_afpd, "afp_openvol: preexec : %d", ret );
1612             ret = AFPERR_MISC;
1613             goto openvol_err;
1614         }
1615     }
1616
1617     if ( stat( volume->v_path, &st ) < 0 ) {
1618         ret = AFPERR_PARAM;
1619         goto openvol_err;
1620     }
1621
1622     if ( chdir( volume->v_path ) < 0 ) {
1623         ret = AFPERR_PARAM;
1624         goto openvol_err;
1625     }
1626     curdir = volume->v_dir;
1627     if (volume->v_cnidscheme == NULL) {
1628         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
1629         LOG(log_warning, logtype_afpd, "Warning: No CNID scheme for volume %s. Using default.",
1630                volume->v_path);
1631     }
1632     if (volume->v_dbpath)
1633         volume->v_cdb = cnid_open (volume->v_dbpath, volume->v_umask, volume->v_cnidscheme);
1634     if (volume->v_cdb == NULL)
1635         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, volume->v_cnidscheme);
1636     if (volume->v_cdb == NULL) {
1637         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, DEFAULT_CNID_SCHEME);
1638         LOG(log_error, logtype_afpd, "Error: invalid CNID backend for %s: %s", volume->v_path,
1639             volume->v_cnidscheme);
1640     }
1641     if (volume->v_cdb == NULL) {
1642         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID for %s", volume->v_path);
1643         ret = AFPERR_MISC;
1644         goto openvol_err;
1645     }
1646
1647     /* Codepages */
1648
1649     if (!volume->v_volcodepage)
1650         volume->v_volcodepage = strdup("UTF8");
1651
1652     if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) {
1653         LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage);
1654         ret = AFPERR_MISC;
1655         goto openvol_err;
1656     }
1657
1658     if ( NULL == ( volume->v_vol = find_charset_functions(volume->v_volcodepage)) || volume->v_vol->flags & CHARSET_ICONV ) {
1659         LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage);
1660     }   
1661
1662     if (!volume->v_maccodepage)
1663         volume->v_maccodepage = strdup(obj->options.maccodepage);
1664
1665     if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) {
1666         LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage);
1667         ret = AFPERR_MISC;
1668         goto openvol_err;
1669     }
1670
1671     if ( NULL == ( volume->v_mac = find_charset_functions(volume->v_maccodepage)) || ! (volume->v_mac->flags & CHARSET_CLIENT) ) {
1672         LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage);
1673         ret = AFPERR_MISC;
1674         goto openvol_err;
1675     }   
1676
1677     ret  = stat_vol(bitmap, volume, rbuf, rbuflen);
1678     if (ret == AFP_OK) {
1679
1680         handle_special_folders( volume );
1681
1682         /*
1683          * If you mount a volume twice, the second time the trash appears on
1684          * the desk-top.  That's because the Mac remembers the DID for the
1685          * trash (even for volumes in different zones, on different servers).
1686          * Just so this works better, we prime the DID cache with the trash,
1687          * fixing the trash at DID 17.
1688          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
1689          */
1690         if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1691
1692             /* FIXME find db time stamp */
1693             cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp));
1694         }
1695         else {
1696             p = Trash;
1697             cname( volume, volume->v_dir, &p );
1698         }
1699         return( AFP_OK );
1700     }
1701
1702 openvol_err:
1703     if (volume->v_dir) {
1704         dirfree( volume->v_dir );
1705         volume->v_dir = volume->v_root = NULL;
1706     }
1707
1708     volume->v_flags &= ~AFPVOL_OPEN;
1709     if (volume->v_cdb != NULL) {
1710         cnid_close(volume->v_cdb);
1711         volume->v_cdb = NULL;
1712     }
1713     *rbuflen = 0;
1714     return ret;
1715 }
1716
1717 /* ------------------------- */
1718 static void closevol(struct vol *vol)
1719 {
1720     if (!vol)
1721         return;
1722
1723     dirfree( vol->v_root );
1724     vol->v_dir = NULL;
1725     if (vol->v_cdb != NULL) {
1726         cnid_close(vol->v_cdb);
1727         vol->v_cdb = NULL;
1728     }
1729
1730     if (vol->v_postexec) {
1731         afprun(0, vol->v_postexec, NULL);
1732     }
1733     if (vol->v_root_postexec) {
1734         afprun(1, vol->v_root_postexec, NULL);
1735     }
1736 }
1737
1738 /* ------------------------- */
1739 void close_all_vol(void)
1740 {
1741     struct vol  *ovol;
1742     curdir = NULL;
1743     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
1744         if ( ovol->v_flags & AFPVOL_OPEN ) {
1745             ovol->v_flags &= ~AFPVOL_OPEN;
1746             closevol(ovol);
1747         }
1748     }
1749 }
1750
1751 /* ------------------------- */
1752 int afp_closevol(obj, ibuf, ibuflen, rbuf, rbuflen )
1753 AFPObj      *obj;
1754 char    *ibuf, *rbuf;
1755 int             ibuflen, *rbuflen;
1756 {
1757     struct vol  *vol, *ovol;
1758     u_int16_t   vid;
1759
1760     *rbuflen = 0;
1761     ibuf += 2;
1762     memcpy(&vid, ibuf, sizeof( vid ));
1763     if (NULL == ( vol = getvolbyvid( vid )) ) {
1764         return( AFPERR_PARAM );
1765     }
1766
1767     vol->v_flags &= ~AFPVOL_OPEN;
1768     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
1769         if ( ovol->v_flags & AFPVOL_OPEN ) {
1770             break;
1771         }
1772     }
1773     if ( ovol != NULL ) {
1774         /* Even if chdir fails, we can't say afp_closevol fails. */
1775         if ( chdir( ovol->v_path ) == 0 ) {
1776             curdir = ovol->v_dir;
1777         }
1778     }
1779
1780     closevol(vol);
1781     if (vol->v_deleted) {
1782         showvol(vol->v_name);
1783         volume_free(vol);
1784         volume_unlink(vol);
1785     }
1786     return( AFP_OK );
1787 }
1788
1789 /* ------------------------- */
1790 struct vol *getvolbyvid(const u_int16_t vid )
1791 {
1792     struct vol  *vol;
1793
1794     for ( vol = Volumes; vol; vol = vol->v_next ) {
1795         if ( vid == vol->v_vid ) {
1796             break;
1797         }
1798     }
1799     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1800         return( NULL );
1801     }
1802
1803 #ifdef FORCE_UIDGID
1804     set_uidgid ( vol );
1805 #endif /* FORCE_UIDGID */
1806
1807     return( vol );
1808 }
1809
1810 /* ------------------------ */
1811 static int ext_cmp_key(const void *key, const void *obj)
1812 {
1813     const char          *p = key;
1814     const struct extmap *em = obj;
1815     return strdiacasecmp(p, em->em_ext);
1816 }
1817 struct extmap *getextmap(const char *path)
1818 {
1819     char          *p;
1820     struct extmap *em;
1821
1822     if (NULL == ( p = strrchr( path, '.' )) ) {
1823         return( Defextmap );
1824     }
1825     p++;
1826     if (!*p || !Extmap_cnt) {
1827         return( Defextmap );
1828     }
1829     em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
1830     if (em) {
1831         return( em );
1832     } else {
1833         return( Defextmap );
1834     }
1835 }
1836
1837 /* ------------------------- */
1838 struct extmap *getdefextmap(void)
1839 {
1840     return( Defextmap );
1841 }
1842
1843 /* --------------------------
1844    poll if a volume is changed by other processes.
1845 */
1846 int  pollvoltime(obj)
1847 AFPObj *obj;
1848 {
1849     struct vol       *vol;
1850     struct timeval   tv;
1851     struct stat      st;
1852     
1853     if (!(afp_version > 21 && obj->options.server_notif)) 
1854          return 0;
1855
1856     if ( gettimeofday( &tv, 0 ) < 0 ) 
1857          return 0;
1858
1859     for ( vol = Volumes; vol; vol = vol->v_next ) {
1860         if ( (vol->v_flags & AFPVOL_OPEN)  && vol->v_mtime + 30 < tv.tv_sec) {
1861             if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) {
1862                 vol->v_mtime = st.st_mtime;
1863                 if (!obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED))
1864                     return -1;
1865                 return 1;
1866             }
1867         }
1868     }
1869     return 0;
1870 }
1871
1872 /* ------------------------- */
1873 void setvoltime(obj, vol )
1874 AFPObj *obj;
1875 struct vol      *vol;
1876 {
1877     struct timeval      tv;
1878
1879     /* just looking at vol->v_mtime is broken seriously since updates
1880      * from other users afpd processes never are seen.
1881      * This is not the most elegant solution (a shared memory between
1882      * the afpd processes would come closer)
1883      * [RS] */
1884
1885     if ( gettimeofday( &tv, 0 ) < 0 ) {
1886         LOG(log_error, logtype_afpd, "setvoltime: gettimeofday: %s", strerror(errno) );
1887         return;
1888     }
1889     if( utime( vol->v_path, NULL ) < 0 ) {
1890         /* write of time failed ... probably a read only filesys,
1891          * where no other users can interfere, so there's no issue here
1892          */
1893     }
1894
1895     /* a little granularity */
1896     if (vol->v_mtime < tv.tv_sec) {
1897         vol->v_mtime = tv.tv_sec;
1898 #if 0
1899 /* if 0ed, we're sending too many */
1900         if (afp_version > 21 && obj->options.server_notif) {
1901             obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
1902         }
1903 #endif
1904     }
1905 }
1906
1907 /* ------------------------- */
1908 int afp_getvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1909 AFPObj      *obj;
1910 char    *ibuf, *rbuf;
1911 int             ibuflen, *rbuflen;
1912 {
1913     struct vol  *vol;
1914     u_int16_t   vid, bitmap;
1915
1916     ibuf += 2;
1917     memcpy(&vid, ibuf, sizeof( vid ));
1918     ibuf += sizeof( vid );
1919     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1920     bitmap = ntohs( bitmap );
1921
1922     if (NULL == ( vol = getvolbyvid( vid )) ) {
1923         *rbuflen = 0;
1924         return( AFPERR_PARAM );
1925     }
1926
1927     return stat_vol(bitmap, vol, rbuf, rbuflen);
1928 }
1929
1930 /* ------------------------- */
1931 int afp_setvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1932 AFPObj      *obj;
1933 char    *ibuf, *rbuf;
1934 int             ibuflen, *rbuflen;
1935 {
1936     struct adouble ad;
1937     struct vol  *vol;
1938     u_int16_t   vid, bitmap;
1939     u_int32_t   aint;
1940
1941     ibuf += 2;
1942     *rbuflen = 0;
1943
1944     memcpy(&vid, ibuf, sizeof( vid ));
1945     ibuf += sizeof( vid );
1946     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1947     bitmap = ntohs( bitmap );
1948     ibuf += sizeof(bitmap);
1949
1950     if (( vol = getvolbyvid( vid )) == NULL ) {
1951         return( AFPERR_PARAM );
1952     }
1953
1954     if (vol->v_flags & AFPVOL_RO)
1955         return AFPERR_VLOCK;
1956
1957     /* we can only set the backup date. */
1958     if (bitmap != (1 << VOLPBIT_BDATE))
1959         return AFPERR_BITMAP;
1960
1961     ad_init(&ad, vol->v_adouble);
1962     if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR,
1963                   0666, &ad) < 0 ) {
1964         if (errno == EROFS)
1965             return AFPERR_VLOCK;
1966
1967         return AFPERR_ACCESS;
1968     }
1969
1970     memcpy(&aint, ibuf, sizeof(aint));
1971     ad_setdate(&ad, AD_DATE_BACKUP, aint);
1972     ad_flush(&ad, ADFLAGS_HF);
1973     ad_close(&ad, ADFLAGS_HF);
1974     return( AFP_OK );
1975 }
1976
1977 /* ------------------------- */
1978 int wincheck(const struct vol *vol, const char *path)
1979 {
1980     int len;
1981
1982     if (!(vol->v_flags & AFPVOL_MSWINDOWS))
1983         return 1;
1984
1985     /* empty paths are not allowed */
1986     if ((len = strlen(path)) == 0)
1987         return 0;
1988
1989     /* leading or trailing whitespaces are not allowed, carriage returns
1990      * and probably other whitespace is okay, tabs are not allowed
1991      */
1992     if ((path[0] == ' ') || (path[len-1] == ' '))
1993         return 0;
1994
1995     /* certain characters are not allowed */
1996     if (strpbrk(path, MSWINDOWS_BADCHARS))
1997         return 0;
1998
1999     /* everything else is okay */
2000     return 1;
2001 }
2002
2003
2004 /*
2005  * precreate a folder 
2006  * this is only intended for folders in the volume root 
2007  * It will *not* work if the folder name contains extended characters 
2008  */
2009 static int create_special_folder (const struct vol *vol, const struct _special_folder *folder)
2010 {
2011         char            *p;
2012         struct adouble  ad;
2013         u_int16_t       attr;
2014         struct stat     st;
2015         int             ret;
2016
2017
2018         p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2);
2019         if ( p == NULL) {
2020                 LOG(log_error, logtype_afpd,"malloc failed");
2021                 exit (-1);
2022         }
2023         strcpy(p, vol->v_path);
2024         strcat(p, "/");
2025         strcat(p, folder->name);
2026
2027         if ( (ret = stat( p, &st )) < 0 ) {
2028                 if (folder->precreate) {
2029                     if (ad_mkdir(p, folder->mode & ~vol->v_umask)) {
2030                         LOG(log_debug, logtype_afpd,"Creating '%s' failed", folder->name);
2031                         free(p);
2032                         return -1;
2033                     }
2034                     ret = 0;
2035                 }
2036         }
2037
2038         if ( !ret && folder->hide) {
2039                 /* Hide it */
2040                 ad_init(&ad, vol->v_adouble);
2041                 if (ad_open( p, vol_noadouble(vol) | ADFLAGS_HF|ADFLAGS_DIR,
2042                         O_RDWR|O_CREAT, 0666, &ad) < 0) {
2043                         free (p);
2044                         return (-1);
2045                 }
2046                 if ((ad_get_HF_flags( &ad ) & O_CREAT) ) {
2047                         ad_setentrylen( &ad, ADEID_NAME, strlen(folder->name));
2048                         memcpy(ad_entry( &ad, ADEID_NAME ), folder->name,
2049                         ad_getentrylen( &ad, ADEID_NAME ));
2050                 }
2051  
2052                 ad_getattr(&ad, &attr);
2053                 attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
2054                 ad_setattr(&ad, attr);
2055     
2056                 ad_flush( &ad, ADFLAGS_HF );
2057                 ad_close( &ad, ADFLAGS_HF );
2058         }
2059         free(p);
2060         return 0;
2061 }
2062
2063 static void handle_special_folders (const struct vol * vol)
2064 {
2065         const _special_folder *p = &special_folders[0];
2066
2067         if (vol->v_flags & AFPVOL_RO)
2068                 return;
2069
2070         for (; p->name != NULL; p++) {
2071                 create_special_folder (vol, p);
2072         }
2073 }
2074