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