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