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