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