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