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