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