]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
setvoltime modified for concurrent multiuser update of volumes
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
10 #include <sys/time.h>
11 #include <sys/syslog.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/param.h>
15 #include <netatalk/endian.h>
16 #include <atalk/asp.h>
17 #include <atalk/dsi.h>
18 #include <atalk/adouble.h>
19 #include <atalk/afp.h>
20 #include <atalk/util.h>
21 #include <atalk/cnid.h>
22 #include <dirent.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <utime.h>
31 #include <errno.h>
32
33 #include "directory.h"
34 #include "file.h"
35 #include "volume.h"
36 #include "globals.h"
37 #include "unix.h"
38
39 #ifndef MIN
40 #define MIN(a, b) ((a) < (b) ? (a) : (b))
41 #endif
42
43 #ifndef NO_LARGE_VOL_SUPPORT
44 #if BYTE_ORDER == BIG_ENDIAN
45 #define hton64(x)       (x)
46 #define ntoh64(x)       (x)
47 #else
48 #define hton64(x)       ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
49                          (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
50 #define ntoh64(x)       (hton64(x))
51 #endif
52 #endif
53
54 static struct vol *volumes = NULL;
55 static int              lastvid = 0;
56 #if AD_VERSION == AD_VERSION1
57 static char             *Trash = "\02\024Network Trash Folder";
58 #endif
59 static struct extmap    *extmap = NULL, *defextmap = NULL;
60
61 #define VOLOPT_ALLOW      0  /* user allow list */
62 #define VOLOPT_DENY       1  /* user deny list */
63 #define VOLOPT_RWLIST     2  /* user rw list */
64 #define VOLOPT_ROLIST     3  /* user ro list */
65 #define VOLOPT_CODEPAGE   4  /* codepage */
66 #define VOLOPT_PASSWORD   5  /* volume password */
67 #define VOLOPT_CASEFOLD   6  /* character case mangling */
68 #define VOLOPT_FLAGS      7  /* various flags */
69 #define VOLOPT_DBPATH     8  /* path to database */
70 #define VOLOPT_MAPCHARS   9  /* does mtou and utom mappings. syntax:
71                                 m and u can be double-byte hex
72                                 strings if necessary.
73                                 m=u -> map both ways
74                                 m>u -> map m to u
75                                 m<u -> map u to m
76                                 !u  -> make u illegal always
77                                 ~u  -> make u illegal only as the first
78                                        part of a double-byte character.
79                              */
80 #define VOLOPT_MAX        9
81 #define VOLOPT_NUM        (VOLOPT_MAX + 1)
82
83 #define VOLPASSLEN  8
84 #define VOLOPT_DEFAULT     ":DEFAULT:"
85 #define VOLOPT_DEFAULT_LEN 9
86 struct vol_option {
87   char *c_value;
88   int i_value; 
89 };
90
91 static __inline__ void volfree(struct vol_option *options,
92                                const struct vol_option *save)
93 {
94   int i;
95
96   if (save) {
97     for (i = 0; i < VOLOPT_MAX; i++) {
98       if (options[i].c_value && (options[i].c_value != save[i].c_value))
99         free(options[i].c_value);
100     }
101   } else {
102     for (i = 0; i < VOLOPT_MAX; i++) {
103       if (options[i].c_value)
104         free(options[i].c_value);
105     }
106   }
107 }
108
109
110 /* handle variable substitutions. here's what we understand:
111  * $c   -> client ip/appletalk address
112  * $f   -> full name (whatever's in the gecos field)
113  * $g   -> group
114  * $h   -> hostname 
115  * $s   -> server name (hostname if it doesn't exist)
116  * $u   -> username (guest is usually nobody)
117  * $v   -> volume name (ADEID_NAME or basename)
118  * $z   -> zone (may not exist)
119  * $$   -> $
120  */
121 #define is_var(a, b) (strncmp((a), (b), 2) == 0)
122 static void volxlate(AFPObj *obj, char *dest, int destlen, 
123                      char *src, struct passwd *pwd, char *path)
124 {
125   char *p, *q;
126   int len;
127
128   strncpy(dest, src, destlen);
129   if ((p = strchr(src, '$')) == NULL) /* nothing to do */
130     return; 
131
132   /* first part of the path. just forward to the next variable. */
133   len = MIN(p - src, destlen);
134   if (len > 0) {
135     destlen -= len;
136     dest += len;
137   }
138
139   while (p && destlen > 0) {
140     /* now figure out what the variable is */
141     q = NULL;
142     if (is_var(p, "$c")) {
143       if (obj->proto == AFPPROTO_ASP) {
144         ASP asp = obj->handle;
145
146         len = sprintf(dest, "%u.%u", ntohs(asp->asp_sat.sat_addr.s_net),
147                       asp->asp_sat.sat_addr.s_node);
148         dest += len;
149         destlen -= len;
150
151       } else if (obj->proto == AFPPROTO_DSI) {
152         DSI *dsi = obj->handle;
153
154         len = sprintf(dest, "%s:%u", inet_ntoa(dsi->client.sin_addr),
155                       ntohs(dsi->client.sin_port));
156         dest += len;
157         destlen -= len;
158       }
159     } else if (is_var(p, "$f")) {
160       if (q = strchr(pwd->pw_gecos, ','))
161         *q = '\0';
162       q = pwd->pw_gecos;
163     } else if (is_var(p, "$g")) {
164       struct group *grp = getgrgid(pwd->pw_gid);
165       if (grp)
166         q = grp->gr_name;
167     } else if (is_var(p, "$h")) {
168       q = obj->options.hostname;
169     } else if (is_var(p, "$s")) {
170       if (obj->Obj)
171         q = obj->Obj;
172       else if (obj->options.server) {
173         q = obj->options.server;
174       } else
175         q = obj->options.hostname;
176     } else if (is_var(p, "$u")) {
177       q = obj->username;
178     } else if (is_var(p, "$v")) {
179       if (path) {
180         struct adouble ad;
181
182         memset(&ad, 0, sizeof(ad));
183         if (ad_open(path, ADFLAGS_HF, O_RDONLY, 0, &ad) < 0)
184           goto no_volname;
185
186         if ((len = MIN(ad_getentrylen(&ad, ADEID_NAME), destlen)) > 0) {
187           memcpy(dest, ad_entry(&ad, ADEID_NAME), len);
188           ad_close(&ad, ADFLAGS_HF);
189           dest += len;
190           destlen -= len;
191         } else {
192           ad_close(&ad, ADFLAGS_HF);
193 no_volname: /* simple basename */
194           if ((q = strrchr(path, '/')) == NULL)
195             q = path;
196           else if (*(q + 1) != '\0')
197             q++;
198         }
199       }
200     } else if (is_var(p, "$z")) {
201       q = obj->Zone;
202     } else if (is_var(p, "$$")) {
203       q = "$";
204     } else
205       q = p;
206     
207     /* copy the stuff over. if we don't understand something that we
208      * should, just skip it over. */
209     if (q) {
210       len = MIN(p == q ? 2 : strlen(q), destlen);
211       strncpy(dest, q, len);
212       dest += len;
213       destlen -= len;
214     }
215
216     /* stuff up to next $ */
217     src = p + 2;
218     p = strchr(src, '$');
219     len = p ? MIN(p - src, destlen) : destlen;
220     if (len > 0) {
221       strncpy(dest, src, len);
222       dest += len;
223       destlen -= len;
224     }
225   }
226 }
227
228 /* to make sure that val is valid, make sure to select an opt that
229    includes val */
230 #define optionok(buf,opt,val) (strstr((buf),(opt)) && ((val)[1] != '\0'))
231
232 static __inline__ char *get_codepage_path(const char *path, const char *name)
233 {
234   char *page;
235   int len;
236
237   if (path) {
238     page = (char *) malloc((len = strlen(path)) + strlen(name) + 2);
239     if (page) {
240       strcpy(page, path);
241       if (path[len - 1] != '/') /* add a / */
242         strcat(page, "/");
243       strcat(page, name);
244     }
245   } else {
246     page = strdup(name);
247   }
248
249   return page;
250 }
251
252 /* handle all the options. tmp can't be NULL. */
253 static void volset(struct vol_option *options, char *volname, int vlen, 
254                    const char *nlspath, const char *tmp)
255 {
256   char *val;
257
258   val = strchr(tmp, ':');
259   if (optionok(tmp, "allow:", val)) {
260     if (options[VOLOPT_ALLOW].c_value)
261       free(options[VOLOPT_ALLOW].c_value);
262     options[VOLOPT_ALLOW].c_value = strdup(val + 1);
263
264   } else if (optionok(tmp, "deny:", val)) {
265     if (options[VOLOPT_DENY].c_value)
266       free(options[VOLOPT_DENY].c_value);
267     options[VOLOPT_DENY].c_value = strdup(val + 1);
268
269   } else if (optionok(tmp, "rwlist:", val)) {
270     if (options[VOLOPT_RWLIST].c_value)
271       free(options[VOLOPT_RWLIST].c_value);
272     options[VOLOPT_RWLIST].c_value = strdup(val + 1);
273
274   } else if (optionok(tmp, "rolist:", val)) {
275     if (options[VOLOPT_ROLIST].c_value)
276       free(options[VOLOPT_ROLIST].c_value);
277     options[VOLOPT_ROLIST].c_value = strdup(val + 1);
278
279   } else if (optionok(tmp, "codepage:", val)) {
280     if (options[VOLOPT_CODEPAGE].c_value)
281       free(options[VOLOPT_CODEPAGE].c_value);
282     options[VOLOPT_CODEPAGE].c_value = get_codepage_path(nlspath, val + 1);
283
284   } else if (optionok(tmp, "casefold:", val)) {
285     if (strcasecmp(val + 1, "tolower") == 0)
286       options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
287     else if (strcasecmp(val + 1, "toupper") == 0)
288       options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
289     else if (strcasecmp(val + 1, "xlatelower") == 0)
290       options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
291     else if (strcasecmp(val + 1, "xlateupper") == 0)
292       options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
293
294   } else if (optionok(tmp, "options:", val)) {
295     char *p;
296     
297     if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
298       return;
299     
300     while (p) {
301       if (strcasecmp(p, "prodos") == 0)
302         options[VOLOPT_FLAGS].i_value |= AFPVOL_A2VOL;
303       else if (strcasecmp(p, "mswindows") == 0) {
304         options[VOLOPT_FLAGS].i_value |= AFPVOL_MSWINDOWS;
305         if (!options[VOLOPT_CODEPAGE].c_value)
306           options[VOLOPT_CODEPAGE].c_value = 
307             get_codepage_path(nlspath, MSWINDOWS_CODEPAGE);
308
309       } else if (strcasecmp(p, "crlf") == 0)
310         options[VOLOPT_FLAGS].i_value |= AFPVOL_CRLF;
311       else if (strcasecmp(p, "noadouble") == 0)
312         options[VOLOPT_FLAGS].i_value |= AFPVOL_NOADOUBLE;
313       else if (strcasecmp(p, "ro") == 0)
314         options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
315       else if (strcasecmp(p, "nohex") == 0)
316         options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
317       else if (strcasecmp(p, "usedots") == 0)
318         options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
319       else if (strcasecmp(p, "limitsize") == 0)
320         options[VOLOPT_FLAGS].i_value |= AFPVOL_LIMITSIZE;
321
322       p = strtok(NULL, ",");
323     }
324
325 #if AD_VERSION > AD_VERSION1
326   } else if (optionok(tmp, "dbpath:", val)) {
327     if (options[VOLOPT_DBPATH].c_value)
328       free(options[VOLOPT_DBPATH].c_value);
329     
330     options[VOLOPT_DBPATH].c_value = strdup(val + 1);
331 #endif
332   } else if (optionok(tmp, "mapchars:",val)) {
333     if (options[VOLOPT_MAPCHARS].c_value)
334       free(options[VOLOPT_MAPCHARS].c_value);
335     options[VOLOPT_MAPCHARS].c_value = strdup(val + 1);
336
337   } else if (optionok(tmp, "password:", val)) {
338     if (options[VOLOPT_PASSWORD].c_value)
339       free(options[VOLOPT_PASSWORD].c_value);
340     options[VOLOPT_PASSWORD].c_value = strdup(val + 1);
341   } else if (val) {
342     /* ignore unknown options */
343     syslog(LOG_DEBUG, "ignoring unknown volume option: %s", tmp);
344
345   } else {
346     /* we'll assume it's a volume name. */
347     strncpy(volname, tmp, vlen); 
348   }
349 }
350
351 static int creatvol(const char *path, char *name, struct vol_option *options)
352 {
353     struct vol  *volume;
354     int         vlen;
355
356     if ( name == NULL || *name == '\0' ) {
357         if ((name = strrchr( path, '/' )) == NULL) {
358             return -1;  /* Obviously not a fully qualified path */
359         }
360
361         /* if you wish to share /, you need to specify a name. */
362         if (*++name == '\0')   
363           return -1; 
364     }
365
366     for ( volume = volumes; volume; volume = volume->v_next ) {
367         if ( strcasecmp( volume->v_name, name ) == 0 ) {
368             return -1;  /* Won't be able to access it, anyway... */
369         }
370     }
371
372     vlen = strlen( name );
373     if ( vlen > AFPVOL_NAMELEN ) {
374         vlen = AFPVOL_NAMELEN;
375         name[AFPVOL_NAMELEN] = '\0';
376     }
377
378     if (( volume =
379             (struct vol *)calloc(1, sizeof( struct vol ))) == NULL ) {
380         syslog( LOG_ERR, "creatvol: malloc: %m" );
381         return -1;
382     }
383     if (( volume->v_name =
384             (char *)malloc( vlen + 1 )) == NULL ) {
385         syslog( LOG_ERR, "creatvol: malloc: %m" );
386         free(volume);
387         return -1;
388     }
389     if (( volume->v_path =
390             (char *)malloc( strlen( path ) + 1 )) == NULL ) {
391         syslog( LOG_ERR, "creatvol: malloc: %m" );
392         free(volume->v_name);
393         free(volume);
394         return -1;
395     }
396
397     strcpy( volume->v_name, name);
398     strcpy( volume->v_path, path );
399
400 #ifdef __svr4__
401     volume->v_qfd = -1;
402 #endif
403     volume->v_vid = lastvid++;
404     volume->v_lastdid = 3;
405
406     /* handle options */
407     if (options) {
408       /* should we casefold? */
409       volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
410
411       /* shift in some flags */
412       volume->v_flags = options[VOLOPT_FLAGS].i_value;
413
414       /* read in the code pages */
415       if (options[VOLOPT_CODEPAGE].c_value)
416         codepage_read(volume, options[VOLOPT_CODEPAGE].c_value);
417
418       if (options[VOLOPT_PASSWORD].c_value) 
419         volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
420
421 #if AD_VERSION > AD_VERSION1
422       if (options[VOLOPT_DBPATH].c_value)
423         volume->v_dbpath = strdup(options[VOLOPT_DBPATH].c_value);
424 #endif
425     }
426
427     volume->v_next = volumes;
428     volumes = volume;
429     return 0;
430 }
431
432 static char *myfgets( buf, size, fp )
433     char        *buf;
434     int         size;
435     FILE        *fp;
436 {
437     char        *p;
438     int         c;
439
440     p = buf;
441     while ((( c = getc( fp )) != EOF ) && ( size > 0 )) {
442         if ( c == '\n' || c == '\r' ) {
443             *p++ = '\n';
444             break;
445         } else {
446             *p++ = c;
447         }
448         size--;
449     }
450
451     if ( p == buf ) {
452         return( NULL );
453     } else {
454         *p = '\0';
455         return( buf );
456     }
457 }
458
459
460 /* check access list. this function wants something of the following
461  * form:
462  *        @group,name,name2,@group2,name3
463  *
464  * a NULL argument allows everybody to have access.
465  * we return three things:
466  *     -1: no list
467  *      0: list exists, but name isn't in it
468  *      1: in list
469  */
470 static int accessvol(args, name)
471     const char *args;
472     const char *name;
473 {
474     char buf[MAXPATHLEN + 1], *p;
475     struct group *gr;
476
477     if (!args)
478       return -1;
479
480     strncpy(buf, args, sizeof(buf));
481     if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
482       return -1;
483
484     while (p) {
485       if (*p == '@') { /* it's a group */
486         if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
487             return 1;
488       } else if (strcmp(p, name) == 0) /* it's a user name */
489         return 1;
490       p = strtok(NULL, ",");
491     }
492
493     return 0;
494 }
495
496 static void setextmap( ext, type, creator, user)
497     char                *ext, *type, *creator;
498     int                 user;
499 {
500     struct extmap       *em;
501
502     for ( em = extmap; em; em = em->em_next ) {
503         if ( strdiacasecmp( em->em_ext, ext ) == 0 ) {
504             break;
505         }
506     }
507
508     if ( em == NULL ) {
509         if (( em =
510                 (struct extmap *)malloc( sizeof( struct extmap ))) == NULL ) {
511             syslog( LOG_ERR, "setextmap: malloc: %m" );
512             return;
513         }
514         em->em_next = extmap;
515         extmap = em;
516     } else if ( !user ) {
517         return;
518     }
519
520     strcpy( em->em_ext, ext );
521
522     if ( *type == '\0' ) {
523         memcpy(em->em_type, "????", sizeof( em->em_type ));
524     } else {
525         memcpy(em->em_type, type, sizeof( em->em_type ));
526     }
527     if ( *creator == '\0' ) {
528         memcpy(em->em_creator, "UNIX", sizeof( em->em_creator ));
529     } else {
530         memcpy(em->em_creator, creator, sizeof( em->em_creator ));
531     }
532
533     if ( strcmp( ext, "." ) == 0 ) {
534         defextmap = em;
535     }
536 }
537
538 /*
539  * Read a volume configuration file and add the volumes contained within to
540  * the global volume list.  If p2 is non-NULL, the file that is opened is
541  * p1/p2
542  *
543  * Lines that begin with # and blank lines are ignored.
544  * Volume lines are of the form:
545  *              <unix path> [<volume name>] [allow:<user>,<@group>,...] \
546  *                           [codepage:<file>] [casefold:<num>]
547  *              <extension> TYPE [CREATOR]
548  */
549 static int readvolfile(obj, p1, p2, user, pwent)
550     AFPObj      *obj;
551     char        *p1, *p2;
552     int         user;
553     struct passwd *pwent;
554 {
555     FILE                *fp;
556     char                path[ MAXPATHLEN + 1], tmp[ MAXPATHLEN + 1],
557                         volname[ AFPVOL_NAMELEN + 1 ], buf[ BUFSIZ ],
558                         type[ 5 ], creator[ 5 ];
559     char                *u, *p;
560     struct passwd       *pw;
561     struct vol_option   options[VOLOPT_NUM], save_options[VOLOPT_NUM];
562     int                 i;
563
564     if (!p1)
565         return -1;
566
567     strcpy( path, p1 );
568     if ( p2 != NULL ) {
569         strcat( path, "/" );
570         strcat( path, p2 );
571     }
572
573     if (( fp = fopen( path, "r" )) == NULL ) {
574         return( -1 );
575     }
576     
577     memset(save_options, 0, sizeof(save_options));
578     while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
579         initline( strlen( buf ), buf );
580         parseline( sizeof( path ) - 1, path );
581         switch ( *path ) {
582         case '\0' :
583         case '#' :
584             continue;
585
586         case ':':
587           /* change the default options for this file */
588           if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
589             *tmp = '\0';
590             for (i = 0; i < VOLOPT_NUM; i++) {
591               if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1, 
592                              path + VOLOPT_DEFAULT_LEN) < 0)
593                 break;
594               volset(save_options, tmp, sizeof(tmp) - 1, 
595                      obj->options.nlspath, path + VOLOPT_DEFAULT_LEN);
596             }
597           }
598           break;
599
600         case '~' :
601             if (( p = strchr( path, '/' )) != NULL ) {
602                 *p++ = '\0';
603             }
604             u = path;
605             u++;
606             if ( *u == '\0' ) {
607                 u = obj->username;
608             }
609             if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
610                 continue;
611             }
612             strcpy( tmp, pw->pw_dir );
613             if ( p != NULL && *p != '\0' ) {
614                 strcat( tmp, "/" );
615                 strcat( tmp, p );
616             }
617             /* fall through */
618
619         case '/' :
620             /* send path through variable substitution */
621             if (*path != '~') /* need to copy path to tmp */
622               strcpy(tmp, path);
623             if (!pwent)
624               pwent = getpwnam(obj->username);
625             volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL);
626
627             /* this is sort of braindead. basically, i want to be
628              * able to specify things in any order, but i don't want to 
629              * re-write everything. 
630              *
631              * currently we have 11 options: 
632              *   volname
633              *   codepage:x
634              *   casefold:x
635              *   allow:x,y,@z
636              *   deny:x,y,@z
637              *   rwlist:x,y,@z
638              *   rolist:x,y,@z
639              *   options:prodos,crlf,noadouble,ro
640              *   dbpath:x
641              *   password:x
642              *   namemask:x,y,!z  (not implemented yet)
643              */
644             memcpy(options, save_options, sizeof(options));
645             *volname = '\0';
646
647             /* read in up to 11 possible options */
648             for (i = 0; i < VOLOPT_NUM; i++) {
649               if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
650                 break;
651
652               volset(options, volname, sizeof(volname) - 1, 
653                      obj->options.nlspath, tmp);
654             }
655
656             /* check allow/deny lists: 
657                allow -> either no list (-1), or in list (1)
658                deny -> either no list (-1), or not in list (0) */
659             if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
660                 (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1)) {
661
662               /* handle read-only behaviour. semantics: 
663                * 1) neither the rolist nor the rwlist exist -> rw
664                * 2) rolist exists -> ro if user is in it.
665                * 3) rwlist exists -> ro unless user is in it. */
666               if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) && 
667                   ((accessvol(options[VOLOPT_ROLIST].c_value, 
668                               obj->username) == 1) ||
669                    !accessvol(options[VOLOPT_RWLIST].c_value, 
670                               obj->username))) 
671                 options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
672
673               /* do variable substitution */
674               volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path);
675               creatvol(path, tmp, options);
676             }
677             volfree(options, save_options);
678             break;
679
680         case '.' :
681             parseline( sizeof( type ) - 1, type );
682             parseline( sizeof( creator ) - 1, creator );
683             setextmap( path, type, creator, user);
684             break;
685
686         default :
687             break;
688         }
689     }
690     volfree(save_options, NULL);
691     if ( fclose( fp ) != 0 ) {
692         syslog( LOG_ERR, "readvolfile: fclose: %m" );
693     }
694     return( 0 );
695 }
696
697
698 static void load_volumes(AFPObj *obj)
699 {
700   struct passwd *pwent = getpwnam(obj->username);
701
702   if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
703     readvolfile(obj, obj->options.systemvol, 0, pwent);
704   }
705
706   if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
707     readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent);
708   } else if (pwent) {
709         /*
710          * Read user's AppleVolumes or .AppleVolumes file
711          * If neither are readable, read the default volumes file. if 
712          * that doesn't work, create a user share.
713          */
714     if ( readvolfile(obj, pwent->pw_dir, "AppleVolumes", 1, pwent) < 0 &&
715          readvolfile(obj, pwent->pw_dir, ".AppleVolumes", 1, pwent) < 0 &&
716          readvolfile(obj, pwent->pw_dir, "applevolumes", 1, pwent) < 0 &&
717          readvolfile(obj, pwent->pw_dir, ".applevolumes", 1, pwent) < 0 &&
718          obj->options.defaultvol != NULL ) {
719       if (readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent) < 0)
720         creatvol(pwent->pw_dir, NULL, NULL);
721     }
722   }
723   if ( obj->options.flags & OPTION_USERVOLFIRST ) {
724     readvolfile(obj, obj->options.systemvol, NULL, 0, pwent );
725   }
726 }
727
728 static int getvolspace( vol, bfree, btotal, xbfree, xbtotal, bsize )
729     struct vol  *vol;
730     u_int32_t   *bfree, *btotal, *bsize;
731     VolSpace    *xbfree, *xbtotal;
732 {
733     int         spaceflag, rc;
734     u_int32_t   maxsize;
735 #ifndef NO_QUOTA_SUPPORT
736     VolSpace    qfree, qtotal;
737 #endif
738
739     spaceflag = AFPVOL_GVSMASK & vol->v_flags;
740     /* report up to 2GB if afp version is < 2.2 (4GB if not) */
741     maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 :
742       (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE))
743        ? 0x7fffffffL : 0xffffffffL);
744
745 #ifdef AFS
746     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
747         if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
748             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
749             goto getvolspace_done;
750         }
751     }
752 #endif AFS
753
754     if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
755                                     bsize)) != AFP_OK ) {
756         return( rc );
757     }
758
759 #define min(a,b)        ((a)<(b)?(a):(b))
760 #ifndef NO_QUOTA_SUPPORT
761     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
762         if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
763             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
764             *xbfree = min(*xbfree, qfree);
765             *xbtotal = min( *xbtotal, qtotal);
766             goto getvolspace_done;
767         }
768     }
769 #endif
770     vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
771
772 getvolspace_done:
773     *bfree = min( *xbfree, maxsize);
774     *btotal = min( *xbtotal, maxsize);
775     return( AFP_OK );
776 }
777
778 static int getvolparams( bitmap, vol, st, buf, buflen )
779     u_int16_t   bitmap;
780     struct vol  *vol;
781     struct stat *st;
782     char        *buf;
783     int         *buflen;
784 {
785     struct adouble      ad;
786     int                 bit = 0, aint, isad = 1;
787     u_short             ashort;
788     u_int32_t           bfree, btotal, bsize;
789     VolSpace            xbfree, xbtotal; /* extended bytes */
790     char                *data, *nameoff = NULL;
791     char                *slash;
792
793     /* courtesy of jallison@whistle.com:
794      * For MacOS8.x support we need to create the
795      * .Parent file here if it doesn't exist. */
796     
797     memset(&ad, 0, sizeof(ad));
798     if ( ad_open( vol->v_path, vol_noadouble(vol) | 
799                   ADFLAGS_HF|ADFLAGS_DIR, O_RDWR | O_CREAT, 
800                   0666, &ad) < 0 ) {
801           isad = 0;
802
803     } else if (ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT) {
804           slash = strrchr( vol->v_path, '/' );
805           if(slash)
806               slash++;
807           else
808               slash = vol->v_path;
809
810           ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
811           memcpy(ad_entry( &ad, ADEID_NAME ), slash, 
812                  ad_getentrylen( &ad, ADEID_NAME ));
813           ad_flush(&ad, ADFLAGS_HF);
814     }
815
816     if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
817                      (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
818                      (1<<VOLPBIT_BSIZE)) ) != 0 ) {
819         if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
820                           &bsize) < 0 ) {
821             if ( isad ) {
822                 ad_close( &ad, ADFLAGS_HF );
823             }
824             return( AFPERR_PARAM );
825         }
826     }
827
828     data = buf;
829     while ( bitmap != 0 ) {
830         while (( bitmap & 1 ) == 0 ) {
831             bitmap = bitmap>>1;
832             bit++;
833         }
834
835         switch ( bit ) {
836         case VOLPBIT_ATTR :
837 #if AD_VERSION > AD_VERSION1
838             ashort = VOLPBIT_ATTR_FILEID;
839 #else
840             ashort = 0;
841 #endif
842             /* check for read-only.
843              * NOTE: we don't actually set the read-only flag unless
844              *       it's passed in that way as it's possible to mount
845              *       a read-write filesystem under a read-only one. */
846             if ((vol->v_flags & AFPVOL_RO) ||
847                 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS)))
848               ashort |= VOLPBIT_ATTR_RO;
849             ashort = htons(ashort);
850             memcpy(data, &ashort, sizeof( ashort ));
851             data += sizeof( ashort );
852             break;
853
854         case VOLPBIT_SIG :
855             ashort = htons( AFPVOLSIG_DEFAULT );
856             memcpy(data, &ashort, sizeof( ashort ));
857             data += sizeof( ashort );
858             break;
859
860         case VOLPBIT_CDATE :
861             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
862                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
863             memcpy(data, &aint, sizeof( aint ));
864             data += sizeof( aint );
865             break;
866
867         case VOLPBIT_MDATE :
868             if ( st->st_mtime > vol->v_time ) {
869                 vol->v_time = st->st_mtime;
870                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
871             } else {
872                 aint = AD_DATE_FROM_UNIX(vol->v_time);
873             }
874             memcpy(data, &aint, sizeof( aint ));
875             data += sizeof( aint );
876             break;
877
878         case VOLPBIT_BDATE :
879             if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
880                 aint = AD_DATE_START;
881             memcpy(data, &aint, sizeof( aint ));
882             data += sizeof( aint );
883             break;
884
885         case VOLPBIT_VID :
886             memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
887             data += sizeof( vol->v_vid );
888             break;
889
890         case VOLPBIT_BFREE :
891             bfree = htonl( bfree );
892             memcpy(data, &bfree, sizeof( bfree ));
893             data += sizeof( bfree );
894             break;
895
896         case VOLPBIT_BTOTAL :
897             btotal = htonl( btotal );
898             memcpy(data, &btotal, sizeof( btotal ));
899             data += sizeof( btotal );
900             break;
901
902 #ifndef NO_LARGE_VOL_SUPPORT
903         case VOLPBIT_XBFREE :
904             xbfree = hton64( xbfree );
905 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
906             bcopy(&xbfree, data, sizeof(xbfree));
907 #else
908             memcpy(data, &xbfree, sizeof( xbfree ));
909 #endif
910             data += sizeof( xbfree );
911             break;
912
913         case VOLPBIT_XBTOTAL :
914             xbtotal = hton64( xbtotal );
915 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
916             bcopy(&xbtotal, data, sizeof(xbtotal));
917 #else
918             memcpy(data, &xbtotal, sizeof( xbtotal ));
919 #endif
920             data += sizeof( xbfree );
921             break;
922 #endif
923
924         case VOLPBIT_NAME :
925             nameoff = data;
926             data += sizeof( u_int16_t );
927             break;
928
929         case VOLPBIT_BSIZE:  /* block size */
930             bsize = htonl(bsize);
931             memcpy(data, &bsize, sizeof(bsize));
932             data += sizeof(bsize);
933             break;
934
935         default :
936             if ( isad ) {
937                 ad_close( &ad, ADFLAGS_HF );
938             }
939             return( AFPERR_BITMAP );
940         }
941         bitmap = bitmap>>1;
942         bit++;
943     }
944     if ( nameoff ) {
945         ashort = htons( data - buf );
946         memcpy(nameoff, &ashort, sizeof( ashort ));
947         aint = strlen( vol->v_name );
948         *data++ = aint;
949         memcpy(data, vol->v_name, aint );
950         data += aint;
951     }
952     if ( isad ) {
953         ad_close( &ad, ADFLAGS_HF );
954     }
955     *buflen = data - buf;
956     return( AFP_OK );
957 }
958
959
960
961 int afp_getsrvrparms(obj, ibuf, ibuflen, rbuf, rbuflen )
962     AFPObj      *obj;
963     char        *ibuf, *rbuf;
964     int         ibuflen, *rbuflen;
965 {
966     struct timeval      tv;
967     struct stat         st;
968     struct vol          *volume;
969     char        *data;
970     int                 vcnt, len;
971
972   
973     if (!volumes)
974       load_volumes(obj);
975
976     data = rbuf + 5;
977     for ( vcnt = 0, volume = volumes; volume; volume = volume->v_next ) {
978         if ( stat( volume->v_path, &st ) < 0 ) {
979             syslog( LOG_INFO, "afp_getsrvrparms: stat %s: %m", 
980                     volume->v_path );
981             continue;           /* can't access directory */
982         }
983         if (!S_ISDIR(st.st_mode)) {
984             continue;           /* not a dir */
985         }
986
987         /* set password bit if there's a volume password */
988         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
989
990         /* Apple 2 clients running ProDOS-8 expect one volume to have
991            bit 0 of this byte set.  They will not recognize anything
992            on the server unless this is the case.  I have not
993            completely worked this out, but it's related to booting
994            from the server.  Support for that function is a ways
995            off.. <shirsch@ibm.net> */
996         *data++ |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
997         len = strlen( volume->v_name );
998         *data++ = len;
999         memcpy(data, volume->v_name, len );
1000         data += len;
1001         vcnt++;
1002     }
1003
1004     *rbuflen = data - rbuf;
1005     data = rbuf;
1006     if ( gettimeofday( &tv, 0 ) < 0 ) {
1007         syslog( LOG_ERR, "afp_getsrvrparms: gettimeofday: %m" );
1008         *rbuflen = 0;
1009         return AFPERR_PARAM;
1010     }
1011     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
1012     memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
1013     data += sizeof( u_int32_t);
1014     *data = vcnt;
1015     return( AFP_OK );
1016 }
1017
1018 int afp_openvol(obj, ibuf, ibuflen, rbuf, rbuflen )
1019     AFPObj      *obj;
1020     char        *ibuf, *rbuf;
1021     int         ibuflen, *rbuflen;
1022 {
1023     struct stat st;
1024     char        *volname;
1025 #if AD_VERSION == AD_VERSION1
1026     char *p;
1027 #endif
1028     struct vol  *volume;
1029     struct dir  *dir;
1030     int         len, ret, buflen;
1031     u_int16_t   bitmap;
1032
1033     ibuf += 2;
1034     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1035     bitmap = ntohs( bitmap );
1036     ibuf += sizeof( bitmap );
1037     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
1038         ret = AFPERR_BITMAP;
1039         goto openvol_err;
1040     }
1041
1042     len = (unsigned char)*ibuf++;
1043     volname = obj->oldtmp;
1044     memcpy(volname, ibuf, len );
1045     *(volname +  len) = '\0';
1046     ibuf += len;
1047     if ((len + 1) & 1) /* pad to an even boundary */
1048       ibuf++;
1049
1050     if (!volumes)
1051       load_volumes(obj);
1052
1053     for ( volume = volumes; volume; volume = volume->v_next ) {
1054         if ( strcasecmp( volname, volume->v_name ) == 0 ) {
1055             break;
1056         }
1057     }
1058
1059     if ( volume == NULL ) {
1060         ret = AFPERR_PARAM;
1061         goto openvol_err;
1062     }
1063
1064     /* check for a volume password */
1065     if (volume->v_password && 
1066         strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
1067         ret = AFPERR_ACCESS;
1068         goto openvol_err;
1069     }
1070
1071     if (( volume->v_flags & AFPVOL_OPEN  ) == 0 ) {
1072         if ((dir = dirnew(strlen(volume->v_name) + 1)) == NULL) {
1073             syslog( LOG_ERR, "afp_openvol: malloc: %m" );
1074             ret = AFPERR_MISC;
1075             goto openvol_err;
1076         }
1077         dir->d_did = DIRDID_ROOT;
1078         dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
1079         strcpy( dir->d_name, volume->v_name );
1080         volume->v_dir = volume->v_root = dir;
1081         volume->v_flags |= AFPVOL_OPEN;
1082     }
1083
1084     if ( stat( volume->v_path, &st ) < 0 ) {
1085         ret = AFPERR_PARAM;
1086         goto openvol_err;
1087     }
1088
1089     buflen = *rbuflen - sizeof( bitmap );
1090     if (( ret = getvolparams( bitmap, volume, &st,
1091             rbuf + sizeof(bitmap), &buflen )) != AFP_OK ) {
1092         goto openvol_err;
1093     }
1094     *rbuflen = buflen + sizeof( bitmap );
1095     bitmap = htons( bitmap );
1096     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1097
1098     curdir = volume->v_dir;
1099     if ( chdir( volume->v_path ) < 0 ) {
1100         ret = AFPERR_PARAM;
1101         goto openvol_err;
1102     }
1103 #if AD_VERSION == AD_VERSION1
1104     /*
1105      * If you mount a volume twice, the second time the trash appears on
1106      * the desk-top.  That's because the Mac remembers the DID for the
1107      * trash (even for volumes in different zones, on different servers).
1108      * Just so this works better, we prime the DID cache with the trash,
1109      * fixing the trash at DID 3.
1110      */
1111     p = Trash;
1112     cname( volume, volume->v_dir, &p );
1113 #endif
1114
1115     return( AFP_OK );
1116
1117 openvol_err:
1118     *rbuflen = 0;
1119     return ret;
1120 }
1121
1122 int afp_closevol(obj, ibuf, ibuflen, rbuf, rbuflen )
1123     AFPObj      *obj;
1124     char        *ibuf, *rbuf;
1125     int         ibuflen, *rbuflen;
1126 {
1127     struct vol  *vol, *ovol;
1128     u_int16_t   vid;
1129
1130     *rbuflen = 0;
1131     ibuf += 2;
1132     memcpy(&vid, ibuf, sizeof( vid ));
1133     if (( vol = getvolbyvid( vid )) == NULL ) {
1134         return( AFPERR_PARAM );
1135     }
1136
1137     vol->v_flags &= ~AFPVOL_OPEN;
1138     for ( ovol = volumes; ovol; ovol = ovol->v_next ) {
1139         if ( ovol->v_flags & AFPVOL_OPEN ) {
1140             break;
1141         }
1142     }
1143     if ( ovol != NULL ) {
1144         curdir = ovol->v_dir;
1145         if ( chdir( ovol->v_path ) < 0 ) {
1146             return( AFPERR_PARAM );
1147         }
1148     }
1149
1150     dirfree( vol->v_root );
1151     vol->v_dir = NULL;
1152 #if AD_VERSION > AD_VERSION1
1153     cnid_close(vol->v_db);
1154     vol->v_db = NULL;
1155 #endif
1156     return( AFP_OK );
1157 }
1158
1159 struct vol *getvolbyvid(const u_int16_t vid )
1160 {
1161     struct vol  *vol;
1162
1163     for ( vol = volumes; vol; vol = vol->v_next ) {
1164         if ( vid == vol->v_vid ) {
1165             break;
1166         }
1167     }
1168     if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
1169         return( NULL );
1170     }
1171
1172     return( vol );
1173 }
1174
1175 struct extmap *getextmap(const char *path)
1176 {
1177     char        *p;
1178     struct extmap       *em;
1179
1180     if (( p = strrchr( path, '.' )) == NULL ) {
1181         return( defextmap );
1182     }
1183
1184     for ( em = extmap; em; em = em->em_next ) {
1185         if ( strdiacasecmp( em->em_ext, p ) == 0 ) {
1186             break;
1187         }
1188     }
1189     if ( em == NULL ) {
1190         return( defextmap );
1191     } else {
1192         return( em );
1193     }
1194 }
1195
1196 void setvoltime(obj, vol )
1197     AFPObj *obj;
1198     struct vol  *vol;
1199 {
1200     struct timeval      tv;
1201
1202     /* just looking at vol->v_time is broken seriously since updates
1203      * from other users afpd processes never are seen.
1204      * This is not the most elegant solution (a shared memory between
1205      * the afpd processes would come closer)
1206      * [RS] */
1207
1208     if ( gettimeofday( &tv, 0 ) < 0 ) {
1209         syslog( LOG_ERR, "setvoltime: gettimeofday: %m" );
1210         return;
1211     }
1212     if( utime( vol->v_path, NULL ) < 0 ) {
1213         /* write of time failed ... probably a read only filesys,
1214          * where no other users can interfere, so there's no issue here
1215          */
1216     }
1217     
1218     /* a little granularity */
1219     if (vol->v_time < tv.tv_sec) {
1220       vol->v_time = tv.tv_sec;
1221       obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
1222     }
1223 }
1224
1225 int afp_getvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1226     AFPObj      *obj;
1227     char        *ibuf, *rbuf;
1228     int         ibuflen, *rbuflen;
1229 {
1230     struct stat st;
1231     struct vol  *vol;
1232     int         buflen, ret;
1233     u_int16_t   vid, bitmap;
1234
1235     ibuf += 2;
1236     memcpy(&vid, ibuf, sizeof( vid ));
1237     ibuf += sizeof( vid );
1238     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1239     bitmap = ntohs( bitmap );
1240
1241     if (( vol = getvolbyvid( vid )) == NULL ) {
1242         *rbuflen = 0;
1243         return( AFPERR_PARAM );
1244     }
1245
1246     if ( stat( vol->v_path, &st ) < 0 ) {
1247         *rbuflen = 0;
1248         return( AFPERR_PARAM );
1249     }
1250
1251     buflen = *rbuflen - sizeof( bitmap );
1252     if (( ret = getvolparams( bitmap, vol, &st,
1253             rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
1254         *rbuflen = 0;
1255         return( ret );
1256     }
1257     *rbuflen = buflen + sizeof( bitmap );
1258     bitmap = htons( bitmap );
1259     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1260     return( AFP_OK );
1261 }
1262
1263 int afp_setvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1264     AFPObj      *obj;
1265     char        *ibuf, *rbuf;
1266     int         ibuflen, *rbuflen;
1267 {
1268     struct adouble ad;
1269     struct vol  *vol;
1270     u_int16_t   vid, bitmap;
1271     u_int32_t   aint;
1272
1273     ibuf += 2;
1274     *rbuflen = 0;
1275
1276     memcpy(&vid, ibuf, sizeof( vid ));
1277     ibuf += sizeof( vid );
1278     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1279     bitmap = ntohs( bitmap );
1280     ibuf += sizeof(bitmap);
1281
1282     if (( vol = getvolbyvid( vid )) == NULL ) {
1283         return( AFPERR_PARAM );
1284     }
1285
1286     if (vol->v_flags & AFPVOL_RO)
1287         return AFPERR_VLOCK;
1288
1289     /* we can only set the backup date. */
1290     if (bitmap != VOLPBIT_BDATE)
1291       return AFPERR_BITMAP;
1292
1293     memset(&ad, 0, sizeof(ad));
1294     if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 
1295                   0666, &ad) < 0 ) {
1296       if (errno == EROFS)
1297         return AFPERR_VLOCK;
1298
1299       return AFPERR_ACCESS;
1300     }
1301
1302     memcpy(&aint, ibuf, sizeof(aint));
1303     ad_setdate(&ad, AD_DATE_BACKUP, aint);
1304     ad_flush(&ad, ADFLAGS_HF);
1305     ad_close(&ad, ADFLAGS_HF);
1306     return( AFP_OK );
1307 }