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