]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/volume.c
First try for a volume defaults config
[netatalk.git] / etc / afpd / volume.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <pwd.h>
14 #include <grp.h>
15 #include <utime.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <inttypes.h>
23 #include <time.h>
24
25 #include <atalk/dsi.h>
26 #include <atalk/adouble.h>
27 #include <atalk/afp.h>
28 #include <atalk/util.h>
29 #include <atalk/logger.h>
30 #include <atalk/vfs.h>
31 #include <atalk/uuid.h>
32 #include <atalk/ea.h>
33 #include <atalk/bstrlib.h>
34 #include <atalk/bstradd.h>
35 #include <atalk/ftw.h>
36 #include <atalk/globals.h>
37 #include <atalk/fce_api.h>
38 #include <atalk/errchk.h>
39 #include <atalk/iniparser.h>
40 #include <atalk/unix.h>
41 #include <atalk/netatalk_conf.h>
42
43 #ifdef CNID_DB
44 #include <atalk/cnid.h>
45 #endif /* CNID_DB*/
46
47 #include "directory.h"
48 #include "file.h"
49 #include "volume.h"
50 #include "unix.h"
51 #include "mangle.h"
52 #include "fork.h"
53 #include "hash.h"
54 #include "acls.h"
55
56 #define VOLPASSLEN  8
57
58 extern int afprun(int root, char *cmd, int *outfd);
59
60 /*!
61  * Read band-size info from Info.plist XML file of an TM sparsebundle
62  *
63  * @param path   (r) path to Info.plist file
64  * @return           band-size in bytes, -1 on error
65  */
66 static long long int get_tm_bandsize(const char *path)
67 {
68     EC_INIT;
69     FILE *file = NULL;
70     char buf[512];
71     long long int bandsize = -1;
72
73     EC_NULL_LOGSTR( file = fopen(path, "r"),
74                     "get_tm_bandsize(\"%s\"): %s",
75                     path, strerror(errno) );
76
77     while (fgets(buf, sizeof(buf), file) != NULL) {
78         if (strstr(buf, "band-size") == NULL)
79             continue;
80
81         if (fscanf(file, " <integer>%lld</integer>", &bandsize) != 1) {
82             LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\"): can't parse band-size", path);
83             EC_FAIL;
84         }
85         break;
86     }
87
88 EC_CLEANUP:
89     if (file)
90         fclose(file);
91     LOG(log_debug, logtype_afpd, "get_tm_bandsize(\"%s\"): bandsize: %lld", path, bandsize);
92     return bandsize;
93 }
94
95 /*!
96  * Return number on entries in a directory
97  *
98  * @param path   (r) path to dir
99  * @return           number of entries, -1 on error
100  */
101 static long long int get_tm_bands(const char *path)
102 {
103     EC_INIT;
104     long long int count = 0;
105     DIR *dir = NULL;
106     const struct dirent *entry;
107
108     EC_NULL( dir = opendir(path) );
109
110     while ((entry = readdir(dir)) != NULL)
111         count++;
112     count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */
113         
114 EC_CLEANUP:
115     if (dir)
116         closedir(dir);
117     if (ret != 0)
118         return -1;
119     return count;
120 }
121
122 /*!
123  * Calculate used size of a TimeMachine volume
124  *
125  * This assumes that the volume is used only for TimeMachine.
126  *
127  * 1) readdir(path of volume)
128  * 2) for every element that matches regex "\(.*\)\.sparsebundle$" :
129  * 3) parse "\1.sparsebundle/Info.plist" and read the band-size XML key integer value
130  * 4) readdir "\1.sparsebundle/bands/" counting files
131  * 5) calculate used size as: (file_count - 1) * band-size
132  *
133  * The result of the calculation is returned in "volume->v_tm_used".
134  * "volume->v_appended" gets reset to 0.
135  * "volume->v_tm_cachetime" is updated with the current time from time(NULL).
136  *
137  * "volume->v_tm_used" is cached for TM_USED_CACHETIME seconds and updated by
138  * "volume->v_appended". The latter is increased by X every time the client
139  * appends X bytes to a file (in fork.c).
140  *
141  * @param vol     (rw) volume to calculate
142  * @return             0 on success, -1 on error
143  */
144 #define TM_USED_CACHETIME 60    /* cache for 60 seconds */
145 static int get_tm_used(struct vol * restrict vol)
146 {
147     EC_INIT;
148     long long int bandsize;
149     VolSpace used = 0;
150     bstring infoplist = NULL;
151     bstring bandsdir = NULL;
152     DIR *dir = NULL;
153     const struct dirent *entry;
154     const char *p;
155     struct stat st;
156     long int links;
157     time_t now = time(NULL);
158
159     if (vol->v_tm_cachetime
160         && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) {
161         if (vol->v_tm_used == -1)
162             EC_FAIL;
163         vol->v_tm_used += vol->v_appended;
164         vol->v_appended = 0;
165         LOG(log_debug, logtype_afpd, "getused(\"%s\"): cached: %" PRIu64 " bytes",
166             vol->v_path, vol->v_tm_used);
167         return 0;
168     }
169
170     vol->v_tm_cachetime = now;
171
172     EC_NULL( dir = opendir(vol->v_path) );
173
174     while ((entry = readdir(dir)) != NULL) {
175         if (((p = strstr(entry->d_name, "sparsebundle")) != NULL)
176             && (strlen(entry->d_name) == (p + strlen("sparsebundle") - entry->d_name))) {
177
178             EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") );
179             
180             if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1)
181                 continue;
182
183             EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") );
184
185             if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1)
186                 continue;
187
188             used += (links - 1) * bandsize;
189             LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes",
190                 cfrombstr(bandsdir), used);
191         }
192     }
193
194     vol->v_tm_used = used;
195
196 EC_CLEANUP:
197     if (infoplist)
198         bdestroy(infoplist);
199     if (bandsdir)
200         bdestroy(bandsdir);
201     if (dir)
202         closedir(dir);
203
204     LOG(log_debug, logtype_afpd, "getused(\"%s\"): %" PRIu64 " bytes", vol->v_path, vol->v_tm_used);
205
206     EC_EXIT;
207 }
208
209 static int getvolspace(const AFPObj *obj, struct vol *vol,
210                        uint32_t *bfree, uint32_t *btotal,
211                        VolSpace *xbfree, VolSpace *xbtotal, uint32_t *bsize)
212 {
213     int         spaceflag, rc;
214     uint32_t   maxsize;
215     VolSpace    used;
216 #ifndef NO_QUOTA_SUPPORT
217     VolSpace    qfree, qtotal;
218 #endif
219
220     spaceflag = AFPVOL_GVSMASK & vol->v_flags;
221     /* report up to 2GB if afp version is < 2.2 (4GB if not) */
222     maxsize = (obj->afp_version < 22) ? 0x7fffffffL : 0xffffffffL;
223
224 #ifdef AFS
225     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
226         if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
227             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
228             goto getvolspace_done;
229         }
230     }
231 #endif
232
233     if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) {
234         return( rc );
235     }
236
237 #ifndef NO_QUOTA_SUPPORT
238     if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
239         if ( uquota_getvolspace(obj, vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
240             vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
241             *xbfree = MIN(*xbfree, qfree);
242             *xbtotal = MIN(*xbtotal, qtotal);
243             goto getvolspace_done;
244         }
245     }
246 #endif
247     vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
248
249 getvolspace_done:
250     if (vol->v_limitsize) {
251         if (get_tm_used(vol) != 0)
252             return AFPERR_MISC;
253
254         *xbtotal = MIN(*xbtotal, (vol->v_limitsize * 1024 * 1024));
255         *xbfree = MIN(*xbfree, *xbtotal < vol->v_tm_used ? 0 : *xbtotal - vol->v_tm_used);
256
257         LOG(log_debug, logtype_afpd,
258             "volparams: total: %" PRIu64 ", used: %" PRIu64 ", free: %" PRIu64 " bytes",
259             *xbtotal, vol->v_tm_used, *xbfree);
260     }
261
262     *bfree = MIN(*xbfree, maxsize);
263     *btotal = MIN(*xbtotal, maxsize);
264     return( AFP_OK );
265 }
266
267 #define FCE_TM_DELTA 10  /* send notification every 10 seconds */
268 void vol_fce_tm_event(void)
269 {
270     static time_t last;
271     time_t now = time(NULL);
272     struct vol  *vol = getvolumes();
273
274     if ((last + FCE_TM_DELTA) < now) {
275         last = now;
276         for ( ; vol; vol = vol->v_next ) {
277             if (vol->v_flags & AFPVOL_TM)
278                 (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_appended);
279         }
280     }
281 }
282
283 /* -----------------------
284  * set volume creation date
285  * avoid duplicate, well at least it tries
286  */
287 static void vol_setdate(uint16_t id, struct adouble *adp, time_t date)
288 {
289     struct vol  *volume;
290     struct vol  *vol = getvolumes();
291
292     for ( volume = getvolumes(); volume; volume = volume->v_next ) {
293         if (volume->v_vid == id) {
294             vol = volume;
295         }
296         else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) {
297             date = date+1;
298             volume = getvolumes(); /* restart */
299         }
300     }
301     vol->v_ctime = AD_DATE_FROM_UNIX(date);
302     ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date);
303 }
304
305 /* ----------------------- */
306 static int getvolparams(const AFPObj *obj, uint16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen)
307 {
308     struct adouble  ad;
309     int         bit = 0, isad = 1;
310     uint32_t       aint;
311     u_short     ashort;
312     uint32_t       bfree, btotal, bsize;
313     VolSpace            xbfree, xbtotal; /* extended bytes */
314     char        *data, *nameoff = NULL;
315     char                *slash;
316
317     LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname);
318
319     /* courtesy of jallison@whistle.com:
320      * For MacOS8.x support we need to create the
321      * .Parent file here if it doesn't exist. */
322
323     /* Convert adouble:v2 to adouble:ea on the fly */
324     (void)ad_convert(vol->v_path, st, vol);
325
326     ad_init(&ad, vol);
327     if (ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0 ) {
328         isad = 0;
329         vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
330
331     } else if (ad_get_MD_flags( &ad ) & O_CREAT) {
332         slash = strrchr( vol->v_path, '/' );
333         if(slash)
334             slash++;
335         else
336             slash = vol->v_path;
337         if (ad_getentryoff(&ad, ADEID_NAME)) {
338             ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
339             memcpy(ad_entry( &ad, ADEID_NAME ), slash,
340                    ad_getentrylen( &ad, ADEID_NAME ));
341         }
342         vol_setdate(vol->v_vid, &ad, st->st_mtime);
343         ad_flush(&ad);
344     }
345     else {
346         if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)
347             vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
348         else
349             vol->v_ctime = aint;
350     }
351
352     if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
353                      (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
354                      (1<<VOLPBIT_BSIZE)) ) != 0 ) {
355         if ( getvolspace(obj, vol, &bfree, &btotal, &xbfree, &xbtotal,
356                           &bsize) != AFP_OK ) {
357             if ( isad ) {
358                 ad_close( &ad, ADFLAGS_HF );
359             }
360             return( AFPERR_PARAM );
361         }
362     }
363
364     data = buf;
365     while ( bitmap != 0 ) {
366         while (( bitmap & 1 ) == 0 ) {
367             bitmap = bitmap>>1;
368             bit++;
369         }
370
371         switch ( bit ) {
372         case VOLPBIT_ATTR :
373             ashort = 0;
374             /* check for read-only.
375              * NOTE: we don't actually set the read-only flag unless
376              *       it's passed in that way as it's possible to mount
377              *       a read-write filesystem under a read-only one. */
378             if ((vol->v_flags & AFPVOL_RO) ||
379                 ((utime(vol->v_path, NULL) < 0) && (errno == EROFS))) {
380                 ashort |= VOLPBIT_ATTR_RO;
381             }
382             /* prior 2.1 only VOLPBIT_ATTR_RO is defined */
383             if (obj->afp_version > 20) {
384                 if (vol->v_cdb != NULL && (vol->v_cdb->flags & CNID_FLAG_PERSISTENT))
385                     ashort |= VOLPBIT_ATTR_FILEID;
386                 ashort |= VOLPBIT_ATTR_CATSEARCH;
387
388                 if (obj->afp_version >= 30) {
389                     ashort |= VOLPBIT_ATTR_UTF8;
390                     if (vol->v_flags & AFPVOL_UNIX_PRIV)
391                         ashort |= VOLPBIT_ATTR_UNIXPRIV;
392                     if (vol->v_flags & AFPVOL_TM)
393                         ashort |= VOLPBIT_ATTR_TM;
394                     if (vol->v_flags & AFPVOL_NONETIDS)
395                         ashort |= VOLPBIT_ATTR_NONETIDS;
396                     if (obj->afp_version >= 32) {
397                         if (vol->v_vfs_ea)
398                             ashort |= VOLPBIT_ATTR_EXT_ATTRS;
399                         if (vol->v_flags & AFPVOL_ACLS)
400                             ashort |= VOLPBIT_ATTR_ACLS;
401                     }
402                 }
403             }
404             ashort = htons(ashort);
405             memcpy(data, &ashort, sizeof( ashort ));
406             data += sizeof( ashort );
407             break;
408
409         case VOLPBIT_SIG :
410             ashort = htons( AFPVOLSIG_DEFAULT );
411             memcpy(data, &ashort, sizeof( ashort ));
412             data += sizeof( ashort );
413             break;
414
415         case VOLPBIT_CDATE :
416             aint = vol->v_ctime;
417             memcpy(data, &aint, sizeof( aint ));
418             data += sizeof( aint );
419             break;
420
421         case VOLPBIT_MDATE :
422             if ( st->st_mtime > vol->v_mtime ) {
423                 vol->v_mtime = st->st_mtime;
424             }
425             aint = AD_DATE_FROM_UNIX(vol->v_mtime);
426             memcpy(data, &aint, sizeof( aint ));
427             data += sizeof( aint );
428             break;
429
430         case VOLPBIT_BDATE :
431             if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
432                 aint = AD_DATE_START;
433             memcpy(data, &aint, sizeof( aint ));
434             data += sizeof( aint );
435             break;
436
437         case VOLPBIT_VID :
438             memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
439             data += sizeof( vol->v_vid );
440             break;
441
442         case VOLPBIT_BFREE :
443             bfree = htonl( bfree );
444             memcpy(data, &bfree, sizeof( bfree ));
445             data += sizeof( bfree );
446             break;
447
448         case VOLPBIT_BTOTAL :
449             btotal = htonl( btotal );
450             memcpy(data, &btotal, sizeof( btotal ));
451             data += sizeof( btotal );
452             break;
453
454 #ifndef NO_LARGE_VOL_SUPPORT
455         case VOLPBIT_XBFREE :
456             xbfree = hton64( xbfree );
457             memcpy(data, &xbfree, sizeof( xbfree ));
458             data += sizeof( xbfree );
459             break;
460
461         case VOLPBIT_XBTOTAL :
462             xbtotal = hton64( xbtotal );
463             memcpy(data, &xbtotal, sizeof( xbtotal ));
464             data += sizeof( xbfree );
465             break;
466 #endif /* ! NO_LARGE_VOL_SUPPORT */
467
468         case VOLPBIT_NAME :
469             nameoff = data;
470             data += sizeof( uint16_t );
471             break;
472
473         case VOLPBIT_BSIZE:  /* block size */
474             bsize = htonl(bsize);
475             memcpy(data, &bsize, sizeof(bsize));
476             data += sizeof(bsize);
477             break;
478
479         default :
480             if ( isad ) {
481                 ad_close( &ad, ADFLAGS_HF );
482             }
483             return( AFPERR_BITMAP );
484         }
485         bitmap = bitmap>>1;
486         bit++;
487     }
488     if ( nameoff ) {
489         ashort = htons( data - buf );
490         memcpy(nameoff, &ashort, sizeof( ashort ));
491         /* name is always in mac charset */
492         aint = ucs2_to_charset( vol->v_maccharset, vol->v_macname, data+1, AFPVOL_MACNAMELEN + 1);
493         if ( aint <= 0 ) {
494             *buflen = 0;
495             return AFPERR_MISC;
496         }
497
498         *data++ = aint;
499         data += aint;
500     }
501     if ( isad ) {
502         ad_close(&ad, ADFLAGS_HF);
503     }
504     *buflen = data - buf;
505     return( AFP_OK );
506 }
507
508 /* ------------------------- */
509 static int stat_vol(const AFPObj *obj, uint16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen)
510 {
511     struct stat st;
512     int     ret;
513     size_t  buflen;
514
515     if ( stat( vol->v_path, &st ) < 0 ) {
516         *rbuflen = 0;
517         return( AFPERR_PARAM );
518     }
519     /* save the volume device number */
520     vol->v_dev = st.st_dev;
521
522     buflen = *rbuflen - sizeof( bitmap );
523     if (( ret = getvolparams(obj, bitmap, vol, &st,
524                               rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
525         *rbuflen = 0;
526         return( ret );
527     }
528     *rbuflen = buflen + sizeof( bitmap );
529     bitmap = htons( bitmap );
530     memcpy(rbuf, &bitmap, sizeof( bitmap ));
531     return( AFP_OK );
532
533 }
534
535 /* ------------------------------- */
536 int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
537 {
538     struct timeval  tv;
539     struct stat     st;
540     struct vol      *volume;
541     char    *data;
542     char        *namebuf;
543     int         vcnt;
544     size_t      len;
545
546     load_volumes(obj, closevol);
547
548     data = rbuf + 5;
549     for ( vcnt = 0, volume = getvolumes(); volume; volume = volume->v_next ) {
550         if (!(volume->v_flags & AFPVOL_NOSTAT)) {
551             struct maccess ma;
552
553             if ( stat( volume->v_path, &st ) < 0 ) {
554                 LOG(log_info, logtype_afpd, "afp_getsrvrparms(%s): stat: %s",
555                     volume->v_path, strerror(errno) );
556                 continue;       /* can't access directory */
557             }
558             if (!S_ISDIR(st.st_mode)) {
559                 continue;       /* not a dir */
560             }
561             accessmode(obj, volume, volume->v_path, &ma, NULL, &st);
562             if ((ma.ma_user & (AR_UREAD | AR_USEARCH)) != (AR_UREAD | AR_USEARCH)) {
563                 continue;   /* no r-x access */
564             }
565         }
566
567         if (utf8_encoding(obj)) {
568             len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname);
569         } else {
570             len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname);
571         }
572
573         if (len == (size_t)-1)
574             continue;
575
576         /* set password bit if there's a volume password */
577         *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
578
579         *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
580         *data++ = len;
581         memcpy(data, namebuf, len );
582         data += len;
583         free(namebuf);
584         vcnt++;
585     }
586
587     *rbuflen = data - rbuf;
588     data = rbuf;
589     if ( gettimeofday( &tv, NULL ) < 0 ) {
590         LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
591         *rbuflen = 0;
592         return AFPERR_PARAM;
593     }
594     tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
595     memcpy(data, &tv.tv_sec, sizeof( uint32_t));
596     data += sizeof( uint32_t);
597     *data = vcnt;
598     return( AFP_OK );
599 }
600
601 /* ------------------------- */
602 static int volume_codepage(AFPObj *obj, struct vol *volume)
603 {
604     struct charset_functions *charset;
605     /* Codepages */
606
607     if (!volume->v_volcodepage)
608         volume->v_volcodepage = strdup("UTF8");
609
610     if ( (charset_t) -1 == ( volume->v_volcharset = add_charset(volume->v_volcodepage)) ) {
611         LOG (log_error, logtype_afpd, "Setting codepage %s as volume codepage failed", volume->v_volcodepage);
612         return -1;
613     }
614
615     if ( NULL == (charset = find_charset_functions(volume->v_volcodepage)) || charset->flags & CHARSET_ICONV ) {
616         LOG (log_warning, logtype_afpd, "WARNING: volume encoding %s is *not* supported by netatalk, expect problems !!!!", volume->v_volcodepage);
617     }
618
619     if (!volume->v_maccodepage)
620         volume->v_maccodepage = strdup(obj->options.maccodepage);
621
622     if ( (charset_t) -1 == ( volume->v_maccharset = add_charset(volume->v_maccodepage)) ) {
623         LOG (log_error, logtype_afpd, "Setting codepage %s as mac codepage failed", volume->v_maccodepage);
624         return -1;
625     }
626
627     if ( NULL == ( charset = find_charset_functions(volume->v_maccodepage)) || ! (charset->flags & CHARSET_CLIENT) ) {
628         LOG (log_error, logtype_afpd, "Fatal error: mac charset %s not supported", volume->v_maccodepage);
629         return -1;
630     }
631     volume->v_kTextEncoding = htonl(charset->kTextEncoding);
632     return 0;
633 }
634
635 /* ------------------------- */
636 static int volume_openDB(const AFPObj *obj, struct vol *volume)
637 {
638     int flags = 0;
639
640     if ((volume->v_flags & AFPVOL_NODEV)) {
641         flags |= CNID_FLAG_NODEV;
642     }
643
644     if (volume->v_cnidscheme == NULL) {
645         volume->v_cnidscheme = strdup(DEFAULT_CNID_SCHEME);
646         LOG(log_info, logtype_afpd, "Volume %s use CNID scheme %s.",
647             volume->v_path, volume->v_cnidscheme);
648     }
649
650     LOG(log_info, logtype_afpd, "CNID server: %s:%s",
651         volume->v_cnidserver ? volume->v_cnidserver : obj->options.Cnid_srv,
652         volume->v_cnidport ? volume->v_cnidport : obj->options.Cnid_port);
653
654     volume->v_cdb = cnid_open(volume->v_path,
655                               volume->v_umask,
656                               volume->v_cnidscheme,
657                               flags,
658                               volume->v_cnidserver ? volume->v_cnidserver : obj->options.Cnid_srv,
659                               volume->v_cnidport ? volume->v_cnidport : obj->options.Cnid_port);
660
661     if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
662         /* The first attempt failed and it wasn't yet an attempt to open in-memory */
663         LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ",
664             volume->v_path, volume->v_cnidscheme);
665         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
666             volume->v_path);
667         flags |= CNID_FLAG_MEMORY;
668         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
669 #ifdef SERVERTEXT
670         /* kill ourself with SIGUSR2 aka msg pending */
671         if (volume->v_cdb) {
672             setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
673                        "Check server messages for details!");
674             kill(getpid(), SIGUSR2);
675             /* deactivate cnid caching/storing in AppleDouble files */
676         }
677 #endif
678     }
679
680     return (!volume->v_cdb)?-1:0;
681 }
682
683 /* -------------------------
684  * we are the user here
685  */
686 int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
687 {
688     struct stat st;
689     char    *volname;
690     char        *p;
691
692     struct vol  *volume;
693     struct dir  *dir;
694     int     len, ret;
695     size_t  namelen;
696     uint16_t   bitmap;
697     char        path[ MAXPATHLEN + 1];
698     char        *vol_uname;
699     char        *vol_mname;
700     char        *volname_tmp;
701
702     ibuf += 2;
703     memcpy(&bitmap, ibuf, sizeof( bitmap ));
704     bitmap = ntohs( bitmap );
705     ibuf += sizeof( bitmap );
706
707     *rbuflen = 0;
708     if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
709         return AFPERR_BITMAP;
710     }
711
712     len = (unsigned char)*ibuf++;
713     volname = obj->oldtmp;
714
715     if ((volname_tmp = strchr(volname,'+')) != NULL)
716         volname = volname_tmp+1;
717
718     if (utf8_encoding(obj)) {
719         namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
720     } else {
721         namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
722     }
723
724     if ( namelen <= 0) {
725         return AFPERR_PARAM;
726     }
727
728     ibuf += len;
729     if ((len + 1) & 1) /* pad to an even boundary */
730         ibuf++;
731
732     load_volumes(obj, closevol);
733
734     for ( volume = getvolumes(); volume; volume = volume->v_next ) {
735         if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
736             break;
737         }
738     }
739
740     if ( volume == NULL ) {
741         return AFPERR_PARAM;
742     }
743
744     /* check for a volume password */
745     if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
746         return AFPERR_ACCESS;
747     }
748
749     if (( volume->v_flags & AFPVOL_OPEN  ) ) {
750         /* the volume is already open */
751         return stat_vol(obj, bitmap, volume, rbuf, rbuflen);
752     }
753
754     if (volume->v_root_preexec) {
755         if ((ret = afprun(1, volume->v_root_preexec, NULL)) && volume->v_root_preexec_close) {
756             LOG(log_error, logtype_afpd, "afp_openvol(%s): root preexec : %d", volume->v_path, ret );
757             return AFPERR_MISC;
758         }
759     }
760
761     if (volume->v_preexec) {
762         if ((ret = afprun(0, volume->v_preexec, NULL)) && volume->v_preexec_close) {
763             LOG(log_error, logtype_afpd, "afp_openvol(%s): preexec : %d", volume->v_path, ret );
764             return AFPERR_MISC;
765         }
766     }
767
768     if ( stat( volume->v_path, &st ) < 0 ) {
769         return AFPERR_PARAM;
770     }
771
772     if ( chdir( volume->v_path ) < 0 ) {
773         return AFPERR_PARAM;
774     }
775
776     if ( NULL == getcwd(path, MAXPATHLEN)) {
777         /* shouldn't be fatal but it will fail later */
778         LOG(log_error, logtype_afpd, "afp_openvol(%s): volume pathlen too long", volume->v_path);
779         return AFPERR_MISC;
780     }
781
782     /* Normalize volume path */
783 #ifdef REALPATH_TAKES_NULL
784     if ((volume->v_path = realpath(path, NULL)) == NULL)
785         return AFPERR_MISC;
786 #else
787     if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
788         return AFPERR_MISC;
789     if (realpath(path, volume->v_path) == NULL) {
790         free(volume->v_path);
791         return AFPERR_MISC;
792     }
793     /* Safe some memory */
794     char *tmp;
795     if ((tmp = strdup(volume->v_path)) == NULL) {
796         free(volume->v_path);
797         return AFPERR_MISC;
798     }
799     free(volume->v_path);
800     volume->v_path = tmp;
801 #endif
802
803     if (volume_codepage(obj, volume) < 0) {
804         ret = AFPERR_MISC;
805         goto openvol_err;
806     }
807
808     /* initialize volume variables
809      * FIXME file size
810      */
811     if (utf8_encoding(obj)) {
812         volume->max_filename = UTF8FILELEN_EARLY;
813     }
814     else {
815         volume->max_filename = MACFILELEN;
816     }
817
818     volume->v_flags |= AFPVOL_OPEN;
819     volume->v_cdb = NULL;
820
821     if (utf8_encoding(obj)) {
822         len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname);
823     } else {
824         len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname);
825     }
826     if ( !vol_mname || len <= 0) {
827         ret = AFPERR_MISC;
828         goto openvol_err;
829     }
830
831     if ((vol_uname = strrchr(path, '/')) == NULL)
832         vol_uname = path;
833     else if (*(vol_uname + 1) != '\0')
834         vol_uname++;
835
836     if ((dir = dir_new(vol_mname,
837                        vol_uname,
838                        volume,
839                        DIRDID_ROOT_PARENT,
840                        DIRDID_ROOT,
841                        bfromcstr(volume->v_path),
842                        &st)
843             ) == NULL) {
844         free(vol_mname);
845         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
846         ret = AFPERR_MISC;
847         goto openvol_err;
848     }
849     free(vol_mname);
850     volume->v_root = dir;
851     curdir = dir;
852
853     if (volume_openDB(obj, volume) < 0) {
854         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
855             volume->v_path, volume->v_cnidscheme);
856         ret = AFPERR_MISC;
857         goto openvol_err;
858     }
859
860     ret  = stat_vol(obj, bitmap, volume, rbuf, rbuflen);
861
862     if (ret == AFP_OK) {
863         /*
864          * If you mount a volume twice, the second time the trash appears on
865          * the desk-top.  That's because the Mac remembers the DID for the
866          * trash (even for volumes in different zones, on different servers).
867          * Just so this works better, we prime the DID cache with the trash,
868          * fixing the trash at DID 17.
869          * FIXME (RL): should it be done inside a CNID backend ? (always returning Trash DID when asked) ?
870          */
871         if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
872
873             /* FIXME find db time stamp */
874             if (cnid_getstamp(volume->v_cdb, volume->v_stamp, sizeof(volume->v_stamp)) < 0) {
875                 LOG (log_error, logtype_afpd,
876                      "afp_openvol(%s): Fatal error: Unable to get stamp value from CNID backend",
877                      volume->v_path);
878                 ret = AFPERR_MISC;
879                 goto openvol_err;
880             }
881         }
882         return( AFP_OK );
883     }
884
885 openvol_err:
886     if (volume->v_root) {
887         dir_free( volume->v_root );
888         volume->v_root = NULL;
889     }
890
891     volume->v_flags &= ~AFPVOL_OPEN;
892     if (volume->v_cdb != NULL) {
893         cnid_close(volume->v_cdb);
894         volume->v_cdb = NULL;
895     }
896     *rbuflen = 0;
897     return ret;
898 }
899
900 void closevol(struct vol *vol)
901 {
902     if (!vol)
903         return;
904
905     vol->v_flags &= ~AFPVOL_OPEN;
906
907     of_closevol(vol);
908
909     dir_free( vol->v_root );
910     vol->v_root = NULL;
911     if (vol->v_cdb != NULL) {
912         cnid_close(vol->v_cdb);
913         vol->v_cdb = NULL;
914     }
915
916     if (vol->v_postexec) {
917         afprun(0, vol->v_postexec, NULL);
918     }
919     if (vol->v_root_postexec) {
920         afprun(1, vol->v_root_postexec, NULL);
921     }
922
923     volume_free(vol);
924     volume_unlink(vol);
925     free(vol);
926 }
927
928 /* ------------------------- */
929 void close_all_vol(void)
930 {
931     struct vol  *ovol;
932     curdir = NULL;
933     for ( ovol = getvolumes(); ovol; ovol = ovol->v_next ) {
934         if ( (ovol->v_flags & AFPVOL_OPEN) ) {
935             ovol->v_flags &= ~AFPVOL_OPEN;
936             closevol(ovol);
937         }
938     }
939 }
940
941 /* ------------------------- */
942 int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
943 {
944     struct vol  *vol;
945     uint16_t   vid;
946
947     *rbuflen = 0;
948     ibuf += 2;
949     memcpy(&vid, ibuf, sizeof( vid ));
950     if (NULL == ( vol = getvolbyvid( vid )) ) {
951         return( AFPERR_PARAM );
952     }
953
954     (void)chdir("/");
955     closevol(vol);
956
957     return( AFP_OK );
958 }
959
960 /* --------------------------
961    poll if a volume is changed by other processes.
962    return
963    0 no attention msg sent
964    1 attention msg sent
965    -1 error (socket closed)
966
967    Note: if attention return -1 no packet has been
968    sent because the buffer is full, we don't care
969    either there's no reader or there's a lot of
970    traffic and another pollvoltime will follow
971 */
972 int  pollvoltime(AFPObj *obj)
973
974 {
975     struct vol       *vol;
976     struct timeval   tv;
977     struct stat      st;
978
979     if (!(obj->afp_version > 21 && obj->options.flags & OPTION_SERVERNOTIF))
980         return 0;
981
982     if ( gettimeofday( &tv, NULL ) < 0 )
983         return 0;
984
985     for ( vol = getvolumes(); vol; vol = vol->v_next ) {
986         if ( (vol->v_flags & AFPVOL_OPEN)  && vol->v_mtime + 30 < tv.tv_sec) {
987             if ( !stat( vol->v_path, &st ) && vol->v_mtime != st.st_mtime ) {
988                 vol->v_mtime = st.st_mtime;
989                 if (!obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED))
990                     return -1;
991                 return 1;
992             }
993         }
994     }
995     return 0;
996 }
997
998 /* ------------------------- */
999 void setvoltime(AFPObj *obj, struct vol *vol)
1000 {
1001     struct timeval  tv;
1002
1003     if ( gettimeofday( &tv, NULL ) < 0 ) {
1004         LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
1005         return;
1006     }
1007     if( utime( vol->v_path, NULL ) < 0 ) {
1008         /* write of time failed ... probably a read only filesys,
1009          * where no other users can interfere, so there's no issue here
1010          */
1011     }
1012
1013     /* a little granularity */
1014     if (vol->v_mtime < tv.tv_sec) {
1015         vol->v_mtime = tv.tv_sec;
1016         /* or finder doesn't update free space
1017          * AFP 3.2 and above clients seem to be ok without so many notification
1018          */
1019         if (obj->afp_version < 32 && obj->options.flags & OPTION_SERVERNOTIF) {
1020             obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
1021         }
1022     }
1023 }
1024
1025 /* ------------------------- */
1026 int afp_getvolparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen)
1027 {
1028     struct vol  *vol;
1029     uint16_t   vid, bitmap;
1030
1031     ibuf += 2;
1032     memcpy(&vid, ibuf, sizeof( vid ));
1033     ibuf += sizeof( vid );
1034     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1035     bitmap = ntohs( bitmap );
1036
1037     if (NULL == ( vol = getvolbyvid( vid )) ) {
1038         *rbuflen = 0;
1039         return( AFPERR_PARAM );
1040     }
1041
1042     return stat_vol(obj, bitmap, vol, rbuf, rbuflen);
1043 }
1044
1045 /* ------------------------- */
1046 int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,  size_t *rbuflen)
1047 {
1048     struct adouble ad;
1049     struct vol  *vol;
1050     uint16_t   vid, bitmap;
1051     uint32_t   aint;
1052
1053     ibuf += 2;
1054     *rbuflen = 0;
1055
1056     memcpy(&vid, ibuf, sizeof( vid ));
1057     ibuf += sizeof( vid );
1058     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1059     bitmap = ntohs( bitmap );
1060     ibuf += sizeof(bitmap);
1061
1062     if (( vol = getvolbyvid( vid )) == NULL ) {
1063         return( AFPERR_PARAM );
1064     }
1065
1066     if ((vol->v_flags & AFPVOL_RO))
1067         return AFPERR_VLOCK;
1068
1069     /* we can only set the backup date. */
1070     if (bitmap != (1 << VOLPBIT_BDATE))
1071         return AFPERR_BITMAP;
1072
1073     ad_init(&ad, vol);
1074     if ( ad_open(&ad,  vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) < 0 ) {
1075         if (errno == EROFS)
1076             return AFPERR_VLOCK;
1077
1078         return AFPERR_ACCESS;
1079     }
1080
1081     memcpy(&aint, ibuf, sizeof(aint));
1082     ad_setdate(&ad, AD_DATE_BACKUP, aint);
1083     ad_flush(&ad);
1084     ad_close(&ad, ADFLAGS_HF);
1085     return( AFP_OK );
1086 }