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