]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
fix a small leak.
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * $Id: volume.c,v 1.51.2.7.2.30 2004-05-11 08:30:07 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 (EXITERR_CONF);
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;
1464     size_t              len;
1465
1466     load_volumes(obj);
1467
1468     data = rbuf + 5;
1469     for ( vcnt = 0, volume = Volumes; volume; volume = volume->v_next ) {
1470         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
1471             if ( stat( volume->v_path, &st ) < 0 ) {
1472                 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s",
1473                         volume->v_path, strerror(errno) );
1474                 continue;               /* can't access directory */
1475             }
1476             if (!S_ISDIR(st.st_mode)) {
1477                 continue;               /* not a dir */
1478             }
1479         }
1480         if (volume->v_hide) {
1481             continue;           /* config file changed but the volume was mounted */
1482         }
1483         len = ucs2_to_charset_allocate((utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset),
1484                                         &namebuf, volume->v_name);
1485         if (len == (size_t)-1)
1486                 continue;
1487
1488         /* set password bit if there's a volume password */
1489         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
1490
1491         /* Apple 2 clients running ProDOS-8 expect one volume to have
1492            bit 0 of this byte set.  They will not recognize anything
1493            on the server unless this is the case.  I have not
1494            completely worked this out, but it's related to booting
1495            from the server.  Support for that function is a ways
1496            off.. <shirsch@ibm.net> */
1497         *data |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
1498         *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
1499         *data++ = len;
1500         memcpy(data, namebuf, len );
1501         data += len;
1502         free(namebuf);
1503         vcnt++;
1504     }
1505
1506     *rbuflen = data - rbuf;
1507     data = rbuf;
1508     if ( gettimeofday( &tv, 0 ) < 0 ) {
1509         LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
1510         *rbuflen = 0;
1511         return AFPERR_PARAM;
1512     }
1513     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
1514     memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
1515     data += sizeof( u_int32_t);
1516     *data = vcnt;
1517     return( AFP_OK );
1518 }
1519
1520 /* ------------------------- 
1521  * we are the user here
1522 */
1523 int afp_openvol(obj, ibuf, ibuflen, rbuf, rbuflen )
1524 AFPObj      *obj;
1525 char    *ibuf, *rbuf;
1526 int             ibuflen, *rbuflen;
1527 {
1528     struct stat st;
1529     char        *volname;
1530     char        *p;
1531     struct vol  *volume;
1532     struct dir  *dir;
1533     int         len, ret;
1534     size_t      namelen;
1535     u_int16_t   bitmap;
1536     char        path[ MAXPATHLEN + 1];
1537     char        *vol_uname;
1538     char        *vol_mname;
1539
1540     ibuf += 2;
1541     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1542     bitmap = ntohs( bitmap );
1543     ibuf += sizeof( bitmap );
1544     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
1545         *rbuflen = 0;
1546         return AFPERR_BITMAP;
1547     }
1548
1549     len = (unsigned char)*ibuf++;
1550     volname = obj->oldtmp;
1551     namelen = convert_string( (utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset), CH_UCS2,
1552                               ibuf, len, volname, sizeof(obj->oldtmp));
1553     if ( namelen <= 0){
1554         *rbuflen = 0;
1555         return AFPERR_PARAM;
1556     }
1557
1558     ibuf += len;
1559     if ((len + 1) & 1) /* pad to an even boundary */
1560         ibuf++;
1561
1562     load_volumes(obj);
1563
1564     for ( volume = Volumes; volume; volume = volume->v_next ) {
1565         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
1566             break;
1567         }
1568     }
1569
1570     if ( volume == NULL ) {
1571         *rbuflen = 0;
1572         return AFPERR_PARAM;
1573     }
1574
1575     /* check for a volume password */
1576     if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
1577         *rbuflen = 0;
1578         return AFPERR_ACCESS;
1579     }
1580
1581     if (( volume->v_flags & AFPVOL_OPEN  ) ) {
1582         /* the volume is already open */
1583 #ifdef FORCE_UIDGID
1584         set_uidgid ( volume );
1585 #endif
1586         return stat_vol(bitmap, volume, rbuf, rbuflen);
1587     }
1588
1589     /* initialize volume variables
1590      * FIXME file size
1591     */
1592     if (afp_version >= 30) {
1593         volume->max_filename = 255;
1594     }
1595     else {
1596         volume->max_filename = MACFILELEN;
1597     }
1598
1599     volume->v_dir = volume->v_root = NULL;
1600
1601     volume->v_flags |= AFPVOL_OPEN;
1602     volume->v_cdb = NULL;  
1603
1604     if (volume->v_root_preexec) {
1605         if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) {
1606             LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret );
1607             ret = AFPERR_MISC;
1608             goto openvol_err;
1609         }
1610     }
1611
1612 #ifdef FORCE_UIDGID
1613     set_uidgid ( volume );
1614 #endif
1615
1616     if (volume->v_preexec) {
1617         if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) {
1618             LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret );
1619             ret = AFPERR_MISC;
1620             goto openvol_err;
1621         }
1622     }
1623
1624     if ( stat( volume->v_path, &st ) < 0 ) {
1625         ret = AFPERR_PARAM;
1626         goto openvol_err;
1627     }
1628
1629     if ( chdir( volume->v_path ) < 0 ) {
1630         ret = AFPERR_PARAM;
1631         goto openvol_err;
1632     }
1633
1634     len = convert_string_allocate( CH_UCS2, (utf8_encoding()?CH_UTF8_MAC:obj->options.maccharset),
1635                                        volume->v_name, namelen, &vol_mname);
1636     if ( !vol_mname || len <= 0) {
1637         ret = AFPERR_MISC;
1638         goto openvol_err;
1639     }
1640     
1641     if ( NULL == getcwd(path, MAXPATHLEN)) {
1642         /* shouldn't be fatal but it will fail later */
1643         LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
1644         ret = AFPERR_MISC;
1645         goto openvol_err;
1646     }        
1647     
1648     if ((vol_uname = strrchr(path, '/')) == NULL)
1649          vol_uname = path;
1650     else if (*(vol_uname + 1) != '\0')
1651          vol_uname++;
1652         
1653     if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
1654         free(vol_mname);
1655         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
1656         ret = AFPERR_MISC;
1657         goto openvol_err;
1658     }
1659     free(vol_mname);
1660
1661     dir->d_did = DIRDID_ROOT;
1662     dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
1663     volume->v_dir = volume->v_root = dir;
1664
1665     curdir = volume->v_dir;
1666     if (volume->v_cnidscheme == NULL) {
1667         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
1668         LOG(log_warning, logtype_afpd, "Warning: No CNID scheme for volume %s. Using default.",
1669                volume->v_path);
1670     }
1671     if (volume->v_dbpath)
1672         volume->v_cdb = cnid_open (volume->v_dbpath, volume->v_umask, volume->v_cnidscheme, (volume->v_flags & AFPVOL_NODEV));
1673     else
1674         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, volume->v_cnidscheme, (volume->v_flags & AFPVOL_NODEV));
1675     if (volume->v_cdb == NULL) {
1676         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s", 
1677             volume->v_path, volume->v_cnidscheme);
1678         ret = AFPERR_MISC;
1679         goto openvol_err;
1680     }
1681
1682     /* Codepages */
1683
1684     if (!volume->v_volcodepage)
1685         volume->v_volcodepage = strdup("UTF8");
1686
1687     if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) {
1688         LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage);
1689         ret = AFPERR_MISC;
1690         goto openvol_err;
1691     }
1692
1693     if ( NULL == ( volume->v_vol = find_charset_functions(volume->v_volcodepage)) || volume->v_vol->flags & CHARSET_ICONV ) {
1694         LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage);
1695     }   
1696
1697     if (!volume->v_maccodepage)
1698         volume->v_maccodepage = strdup(obj->options.maccodepage);
1699
1700     if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) {
1701         LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage);
1702         ret = AFPERR_MISC;
1703         goto openvol_err;
1704     }
1705
1706     if ( NULL == ( volume->v_mac = find_charset_functions(volume->v_maccodepage)) || ! (volume->v_mac->flags & CHARSET_CLIENT) ) {
1707         LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage);
1708         ret = AFPERR_MISC;
1709         goto openvol_err;
1710     }   
1711
1712     ret  = stat_vol(bitmap, volume, rbuf, rbuflen);
1713     if (ret == AFP_OK) {
1714
1715         handle_special_folders( volume );
1716
1717         /*
1718          * If you mount a volume twice, the second time the trash appears on
1719          * the desk-top.  That's because the Mac remembers the DID for the
1720          * trash (even for volumes in different zones, on different servers).
1721          * Just so this works better, we prime the DID cache with the trash,
1722          * fixing the trash at DID 17.
1723          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
1724          */
1725         if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1726
1727             /* FIXME find db time stamp */
1728             if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) {
1729                 LOG (log_error, logtype_afpd, 
1730                       "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend",
1731                       volume->v_path);
1732                 goto openvol_err;
1733             }
1734         }
1735         else {
1736             p = Trash;
1737             cname( volume, volume->v_dir, &p );
1738         }
1739         return( AFP_OK );
1740     }
1741
1742 openvol_err:
1743     if (volume->v_dir) {
1744         dirfree( volume->v_dir );
1745         volume->v_dir = volume->v_root = NULL;
1746     }
1747
1748     volume->v_flags &= ~AFPVOL_OPEN;
1749     if (volume->v_cdb != NULL) {
1750         cnid_close(volume->v_cdb);
1751         volume->v_cdb = NULL;
1752     }
1753     *rbuflen = 0;
1754     return ret;
1755 }
1756
1757 /* ------------------------- */
1758 static void closevol(struct vol *vol)
1759 {
1760     if (!vol)
1761         return;
1762
1763     dirfree( vol->v_root );
1764     vol->v_dir = NULL;
1765     if (vol->v_cdb != NULL) {
1766         cnid_close(vol->v_cdb);
1767         vol->v_cdb = NULL;
1768     }
1769
1770     if (vol->v_postexec) {
1771         afprun(0, vol->v_postexec, NULL);
1772     }
1773     if (vol->v_root_postexec) {
1774         afprun(1, vol->v_root_postexec, NULL);
1775     }
1776 }
1777
1778 /* ------------------------- */
1779 void close_all_vol(void)
1780 {
1781     struct vol  *ovol;
1782     curdir = NULL;
1783     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
1784         if ( ovol->v_flags & AFPVOL_OPEN ) {
1785             ovol->v_flags &= ~AFPVOL_OPEN;
1786             closevol(ovol);
1787         }
1788     }
1789 }
1790
1791 /* ------------------------- */
1792 int afp_closevol(obj, ibuf, ibuflen, rbuf, rbuflen )
1793 AFPObj      *obj;
1794 char    *ibuf, *rbuf;
1795 int             ibuflen, *rbuflen;
1796 {
1797     struct vol  *vol, *ovol;
1798     u_int16_t   vid;
1799
1800     *rbuflen = 0;
1801     ibuf += 2;
1802     memcpy(&vid, ibuf, sizeof( vid ));
1803     if (NULL == ( vol = getvolbyvid( vid )) ) {
1804         return( AFPERR_PARAM );
1805     }
1806
1807     vol->v_flags &= ~AFPVOL_OPEN;
1808     for ( ovol = Volumes; ovol; ovol = ovol->v_next ) {
1809         if ( ovol->v_flags & AFPVOL_OPEN ) {
1810             break;
1811         }
1812     }
1813     if ( ovol != NULL ) {
1814         /* Even if chdir fails, we can't say afp_closevol fails. */
1815         if ( chdir( ovol->v_path ) == 0 ) {
1816             curdir = ovol->v_dir;
1817         }
1818     }
1819
1820     closevol(vol);
1821     if (vol->v_deleted) {
1822         showvol(vol->v_name);
1823         volume_free(vol);
1824         volume_unlink(vol);
1825         free(vol);
1826     }
1827     return( AFP_OK );
1828 }
1829
1830 /* ------------------------- */
1831 struct vol *getvolbyvid(const u_int16_t vid )
1832 {
1833     struct vol  *vol;
1834
1835     for ( vol = Volumes; vol; vol = vol->v_next ) {
1836         if ( vid == vol->v_vid ) {
1837             break;
1838         }
1839     }
1840     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1841         return( NULL );
1842     }
1843
1844 #ifdef FORCE_UIDGID
1845     set_uidgid ( vol );
1846 #endif /* FORCE_UIDGID */
1847
1848     return( vol );
1849 }
1850
1851 /* ------------------------ */
1852 static int ext_cmp_key(const void *key, const void *obj)
1853 {
1854     const char          *p = key;
1855     const struct extmap *em = obj;
1856     return strdiacasecmp(p, em->em_ext);
1857 }
1858 struct extmap *getextmap(const char *path)
1859 {
1860     char          *p;
1861     struct extmap *em;
1862
1863     if (NULL == ( p = strrchr( path, '.' )) ) {
1864         return( Defextmap );
1865     }
1866     p++;
1867     if (!*p || !Extmap_cnt) {
1868         return( Defextmap );
1869     }
1870     em = bsearch(p, Extmap, Extmap_cnt, sizeof(struct extmap), ext_cmp_key);
1871     if (em) {
1872         return( em );
1873     } else {
1874         return( Defextmap );
1875     }
1876 }
1877
1878 /* ------------------------- */
1879 struct extmap *getdefextmap(void)
1880 {
1881     return( Defextmap );
1882 }
1883
1884 /* --------------------------
1885    poll if a volume is changed by other processes.
1886 */
1887 int  pollvoltime(obj)
1888 AFPObj *obj;
1889 {
1890     struct vol       *vol;
1891     struct timeval   tv;
1892     struct stat      st;
1893     
1894     if (!(afp_version > 21 && obj->options.server_notif)) 
1895          return 0;
1896
1897     if ( gettimeofday( &tv, 0 ) < 0 ) 
1898          return 0;
1899
1900     for ( vol = Volumes; vol; vol = vol->v_next ) {
1901         if ( (vol->v_flags & AFPVOL_OPEN)  && vol->v_mtime + 30 < tv.tv_sec) {
1902             if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) {
1903                 vol->v_mtime = st.st_mtime;
1904                 if (!obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED))
1905                     return -1;
1906                 return 1;
1907             }
1908         }
1909     }
1910     return 0;
1911 }
1912
1913 /* ------------------------- */
1914 void setvoltime(obj, vol )
1915 AFPObj *obj;
1916 struct vol      *vol;
1917 {
1918     struct timeval      tv;
1919
1920     /* just looking at vol->v_mtime is broken seriously since updates
1921      * from other users afpd processes never are seen.
1922      * This is not the most elegant solution (a shared memory between
1923      * the afpd processes would come closer)
1924      * [RS] */
1925
1926     if ( gettimeofday( &tv, 0 ) < 0 ) {
1927         LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
1928         return;
1929     }
1930     if( utime( vol->v_path, NULL ) < 0 ) {
1931         /* write of time failed ... probably a read only filesys,
1932          * where no other users can interfere, so there's no issue here
1933          */
1934     }
1935
1936     /* a little granularity */
1937     if (vol->v_mtime < tv.tv_sec) {
1938         vol->v_mtime = tv.tv_sec;
1939         /* or finder doesn't update free space */
1940         if (afp_version > 21 && obj->options.server_notif) {
1941             obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
1942         }
1943     }
1944 }
1945
1946 /* ------------------------- */
1947 int afp_getvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1948 AFPObj      *obj;
1949 char    *ibuf, *rbuf;
1950 int             ibuflen, *rbuflen;
1951 {
1952     struct vol  *vol;
1953     u_int16_t   vid, bitmap;
1954
1955     ibuf += 2;
1956     memcpy(&vid, ibuf, sizeof( vid ));
1957     ibuf += sizeof( vid );
1958     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1959     bitmap = ntohs( bitmap );
1960
1961     if (NULL == ( vol = getvolbyvid( vid )) ) {
1962         *rbuflen = 0;
1963         return( AFPERR_PARAM );
1964     }
1965
1966     return stat_vol(bitmap, vol, rbuf, rbuflen);
1967 }
1968
1969 /* ------------------------- */
1970 int afp_setvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1971 AFPObj      *obj;
1972 char    *ibuf, *rbuf;
1973 int             ibuflen, *rbuflen;
1974 {
1975     struct adouble ad;
1976     struct vol  *vol;
1977     u_int16_t   vid, bitmap;
1978     u_int32_t   aint;
1979
1980     ibuf += 2;
1981     *rbuflen = 0;
1982
1983     memcpy(&vid, ibuf, sizeof( vid ));
1984     ibuf += sizeof( vid );
1985     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1986     bitmap = ntohs( bitmap );
1987     ibuf += sizeof(bitmap);
1988
1989     if (( vol = getvolbyvid( vid )) == NULL ) {
1990         return( AFPERR_PARAM );
1991     }
1992
1993     if (vol->v_flags & AFPVOL_RO)
1994         return AFPERR_VLOCK;
1995
1996     /* we can only set the backup date. */
1997     if (bitmap != (1 << VOLPBIT_BDATE))
1998         return AFPERR_BITMAP;
1999
2000     ad_init(&ad, vol->v_adouble);
2001     if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR,
2002                   0666, &ad) < 0 ) {
2003         if (errno == EROFS)
2004             return AFPERR_VLOCK;
2005
2006         return AFPERR_ACCESS;
2007     }
2008
2009     memcpy(&aint, ibuf, sizeof(aint));
2010     ad_setdate(&ad, AD_DATE_BACKUP, aint);
2011     ad_flush(&ad, ADFLAGS_HF);
2012     ad_close(&ad, ADFLAGS_HF);
2013     return( AFP_OK );
2014 }
2015
2016 /* ------------------------- */
2017 int wincheck(const struct vol *vol, const char *path)
2018 {
2019     int len;
2020
2021     if (!(vol->v_flags & AFPVOL_MSWINDOWS))
2022         return 1;
2023
2024     /* empty paths are not allowed */
2025     if ((len = strlen(path)) == 0)
2026         return 0;
2027
2028     /* leading or trailing whitespaces are not allowed, carriage returns
2029      * and probably other whitespace is okay, tabs are not allowed
2030      */
2031     if ((path[0] == ' ') || (path[len-1] == ' '))
2032         return 0;
2033
2034     /* certain characters are not allowed */
2035     if (strpbrk(path, MSWINDOWS_BADCHARS))
2036         return 0;
2037
2038     /* everything else is okay */
2039     return 1;
2040 }
2041
2042
2043 /*
2044  * precreate a folder 
2045  * this is only intended for folders in the volume root 
2046  * It will *not* work if the folder name contains extended characters 
2047  */
2048 static int create_special_folder (const struct vol *vol, const struct _special_folder *folder)
2049 {
2050         char            *p,*q,*r;
2051         struct adouble  ad;
2052         u_int16_t       attr;
2053         struct stat     st;
2054         int             ret;
2055
2056
2057         p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2);
2058         if ( p == NULL) {
2059                 LOG(log_error, logtype_afpd,"malloc failed");
2060                 exit (EXITERR_SYS);
2061         }
2062
2063         q=strdup(folder->name);
2064         if ( q == NULL) {
2065                 LOG(log_error, logtype_afpd,"malloc failed");
2066                 exit (EXITERR_SYS);
2067         }
2068
2069         strcpy(p, vol->v_path);
2070         strcat(p, "/");
2071
2072         r=q;
2073         while (*r) {
2074                 if ((vol->v_casefold & AFPVOL_MTOUUPPER))
2075                         *r=toupper(*r);
2076                 else if ((vol->v_casefold & AFPVOL_MTOULOWER))
2077                         *r=tolower(*r);
2078                 r++;
2079         }
2080         strcat(p, q);
2081
2082         if ( (ret = stat( p, &st )) < 0 ) {
2083                 if (folder->precreate) {
2084                     if (ad_mkdir(p, folder->mode)) {
2085                         LOG(log_debug, logtype_afpd,"Creating '%s' failed in %s: %s", p, vol->v_path, strerror(errno));
2086                         free(p);
2087                         free(q);
2088                         return -1;
2089                     }
2090                     ret = 0;
2091                 }
2092         }
2093
2094         if ( !ret && folder->hide) {
2095                 /* Hide it */
2096                 ad_init(&ad, vol->v_adouble);
2097                 if (ad_open( p, vol_noadouble(vol) | ADFLAGS_HF|ADFLAGS_DIR,
2098                         O_RDWR|O_CREAT, 0666, &ad) < 0) {
2099                         free (p);
2100                         free(q);
2101                         return (-1);
2102                 }
2103                 if ((ad_get_HF_flags( &ad ) & O_CREAT) ) {
2104                     if (ad_getentryoff(&ad, ADEID_NAME)) {
2105                         ad_setentrylen( &ad, ADEID_NAME, strlen(folder->name));
2106                         memcpy(ad_entry( &ad, ADEID_NAME ), folder->name,
2107                                ad_getentrylen( &ad, ADEID_NAME ));
2108                     }
2109                 }
2110  
2111                 ad_getattr(&ad, &attr);
2112                 attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
2113                 ad_setattr(&ad, attr);
2114 #if 0           
2115                 /* do the same with the finder info */
2116                 if (ad_entry(&ad, ADEID_FINDERI)) {
2117                         memcpy(&attr, ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, sizeof(attr));
2118                         attr   |= htons(FINDERINFO_INVISIBLE);
2119                         memcpy(ad_entry(&ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,&attr, sizeof(attr));
2120                 }
2121 #endif    
2122                 ad_flush( &ad, ADFLAGS_HF );
2123                 ad_close( &ad, ADFLAGS_HF );
2124         }
2125         free(p);
2126         free(q);
2127         return 0;
2128 }
2129
2130 static void handle_special_folders (const struct vol * vol)
2131 {
2132         const _special_folder *p = &special_folders[0];
2133
2134         if (vol->v_flags & AFPVOL_RO)
2135                 return;
2136
2137         for (; p->name != NULL; p++) {
2138                 create_special_folder (vol, p);
2139         }
2140 }
2141