2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
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>
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>
45 #include <atalk/cnid.h>
48 #include "directory.h"
59 extern int afprun(int root, char *cmd, int *outfd);
61 typedef struct _special_folder {
68 static const _special_folder special_folders[] = {
69 {".AppleDesktop", 1, 0777, 0},
72 /* Forward declarations */
73 static void handle_special_folders (const struct vol *);
75 static void showvol(const ucs2_t *name)
77 struct vol *volume = getvolumes();
79 for ( ; volume; volume = volume->v_next ) {
80 if (volume->v_hide && !strcasecmp_w( volume->v_name, name ) ) {
87 static void closevol(struct vol *vol)
92 vol->v_flags &= ~AFPVOL_OPEN;
96 dir_free( vol->v_root );
98 if (vol->v_cdb != NULL) {
99 cnid_close(vol->v_cdb);
103 if (vol->v_postexec) {
104 afprun(0, vol->v_postexec, NULL);
106 if (vol->v_root_postexec) {
107 afprun(1, vol->v_root_postexec, NULL);
110 if (vol->v_deleted) {
111 showvol(vol->v_name);
119 * Read band-size info from Info.plist XML file of an TM sparsebundle
121 * @param path (r) path to Info.plist file
122 * @return band-size in bytes, -1 on error
124 static long long int get_tm_bandsize(const char *path)
129 long long int bandsize = -1;
131 EC_NULL_LOGSTR( file = fopen(path, "r"),
132 "get_tm_bandsize(\"%s\"): %s",
133 path, strerror(errno) );
135 while (fgets(buf, sizeof(buf), file) != NULL) {
136 if (strstr(buf, "band-size") == NULL)
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);
149 LOG(log_debug, logtype_afpd, "get_tm_bandsize(\"%s\"): bandsize: %lld", path, bandsize);
154 * Return number on entries in a directory
156 * @param path (r) path to dir
157 * @return number of entries, -1 on error
159 static long long int get_tm_bands(const char *path)
162 long long int count = 0;
164 const struct dirent *entry;
166 EC_NULL( dir = opendir(path) );
168 while ((entry = readdir(dir)) != NULL)
170 count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */
181 * Calculate used size of a TimeMachine volume
183 * This assumes that the volume is used only for TimeMachine.
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
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).
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).
199 * @param vol (rw) volume to calculate
200 * @return 0 on success, -1 on error
202 #define TM_USED_CACHETIME 60 /* cache for 60 seconds */
203 static int get_tm_used(struct vol * restrict vol)
206 long long int bandsize;
208 bstring infoplist = NULL;
209 bstring bandsdir = NULL;
211 const struct dirent *entry;
215 time_t now = time(NULL);
217 if (vol->v_tm_cachetime
218 && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) {
219 if (vol->v_tm_used == -1)
221 vol->v_tm_used += vol->v_appended;
223 LOG(log_debug, logtype_afpd, "getused(\"%s\"): cached: %" PRIu64 " bytes",
224 vol->v_path, vol->v_tm_used);
228 vol->v_tm_cachetime = now;
230 EC_NULL( dir = opendir(vol->v_path) );
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))) {
236 EC_NULL_LOG( infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist") );
238 if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1)
241 EC_NULL_LOG( bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands") );
243 if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1)
246 used += (links - 1) * bandsize;
247 LOG(log_debug, logtype_afpd, "getused(\"%s\"): bands: %" PRIu64 " bytes",
248 cfrombstr(bandsdir), used);
252 vol->v_tm_used = used;
262 LOG(log_debug, logtype_afpd, "getused(\"%s\"): %" PRIu64 " bytes", vol->v_path, vol->v_tm_used);
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)
274 #ifndef NO_QUOTA_SUPPORT
275 VolSpace qfree, qtotal;
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;
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;
291 if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) {
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;
305 vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
308 if (vol->v_limitsize) {
309 if (get_tm_used(vol) != 0)
312 *xbtotal = MIN(*xbtotal, (vol->v_limitsize * 1024 * 1024));
313 *xbfree = MIN(*xbfree, *xbtotal < vol->v_tm_used ? 0 : *xbtotal - vol->v_tm_used);
315 LOG(log_debug, logtype_afpd,
316 "volparams: total: %" PRIu64 ", used: %" PRIu64 ", free: %" PRIu64 " bytes",
317 *xbtotal, vol->v_tm_used, *xbfree);
320 *bfree = MIN(*xbfree, maxsize);
321 *btotal = MIN(*xbtotal, maxsize);
325 #define FCE_TM_DELTA 10 /* send notification every 10 seconds */
326 void vol_fce_tm_event(void)
329 time_t now = time(NULL);
330 struct vol *vol = getvolumes();
332 if ((last + FCE_TM_DELTA) < 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);
341 /* -----------------------
342 * set volume creation date
343 * avoid duplicate, well at least it tries
345 static void vol_setdate(uint16_t id, struct adouble *adp, time_t date)
348 struct vol *vol = getvolumes();
350 for ( volume = getvolumes(); volume; volume = volume->v_next ) {
351 if (volume->v_vid == id) {
354 else if ((time_t)(AD_DATE_FROM_UNIX(date)) == volume->v_ctime) {
356 volume = getvolumes(); /* restart */
359 vol->v_ctime = AD_DATE_FROM_UNIX(date);
360 ad_setdate(adp, AD_DATE_CREATE | AD_DATE_UNIX, date);
363 /* ----------------------- */
364 static int getvolparams(const AFPObj *obj, uint16_t bitmap, struct vol *vol, struct stat *st, char *buf, size_t *buflen)
367 int bit = 0, isad = 1;
370 uint32_t bfree, btotal, bsize;
371 VolSpace xbfree, xbtotal; /* extended bytes */
372 char *data, *nameoff = NULL;
375 LOG(log_debug, logtype_afpd, "getvolparams: Volume '%s'", vol->v_localname);
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. */
381 /* Convert adouble:v2 to adouble:ea on the fly */
382 (void)ad_convert(vol->v_path, st, vol);
385 if (ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0 ) {
387 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
389 } else if (ad_get_MD_flags( &ad ) & O_CREAT) {
390 slash = strrchr( 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 ));
400 vol_setdate(vol->v_vid, &ad, st->st_mtime);
404 if (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)
405 vol->v_ctime = AD_DATE_FROM_UNIX(st->st_mtime);
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 ) {
416 ad_close( &ad, ADFLAGS_HF );
418 return( AFPERR_PARAM );
423 while ( bitmap != 0 ) {
424 while (( bitmap & 1 ) == 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;
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;
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) {
456 ashort |= VOLPBIT_ATTR_EXT_ATTRS;
457 if (vol->v_flags & AFPVOL_ACLS)
458 ashort |= VOLPBIT_ATTR_ACLS;
462 ashort = htons(ashort);
463 memcpy(data, &ashort, sizeof( ashort ));
464 data += sizeof( ashort );
468 ashort = htons( AFPVOLSIG_DEFAULT );
469 memcpy(data, &ashort, sizeof( ashort ));
470 data += sizeof( ashort );
475 memcpy(data, &aint, sizeof( aint ));
476 data += sizeof( aint );
480 if ( st->st_mtime > vol->v_mtime ) {
481 vol->v_mtime = st->st_mtime;
483 aint = AD_DATE_FROM_UNIX(vol->v_mtime);
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
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 );
496 memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
497 data += sizeof( vol->v_vid );
501 bfree = htonl( bfree );
502 memcpy(data, &bfree, sizeof( bfree ));
503 data += sizeof( bfree );
506 case VOLPBIT_BTOTAL :
507 btotal = htonl( btotal );
508 memcpy(data, &btotal, sizeof( btotal ));
509 data += sizeof( btotal );
512 #ifndef NO_LARGE_VOL_SUPPORT
513 case VOLPBIT_XBFREE :
514 xbfree = hton64( xbfree );
515 memcpy(data, &xbfree, sizeof( xbfree ));
516 data += sizeof( xbfree );
519 case VOLPBIT_XBTOTAL :
520 xbtotal = hton64( xbtotal );
521 memcpy(data, &xbtotal, sizeof( xbtotal ));
522 data += sizeof( xbfree );
524 #endif /* ! NO_LARGE_VOL_SUPPORT */
528 data += sizeof( uint16_t );
531 case VOLPBIT_BSIZE: /* block size */
532 bsize = htonl(bsize);
533 memcpy(data, &bsize, sizeof(bsize));
534 data += sizeof(bsize);
539 ad_close( &ad, ADFLAGS_HF );
541 return( AFPERR_BITMAP );
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);
560 ad_close(&ad, ADFLAGS_HF);
562 *buflen = data - buf;
566 /* ------------------------- */
567 static int stat_vol(const AFPObj *obj, uint16_t bitmap, struct vol *vol, char *rbuf, size_t *rbuflen)
573 if ( stat( vol->v_path, &st ) < 0 ) {
575 return( AFPERR_PARAM );
577 /* save the volume device number */
578 vol->v_dev = st.st_dev;
580 buflen = *rbuflen - sizeof( bitmap );
581 if (( ret = getvolparams(obj, bitmap, vol, &st,
582 rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
586 *rbuflen = buflen + sizeof( bitmap );
587 bitmap = htons( bitmap );
588 memcpy(rbuf, &bitmap, sizeof( bitmap ));
593 /* ------------------------------- */
594 int afp_getsrvrparms(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
604 load_volumes(obj, of_closevol);
607 for ( vcnt = 0, volume = getvolumes(); volume; volume = volume->v_next ) {
608 if (!(volume->v_flags & AFPVOL_NOSTAT)) {
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 */
616 if (!S_ISDIR(st.st_mode)) {
617 continue; /* not a dir */
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 */
624 if (volume->v_hide) {
625 continue; /* config file changed but the volume was mounted */
628 if (utf8_encoding()) {
629 len = ucs2_to_charset_allocate(CH_UTF8_MAC, &namebuf, volume->v_u8mname);
631 len = ucs2_to_charset_allocate(obj->options.maccharset, &namebuf, volume->v_macname);
634 if (len == (size_t)-1)
637 /* set password bit if there's a volume password */
638 *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
640 *data++ |= 0; /* UNIX PRIVS BIT ..., OSX doesn't seem to use it, so we don't either */
642 memcpy(data, namebuf, len );
648 *rbuflen = data - rbuf;
650 if ( gettimeofday( &tv, NULL ) < 0 ) {
651 LOG(log_error, logtype_afpd, "afp_getsrvrparms(%s): gettimeofday: %s", volume->v_path, strerror(errno) );
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);
662 /* ------------------------- */
663 static int volume_codepage(AFPObj *obj, struct vol *volume)
665 struct charset_functions *charset;
668 if (!volume->v_volcodepage)
669 volume->v_volcodepage = strdup("UTF8");
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);
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);
680 if (!volume->v_maccodepage)
681 volume->v_maccodepage = strdup(obj->options.maccodepage);
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);
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);
692 volume->v_kTextEncoding = htonl(charset->kTextEncoding);
696 /* ------------------------- */
697 static int volume_openDB(const AFPObj *obj, struct vol *volume)
701 if ((volume->v_flags & AFPVOL_NODEV)) {
702 flags |= CNID_FLAG_NODEV;
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);
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);
715 volume->v_cdb = cnid_open(volume->v_path,
717 volume->v_cnidscheme,
719 volume->v_cnidserver ? volume->v_cnidserver : obj->options.Cnid_srv,
720 volume->v_cnidport ? volume->v_cnidport : obj->options.Cnid_port);
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.",
728 flags |= CNID_FLAG_MEMORY;
729 volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
731 /* kill ourself with SIGUSR2 aka msg pending */
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 */
741 return (!volume->v_cdb)?-1:0;
744 /* -------------------------
745 * we are the user here
747 int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
758 char path[ MAXPATHLEN + 1];
764 memcpy(&bitmap, ibuf, sizeof( bitmap ));
765 bitmap = ntohs( bitmap );
766 ibuf += sizeof( bitmap );
769 if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
770 return AFPERR_BITMAP;
773 len = (unsigned char)*ibuf++;
774 volname = obj->oldtmp;
776 if ((volname_tmp = strchr(volname,'+')) != NULL)
777 volname = volname_tmp+1;
779 if (utf8_encoding()) {
780 namelen = convert_string(CH_UTF8_MAC, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
782 namelen = convert_string(obj->options.maccharset, CH_UCS2, ibuf, len, volname, sizeof(obj->oldtmp));
790 if ((len + 1) & 1) /* pad to an even boundary */
793 load_volumes(obj, of_closevol);
795 for ( volume = getvolumes(); volume; volume = volume->v_next ) {
796 if ( strcasecmp_w( (ucs2_t*) volname, volume->v_name ) == 0 ) {
801 if ( volume == NULL ) {
805 /* check for a volume password */
806 if (volume->v_password && strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
807 return AFPERR_ACCESS;
810 if (( volume->v_flags & AFPVOL_OPEN ) ) {
811 /* the volume is already open */
812 return stat_vol(obj, bitmap, volume, rbuf, rbuflen);
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 );
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 );
829 if ( stat( volume->v_path, &st ) < 0 ) {
833 if ( chdir( volume->v_path ) < 0 ) {
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);
843 /* Normalize volume path */
844 #ifdef REALPATH_TAKES_NULL
845 if ((volume->v_path = realpath(path, NULL)) == NULL)
848 if ((volume->v_path = malloc(MAXPATHLEN+1)) == NULL)
850 if (realpath(path, volume->v_path) == NULL) {
851 free(volume->v_path);
854 /* Safe some memory */
856 if ((tmp = strdup(volume->v_path)) == NULL) {
857 free(volume->v_path);
860 free(volume->v_path);
861 volume->v_path = tmp;
864 if (volume_codepage(obj, volume) < 0) {
869 /* initialize volume variables
872 if (utf8_encoding()) {
873 volume->max_filename = UTF8FILELEN_EARLY;
876 volume->max_filename = MACFILELEN;
879 volume->v_flags |= AFPVOL_OPEN;
880 volume->v_cdb = NULL;
882 if (utf8_encoding()) {
883 len = convert_string_allocate(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, namelen, &vol_mname);
885 len = convert_string_allocate(CH_UCS2, obj->options.maccharset, volume->v_macname, namelen, &vol_mname);
887 if ( !vol_mname || len <= 0) {
892 if ((vol_uname = strrchr(path, '/')) == NULL)
894 else if (*(vol_uname + 1) != '\0')
897 if ((dir = dir_new(vol_mname,
902 bfromcstr(volume->v_path),
906 LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
911 volume->v_root = dir;
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);
921 ret = stat_vol(obj, bitmap, volume, rbuf, rbuflen);
924 handle_special_folders(volume);
926 volume->v_cnidserver ? volume->v_cnidserver : obj->options.Cnid_srv,
927 volume->v_cnidport ? volume->v_cnidport : obj->options.Cnid_port);
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) ?
938 if ((volume->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
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",
953 if (volume->v_root) {
954 dir_free( volume->v_root );
955 volume->v_root = NULL;
958 volume->v_flags &= ~AFPVOL_OPEN;
959 if (volume->v_cdb != NULL) {
960 cnid_close(volume->v_cdb);
961 volume->v_cdb = NULL;
967 /* ------------------------- */
968 void close_all_vol(void)
972 for ( ovol = getvolumes(); ovol; ovol = ovol->v_next ) {
973 if ( (ovol->v_flags & AFPVOL_OPEN) ) {
974 ovol->v_flags &= ~AFPVOL_OPEN;
980 /* ------------------------- */
981 int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
988 memcpy(&vid, ibuf, sizeof( vid ));
989 if (NULL == ( vol = getvolbyvid( vid )) ) {
990 return( AFPERR_PARAM );
999 /* --------------------------
1000 poll if a volume is changed by other processes.
1002 0 no attention msg sent
1003 1 attention msg sent
1004 -1 error (socket closed)
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
1011 int pollvoltime(AFPObj *obj)
1018 if (!(afp_version > 21 && obj->options.flags & OPTION_SERVERNOTIF))
1021 if ( gettimeofday( &tv, NULL ) < 0 )
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))
1037 /* ------------------------- */
1038 void setvoltime(AFPObj *obj, struct vol *vol)
1042 if ( gettimeofday( &tv, NULL ) < 0 ) {
1043 LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
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
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
1058 if (afp_version < 32 && obj->options.flags & OPTION_SERVERNOTIF) {
1059 obj->attention(obj->dsi, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
1064 /* ------------------------- */
1065 int afp_getvolparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_,char *rbuf, size_t *rbuflen)
1068 uint16_t vid, bitmap;
1071 memcpy(&vid, ibuf, sizeof( vid ));
1072 ibuf += sizeof( vid );
1073 memcpy(&bitmap, ibuf, sizeof( bitmap ));
1074 bitmap = ntohs( bitmap );
1076 if (NULL == ( vol = getvolbyvid( vid )) ) {
1078 return( AFPERR_PARAM );
1081 return stat_vol(obj, bitmap, vol, rbuf, rbuflen);
1084 /* ------------------------- */
1085 int afp_setvolparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1089 uint16_t vid, bitmap;
1095 memcpy(&vid, ibuf, sizeof( vid ));
1096 ibuf += sizeof( vid );
1097 memcpy(&bitmap, ibuf, sizeof( bitmap ));
1098 bitmap = ntohs( bitmap );
1099 ibuf += sizeof(bitmap);
1101 if (( vol = getvolbyvid( vid )) == NULL ) {
1102 return( AFPERR_PARAM );
1105 if ((vol->v_flags & AFPVOL_RO))
1106 return AFPERR_VLOCK;
1108 /* we can only set the backup date. */
1109 if (bitmap != (1 << VOLPBIT_BDATE))
1110 return AFPERR_BITMAP;
1113 if ( ad_open(&ad, vol->v_path, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) < 0 ) {
1115 return AFPERR_VLOCK;
1117 return AFPERR_ACCESS;
1120 memcpy(&aint, ibuf, sizeof(aint));
1121 ad_setdate(&ad, AD_DATE_BACKUP, aint);
1123 ad_close(&ad, ADFLAGS_HF);
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
1132 static int create_special_folder (const struct vol *vol, const struct _special_folder *folder)
1141 p = (char *) malloc ( strlen(vol->v_path)+strlen(folder->name)+2);
1143 LOG(log_error, logtype_afpd,"malloc failed");
1147 q=strdup(folder->name);
1149 LOG(log_error, logtype_afpd,"malloc failed");
1153 strcpy(p, vol->v_path);
1158 if ((vol->v_casefold & AFPVOL_MTOUUPPER))
1160 else if ((vol->v_casefold & AFPVOL_MTOULOWER))
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));
1178 if ( !ret && folder->hide) {
1181 if (ad_open(&ad, p, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) != 0) {
1187 ad_setname(&ad, folder->name);
1189 ad_getattr(&ad, &attr);
1190 attr |= htons( ntohs( attr ) | ATTRBIT_INVISIBLE );
1191 ad_setattr(&ad, attr);
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));
1201 ad_close(&ad, ADFLAGS_HF);
1208 static void handle_special_folders (const struct vol * vol)
1210 const _special_folder *p = &special_folders[0];
1214 for (; p->name != NULL; p++) {
1215 create_special_folder (vol, p);