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