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