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