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