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