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