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