• 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 * Copyright (c) 2010      Frank Lahm
4 *
5 * All Rights Reserved.  See COPYRIGHT.
6 */
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif /* HAVE_CONFIG_H */
11
12#include <stdio.h>
13#include <string.h>
14#include <errno.h>
15#include <sys/param.h>
16#include <sys/socket.h>
17#include <inttypes.h>
18
19#include <atalk/dsi.h>
20#include <atalk/afp.h>
21#include <atalk/adouble.h>
22#include <atalk/logger.h>
23#include <atalk/util.h>
24#include <atalk/cnid.h>
25#include <atalk/bstrlib.h>
26#include <atalk/bstradd.h>
27#include <atalk/globals.h>
28#include <atalk/netatalk_conf.h>
29#include <atalk/ea.h>
30
31#include "fork.h"
32#include "file.h"
33#include "directory.h"
34#include "desktop.h"
35#include "volume.h"
36
37#ifdef AFS
38struct ofork *writtenfork;
39#endif
40
41static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap, char *buf, size_t *buflen)
42{
43    struct path         path;
44    struct stat     *st;
45
46    struct adouble  *adp;
47    struct dir      *dir;
48    struct vol      *vol;
49
50
51    /* can only get the length of the opened fork */
52    if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN)))
53           && (ofork->of_flags & AFPFORK_RSRC))
54         ||
55         ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN)))
56           && (ofork->of_flags & AFPFORK_DATA))) {
57        return( AFPERR_BITMAP );
58    }
59
60    if (! AD_META_OPEN(ofork->of_ad)) {
61        adp = NULL;
62    } else {
63        adp = ofork->of_ad;
64    }
65
66    vol = ofork->of_vol;
67    dir = dirlookup(vol, ofork->of_did);
68
69    if (NULL == (path.m_name = utompath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) {
70        return( AFPERR_MISC );
71    }
72    path.u_name = of_name(ofork);
73    path.id = 0;
74    st = &path.st;
75    if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) |
76                    (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) |
77                    (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) {
78        if ( ad_data_fileno( ofork->of_ad ) <= 0 ) {
79            /* 0 is for symlink */
80            if (movecwd(vol, dir) < 0)
81                return( AFPERR_NOOBJ );
82            if ( lstat( path.u_name, st ) < 0 )
83                return( AFPERR_NOOBJ );
84        } else {
85            if ( fstat( ad_data_fileno( ofork->of_ad ), st ) < 0 ) {
86                return( AFPERR_BITMAP );
87            }
88        }
89    }
90    return getmetadata(obj, vol, bitmap, &path, dir, buf, buflen, adp );
91}
92
93static off_t get_off_t(char **ibuf, int is64)
94{
95    uint32_t             temp;
96    off_t                 ret;
97
98    ret = 0;
99    memcpy(&temp, *ibuf, sizeof( temp ));
100    ret = ntohl(temp); /* ntohl is unsigned */
101    *ibuf += sizeof(temp);
102
103    if (is64) {
104        memcpy(&temp, *ibuf, sizeof( temp ));
105        *ibuf += sizeof(temp);
106        ret = ntohl(temp)| (ret << 32);
107    }
108    else {
109        ret = (int)ret; /* sign extend */
110    }
111    return ret;
112}
113
114static int set_off_t(off_t offset, char *rbuf, int is64)
115{
116    uint32_t  temp;
117    int        ret;
118
119    ret = 0;
120    if (is64) {
121        temp = htonl(offset >> 32);
122        memcpy(rbuf, &temp, sizeof( temp ));
123        rbuf += sizeof(temp);
124        ret = sizeof( temp );
125        offset &= 0xffffffff;
126    }
127    temp = htonl(offset);
128    memcpy(rbuf, &temp, sizeof( temp ));
129    ret += sizeof( temp );
130
131    return ret;
132}
133
134static int is_neg(int is64, off_t val)
135{
136    if (val < 0 || (sizeof(off_t) == 8 && !is64 && (val & 0x80000000U)))
137        return 1;
138    return 0;
139}
140
141static int sum_neg(int is64, off_t offset, off_t reqcount)
142{
143    if (is_neg(is64, offset +reqcount) )
144        return 1;
145    return 0;
146}
147
148static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int access, int ofrefnum)
149{
150    int ret;
151    int readset;
152    int writeset;
153    int denyreadset;
154    int denywriteset;
155
156#ifdef HAVE_FSHARE_T
157    fshare_t shmd;
158
159    if (obj->options.flags & OPTION_SHARE_RESERV) {
160        shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0);
161        if (shmd.f_access == 0)
162            /* we must give an access mode, otherwise fcntl will complain */
163            shmd.f_access = F_RDACC;
164        shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0;
165        shmd.f_id = ofrefnum;
166
167        int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp);
168
169        if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) {
170            LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno));
171            errno = EACCES;
172            return -1;
173        }
174    }
175#endif
176
177    if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) {
178        return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum);
179    }
180
181    if ((access & (OPENACC_RD | OPENACC_DRD))) {
182        if ((readset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_RD)) <0)
183            return readset;
184        if ((denyreadset = ad_testlock(adp, eid, AD_FILELOCK_DENY_RD)) <0)
185            return denyreadset;
186
187        if ((access & OPENACC_RD) && denyreadset) {
188            errno = EACCES;
189            return -1;
190        }
191        if ((access & OPENACC_DRD) && readset) {
192            errno = EACCES;
193            return -1;
194        }
195        /* boolean logic is not enough, because getforkmode is not always telling the
196         * true
197         */
198        if ((access & OPENACC_RD)) {
199            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_RD, 1, ofrefnum);
200            if (ret)
201                return ret;
202        }
203        if ((access & OPENACC_DRD)) {
204            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_RD, 1, ofrefnum);
205            if (ret)
206                return ret;
207        }
208    }
209    /* ------------same for writing -------------- */
210    if ((access & (OPENACC_WR | OPENACC_DWR))) {
211        if ((writeset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_WR)) <0)
212            return writeset;
213        if ((denywriteset = ad_testlock(adp, eid, AD_FILELOCK_DENY_WR)) <0)
214            return denywriteset;
215
216        if ((access & OPENACC_WR) && denywriteset) {
217            errno = EACCES;
218            return -1;
219        }
220        if ((access & OPENACC_DWR) && writeset) {
221            errno = EACCES;
222            return -1;
223        }
224        if ((access & OPENACC_WR)) {
225            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_WR, 1, ofrefnum);
226            if (ret)
227                return ret;
228        }
229        if ((access & OPENACC_DWR)) {
230            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_WR, 1, ofrefnum);
231            if (ret)
232                return ret;
233        }
234    }
235
236    return 0;
237}
238
239/* ----------------------- */
240int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
241{
242    struct vol      *vol;
243    struct dir      *dir;
244    struct ofork    *ofork, *opened;
245    struct adouble  *adsame = NULL;
246    size_t          buflen;
247    int             ret, adflags, eid;
248    uint32_t        did;
249    uint16_t        vid, bitmap, access, ofrefnum;
250    char            fork, *path, *upath;
251    struct stat     *st;
252    uint16_t        bshort;
253    struct path     *s_path;
254
255    ibuf++;
256    fork = *ibuf++;
257    memcpy(&vid, ibuf, sizeof( vid ));
258    ibuf += sizeof(vid);
259
260    *rbuflen = 0;
261    if (NULL == ( vol = getvolbyvid( vid ))) {
262        return( AFPERR_PARAM );
263    }
264
265    memcpy(&did, ibuf, sizeof( did ));
266    ibuf += sizeof( int );
267
268    if (NULL == ( dir = dirlookup( vol, did ))) {
269        return afp_errno;
270    }
271
272    memcpy(&bitmap, ibuf, sizeof( bitmap ));
273    bitmap = ntohs( bitmap );
274    ibuf += sizeof( bitmap );
275    memcpy(&access, ibuf, sizeof( access ));
276    access = ntohs( access );
277    ibuf += sizeof( access );
278
279    if ((vol->v_flags & AFPVOL_RO) && (access & OPENACC_WR)) {
280        return AFPERR_VLOCK;
281    }
282
283    if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
284        return get_afp_errno(AFPERR_PARAM);
285    }
286
287    if (*s_path->m_name == '\0') {
288        /* it's a dir ! */
289        return  AFPERR_BADTYPE;
290    }
291
292    /* stat() data fork st is set because it's not a dir */
293    switch ( s_path->st_errno ) {
294    case 0:
295        break;
296    case ENOENT:
297        return AFPERR_NOOBJ;
298    case EACCES:
299        return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
300    default:
301        LOG(log_error, logtype_afpd, "afp_openfork(%s): %s", s_path->m_name, strerror(errno));
302        return AFPERR_PARAM;
303    }
304
305    upath = s_path->u_name;
306    path = s_path->m_name;
307    st = &s_path->st;
308
309    if (!vol_unix_priv(vol)) {
310        if (check_access(obj, vol, upath, access ) < 0) {
311            return AFPERR_ACCESS;
312        }
313    } else {
314        if (file_access(obj, vol, s_path, access ) < 0) {
315            return AFPERR_ACCESS;
316        }
317    }
318
319    if ((opened = of_findname(vol, s_path))) {
320        adsame = opened->of_ad;
321    }
322
323    adflags = ADFLAGS_SETSHRMD;
324
325    if (fork == OPENFORK_DATA) {
326        eid = ADEID_DFORK;
327        adflags |= ADFLAGS_DF;
328    } else {
329        eid = ADEID_RFORK;
330        adflags |= ADFLAGS_RF;
331    }
332
333    if (access & OPENACC_WR) {
334        adflags |= ADFLAGS_RDWR;
335        if (fork != OPENFORK_DATA)
336            /*
337             * We only try to create the resource
338             * fork if the user wants to open it for write acess.
339             */
340            adflags |= ADFLAGS_CREATE;
341    } else {
342        adflags |= ADFLAGS_RDONLY;
343    }
344
345    if ((ofork = of_alloc(vol, curdir, path, &ofrefnum, eid, adsame, st)) == NULL)
346        return AFPERR_NFILE;
347
348    LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\", %s, %s)",
349        fullpathname(s_path->u_name),
350        (fork == OPENFORK_DATA) ? "data" : "reso",
351        !(access & OPENACC_WR) ? "O_RDONLY" : "O_RDWR");
352
353    ret = AFPERR_NOOBJ;
354
355    /* First ad_open(), opens data or ressource fork */
356    if (ad_open(ofork->of_ad, upath, adflags, 0666) < 0) {
357        switch (errno) {
358        case EROFS:
359            ret = AFPERR_VLOCK;
360        case EACCES:
361            goto openfork_err;
362        case ENOENT:
363            goto openfork_err;
364        case EMFILE :
365        case ENFILE :
366            ret = AFPERR_NFILE;
367            goto openfork_err;
368        case EISDIR :
369            ret = AFPERR_BADTYPE;
370            goto openfork_err;
371        default:
372            LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
373            ret = AFPERR_PARAM;
374            goto openfork_err;
375        }
376    }
377
378    /*
379     * Create metadata if we open rw, otherwise only open existing metadata
380     */
381    if (access & OPENACC_WR) {
382        adflags = ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE;
383    } else {
384        adflags = ADFLAGS_HF | ADFLAGS_RDONLY;
385    }
386
387    if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
388        ofork->of_flags |= AFPFORK_META;
389        if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) {
390            LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath);
391            cnid_t id;
392            if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
393                LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
394                goto openfork_err;
395            }
396            (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp);
397            ad_flush(ofork->of_ad);
398        }
399    } else {
400        switch (errno) {
401        case EACCES:
402        case ENOENT:
403            /* no metadata? We don't care! */
404            break;
405        case EROFS:
406            ret = AFPERR_VLOCK;
407        case EMFILE :
408        case ENFILE :
409            ret = AFPERR_NFILE;
410            goto openfork_err;
411        case EISDIR :
412            ret = AFPERR_BADTYPE;
413            goto openfork_err;
414        default:
415            LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
416            ret = AFPERR_PARAM;
417            goto openfork_err;
418        }
419    }
420
421    if ((adflags & ADFLAGS_RF) && (ad_get_RF_flags( ofork->of_ad) & O_CREAT)) {
422        if (ad_setname(ofork->of_ad, path)) {
423            ad_flush( ofork->of_ad );
424        }
425    }
426
427    if ((ret = getforkparams(obj, ofork, bitmap, rbuf + 2 * sizeof(int16_t), &buflen)) != AFP_OK) {
428        ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
429        goto openfork_err;
430    }
431
432    *rbuflen = buflen + 2 * sizeof( uint16_t );
433    bitmap = htons( bitmap );
434    memcpy(rbuf, &bitmap, sizeof( uint16_t ));
435    rbuf += sizeof( uint16_t );
436
437    /* check  WriteInhibit bit if we have a ressource fork
438     * the test is done here, after some Mac trafic capture
439     */
440    if (ad_meta_fileno(ofork->of_ad) != -1) {   /* META */
441        ad_getattr(ofork->of_ad, &bshort);
442        if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
443            ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
444            of_dealloc(ofork);
445            ofrefnum = 0;
446            memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
447            return(AFPERR_OLOCK);
448        }
449    }
450
451    /*
452     * synchronization locks:
453     */
454
455    /* don't try to lock non-existent rforks. */
456    if ((eid == ADEID_DFORK)
457        || (ad_reso_fileno(ofork->of_ad) != -1)
458        || (ofork->of_ad->ad_vers == AD_VERSION_EA)) {
459        ret = fork_setmode(obj, ofork->of_ad, eid, access, ofrefnum);
460        /* can we access the fork? */
461        if (ret < 0) {
462            ofork->of_flags |= AFPFORK_ERROR;
463            ret = errno;
464            ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
465            of_dealloc(ofork);
466            switch (ret) {
467            case EAGAIN: /* return data anyway */
468            case EACCES:
469            case EINVAL:
470                ofrefnum = 0;
471                memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
472                return( AFPERR_DENYCONF );
473                break;
474            default:
475                *rbuflen = 0;
476                LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_lock: %s", s_path->m_name, strerror(ret) );
477                return( AFPERR_PARAM );
478            }
479        }
480        if ((access & OPENACC_WR))
481            ofork->of_flags |= AFPFORK_ACCWR;
482    }
483    /* the file may be open read only without ressource fork */
484    if ((access & OPENACC_RD))
485        ofork->of_flags |= AFPFORK_ACCRD;
486
487    LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): fork: %" PRIu16,
488        fullpathname(s_path->m_name), ofork->of_refnum);
489
490    memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
491    return( AFP_OK );
492
493openfork_err:
494    of_dealloc(ofork);
495    if (errno == EACCES)
496        return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
497    return ret;
498}
499
500int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
501{
502    struct ofork    *ofork;
503    off_t       size;
504    uint16_t       ofrefnum, bitmap;
505    int                 err;
506    int                 is64;
507    int                 eid;
508    off_t       st_size;
509
510    ibuf += 2;
511
512    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
513    ibuf += sizeof( ofrefnum );
514
515    memcpy(&bitmap, ibuf, sizeof(bitmap));
516    bitmap = ntohs(bitmap);
517    ibuf += sizeof( bitmap );
518
519    *rbuflen = 0;
520    if (NULL == ( ofork = of_find( ofrefnum )) ) {
521        LOG(log_error, logtype_afpd, "afp_setforkparams: of_find(%d) could not locate fork", ofrefnum );
522        return( AFPERR_PARAM );
523    }
524
525    if (ofork->of_vol->v_flags & AFPVOL_RO)
526        return AFPERR_VLOCK;
527
528    if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
529        return AFPERR_ACCESS;
530
531    if ( ofork->of_flags & AFPFORK_DATA) {
532        eid = ADEID_DFORK;
533    } else if (ofork->of_flags & AFPFORK_RSRC) {
534        eid = ADEID_RFORK;
535    } else
536        return AFPERR_PARAM;
537
538    if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) ))
539           && eid == ADEID_RFORK
540             ) ||
541         ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) ))
542           && eid == ADEID_DFORK)) {
543        return AFPERR_BITMAP;
544    }
545
546    is64 = 0;
547    if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
548        if (obj->afp_version >= 30) {
549            is64 = 4;
550        }
551        else
552            return AFPERR_BITMAP;
553    }
554
555    if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
556        return AFPERR_PARAM ;
557
558    size = get_off_t(&ibuf, is64);
559
560    if (size < 0)
561        return AFPERR_PARAM; /* Some MacOS don't return an error they just don't change the size! */
562
563
564    if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
565        st_size = ad_size(ofork->of_ad, eid);
566        err = -2;
567        if (st_size > size &&
568            ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0)
569            goto afp_setfork_err;
570
571        err = ad_dtruncate( ofork->of_ad, size );
572        if (st_size > size)
573            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
574        if (err < 0)
575            goto afp_setfork_err;
576    } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
577        ad_refresh(NULL, ofork->of_ad );
578
579        st_size = ad_size(ofork->of_ad, eid);
580        err = -2;
581        if (st_size > size &&
582            ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
583            goto afp_setfork_err;
584        }
585
586        err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size);
587        if (st_size > size)
588            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
589        if (err < 0)
590            goto afp_setfork_err;
591
592        if (ad_flush( ofork->of_ad ) < 0) {
593            LOG(log_error, logtype_afpd, "afp_setforkparams(%s): ad_flush: %s", of_name(ofork), strerror(errno) );
594            return AFPERR_PARAM;
595        }
596    } else
597        return AFPERR_BITMAP;
598
599#ifdef AFS
600    if ( flushfork( ofork ) < 0 ) {
601        LOG(log_error, logtype_afpd, "afp_setforkparams(%s): flushfork: %s", of_name(ofork), strerror(errno) );
602    }
603#endif /* AFS */
604
605    return( AFP_OK );
606
607afp_setfork_err:
608    if (err == -2)
609        return AFPERR_LOCK;
610    else {
611        switch (errno) {
612        case EROFS:
613            return AFPERR_VLOCK;
614        case EPERM:
615        case EACCES:
616            return AFPERR_ACCESS;
617        case EDQUOT:
618        case EFBIG:
619        case ENOSPC:
620            LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL");
621            return AFPERR_DFULL;
622        default:
623            return AFPERR_PARAM;
624        }
625    }
626}
627
628/* for this to work correctly, we need to check for locks before each
629 * read and write. that's most easily handled by always doing an
630 * appropriate check before each ad_read/ad_write. other things
631 * that can change files like truncate are handled internally to those
632 * functions.
633 */
634#define ENDBIT(a)  ((a) & 0x80)
635#define UNLOCKBIT(a) ((a) & 0x01)
636
637
638/* ---------------------- */
639static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
640{
641    struct ofork    *ofork;
642    off_t               offset, length;
643    int                 eid;
644    uint16_t       ofrefnum;
645    uint8_t            flags;
646    int                 lockop;
647
648    *rbuflen = 0;
649
650    /* figure out parameters */
651    ibuf++;
652    flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
653    ibuf++;
654    memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
655    ibuf += sizeof(ofrefnum);
656
657    if (NULL == ( ofork = of_find( ofrefnum )) ) {
658        LOG(log_error, logtype_afpd, "byte_lock: of_find(%d) could not locate fork", ofrefnum );
659        return( AFPERR_PARAM );
660    }
661
662    if ( ofork->of_flags & AFPFORK_DATA) {
663        eid = ADEID_DFORK;
664    } else if (ofork->of_flags & AFPFORK_RSRC) {
665        eid = ADEID_RFORK;
666    } else
667        return AFPERR_PARAM;
668
669    offset = get_off_t(&ibuf, is64);
670    length = get_off_t(&ibuf, is64);
671
672    if (length == -1)
673        length = BYTELOCK_MAX;
674    else if (!length || is_neg(is64, length)) {
675        return AFPERR_PARAM;
676    } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
677        return AFPERR_LOCK;
678    }
679
680    if (ENDBIT(flags)) {
681        offset += ad_size(ofork->of_ad, eid);
682        /* FIXME what do we do if file size > 2 GB and
683           it's not byte_lock_ext?
684        */
685    }
686    if (offset < 0)    /* error if we have a negative offset */
687        return AFPERR_PARAM;
688
689    /* if the file is a read-only file, we use read locks instead of
690     * write locks. that way, we can prevent anyone from initiating
691     * a write lock. */
692    lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
693    if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
694                ofork->of_refnum) < 0) {
695        switch (errno) {
696        case EACCES:
697        case EAGAIN:
698            return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
699            break;
700        case ENOLCK:
701            return AFPERR_NLOCK;
702            break;
703        case EINVAL:
704            return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
705            break;
706        case EBADF:
707        default:
708            return AFPERR_PARAM;
709            break;
710        }
711    }
712    *rbuflen = set_off_t (offset, rbuf, is64);
713    return( AFP_OK );
714}
715
716/* --------------------------- */
717int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
718{
719    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
720}
721
722/* --------------------------- */
723int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
724{
725    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
726}
727
728#undef UNLOCKBIT
729
730/*!
731 * Read *rbuflen bytes from fork at offset
732 *
733 * @param ofork    (r)  fork handle
734 * @param eid      (r)  data fork or ressource fork entry id
735 * @param offset   (r)  offset
736 * @param rbuf     (r)  data buffer
737 * @param rbuflen  (rw) in: number of bytes to read, out: bytes read
738 *
739 * @return         AFP status code
740 */
741static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
742{
743    ssize_t cc;
744
745    cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
746    if ( cc < 0 ) {
747        LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
748        *rbuflen = 0;
749        return( AFPERR_PARAM );
750    }
751    *rbuflen = cc;
752
753    if ((size_t)cc < *rbuflen)
754        return AFPERR_EOF;
755    return AFP_OK;
756}
757
758static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
759{
760    DSI          *dsi = obj->dsi;
761    struct ofork *ofork;
762    off_t        offset, saveoff, reqcount, savereqcount, size;
763    ssize_t      cc, err;
764    int          eid;
765    uint16_t     ofrefnum;
766
767    /* we break the AFP spec here by not supporting nlmask and nlchar anymore */
768
769    ibuf += 2;
770    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
771    ibuf += sizeof( u_short );
772
773    if (NULL == ( ofork = of_find( ofrefnum )) ) {
774        LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
775        err = AFPERR_PARAM;
776        goto afp_read_err;
777    }
778
779    if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
780        err = AFPERR_ACCESS;
781        goto afp_read_err;
782    }
783
784    if ( ofork->of_flags & AFPFORK_DATA) {
785        eid = ADEID_DFORK;
786    } else if (ofork->of_flags & AFPFORK_RSRC) {
787        eid = ADEID_RFORK;
788    } else { /* fork wasn't opened. this should never really happen. */
789        err = AFPERR_ACCESS;
790        goto afp_read_err;
791    }
792
793    offset   = get_off_t(&ibuf, is64);
794    reqcount = get_off_t(&ibuf, is64);
795
796    /* zero request count */
797    err = AFP_OK;
798    if (!reqcount) {
799        goto afp_read_err;
800    }
801
802    AFP_READ_START((long)reqcount);
803
804    /* reqcount isn't always truthful. we need to deal with that. */
805    size = ad_size(ofork->of_ad, eid);
806
807    LOG(log_debug, logtype_afpd,
808        "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")",
809        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size);
810
811    if (offset >= size) {
812        err = AFPERR_EOF;
813        goto afp_read_err;
814    }
815
816    /* subtract off the offset */
817    if (reqcount + offset > size) {
818        reqcount = size - offset;
819        err = AFPERR_EOF;
820    }
821
822    savereqcount = reqcount;
823    saveoff = offset;
824
825    if (reqcount < 0 || offset < 0) {
826        err = AFPERR_PARAM;
827        goto afp_read_err;
828    }
829
830    if (obj->options.flags & OPTION_AFP_READ_LOCK) {
831        if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, offset, reqcount, ofork->of_refnum) < 0) {
832            err = AFPERR_LOCK;
833            goto afp_read_err;
834        }
835    }
836
837#ifdef WITH_SENDFILE
838    if (!(eid == ADEID_DFORK && ad_data_fileno(ofork->of_ad) == AD_SYMLINK) &&
839        !(obj->options.flags & OPTION_NOSENDFILE)) {
840        int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
841        if (dsi_stream_read_file(dsi, fd, offset, reqcount, err) < 0) {
842            LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s",
843                of_name(ofork), strerror(errno));
844            goto afp_read_exit;
845        }
846        goto afp_read_done;
847    }
848#endif
849
850    *rbuflen = MIN(reqcount, dsi->server_quantum);
851
852    cc = read_file(ofork, eid, offset, ibuf, rbuflen);
853    if (cc < 0) {
854        err = cc;
855        goto afp_read_done;
856    }
857
858    LOG(log_debug, logtype_afpd,
859        "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
860        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
861
862    offset += *rbuflen;
863
864    /*
865     * dsi_readinit() returns size of next read buffer. by this point,
866     * we know that we're sending some data. if we fail, something
867     * horrible happened.
868     */
869    if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0)
870        goto afp_read_exit;
871    *rbuflen = cc;
872
873    while (*rbuflen > 0) {
874        /*
875         * This loop isn't really entered anymore, we've already
876         * sent the whole requested block in dsi_readinit().
877         */
878        cc = read_file(ofork, eid, offset, ibuf, rbuflen);
879        if (cc < 0)
880            goto afp_read_exit;
881
882        offset += *rbuflen;
883        /* dsi_read() also returns buffer size of next allocation */
884        cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */
885        if (cc < 0)
886            goto afp_read_exit;
887        *rbuflen = cc;
888    }
889    dsi_readdone(dsi);
890    goto afp_read_done;
891
892afp_read_exit:
893    LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
894    dsi_readdone(dsi);
895   if (obj->options.flags & OPTION_AFP_READ_LOCK)
896       ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
897    obj->exit(EXITERR_CLNT);
898
899afp_read_done:
900    if (obj->options.flags & OPTION_AFP_READ_LOCK)
901        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
902
903    AFP_READ_DONE();
904    return err;
905
906afp_read_err:
907    *rbuflen = 0;
908    return err;
909}
910
911/* ---------------------- */
912int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
913{
914    return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
915}
916
917/* ---------------------- */
918int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
919{
920    return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
921}
922
923/* ---------------------- */
924int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
925{
926    struct vol *vol;
927    uint16_t vid;
928
929    *rbuflen = 0;
930    ibuf += 2;
931
932    memcpy(&vid, ibuf, sizeof(vid));
933    if (NULL == ( vol = getvolbyvid( vid )) ) {
934        return( AFPERR_PARAM );
935    }
936
937    of_flush(vol);
938    return( AFP_OK );
939}
940
941int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
942{
943    struct ofork    *ofork;
944    uint16_t       ofrefnum;
945
946    *rbuflen = 0;
947    ibuf += 2;
948    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
949
950    if (NULL == ( ofork = of_find( ofrefnum )) ) {
951        LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
952        return( AFPERR_PARAM );
953    }
954
955    LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
956        (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
957
958    if ( flushfork( ofork ) < 0 ) {
959        LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
960    }
961
962    return( AFP_OK );
963}
964
965/*
966  FIXME
967  There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
968  fsync(2) on OSX is implemented differently than on other platforms.
969  see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
970*/
971int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
972{
973    struct ofork        *ofork;
974    uint16_t           ofrefnum;
975
976    *rbuflen = 0;
977    ibuf += 2;
978
979    memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
980    ibuf += sizeof( ofrefnum );
981
982    if (NULL == ( ofork = of_find( ofrefnum )) ) {
983        LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
984        return( AFPERR_PARAM );
985    }
986
987    LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
988        (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
989
990    if ( flushfork( ofork ) < 0 ) {
991        LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
992        return AFPERR_MISC;
993    }
994
995    return( AFP_OK );
996}
997
998/* this is very similar to closefork */
999int flushfork(struct ofork *ofork)
1000{
1001    struct timeval tv;
1002
1003    int err = 0, doflush = 0;
1004
1005    if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1006         fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1007        LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1008            of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1009        err = -1;
1010    }
1011
1012    if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1013         (ofork->of_flags & AFPFORK_RSRC)) {
1014
1015        /* read in the rfork length */
1016        ad_refresh(NULL, ofork->of_ad);
1017
1018        /* set the date if we're dirty */
1019        if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1020            ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1021            ofork->of_flags &= ~AFPFORK_DIRTY;
1022            doflush++;
1023        }
1024
1025        /* flush the header */
1026        if (doflush && ad_flush(ofork->of_ad) < 0)
1027            err = -1;
1028
1029        if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1030            err = -1;
1031
1032        if (err < 0)
1033            LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1034                of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1035    }
1036
1037    return( err );
1038}
1039
1040int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1041{
1042    struct ofork    *ofork;
1043    uint16_t       ofrefnum;
1044
1045    *rbuflen = 0;
1046    ibuf += 2;
1047    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1048
1049    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1050        LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1051        return( AFPERR_PARAM );
1052    }
1053
1054    LOG(log_debug, logtype_afpd, "afp_closefork(fork: %" PRIu16 " [%s])",
1055        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc");
1056
1057    if (of_closefork(obj, ofork) < 0 ) {
1058        LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) );
1059        return( AFPERR_PARAM );
1060    }
1061
1062    return( AFP_OK );
1063}
1064
1065
1066static ssize_t write_file(struct ofork *ofork, int eid,
1067                          off_t offset, char *rbuf,
1068                          size_t rbuflen)
1069{
1070    ssize_t cc;
1071
1072    LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
1073        (uintmax_t)offset, rbuflen);
1074
1075    if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1076                        rbuf, rbuflen)) < 0 ) {
1077        switch ( errno ) {
1078        case EDQUOT :
1079        case EFBIG :
1080        case ENOSPC :
1081            LOG(log_error, logtype_afpd, "write_file: DISK FULL");
1082            return( AFPERR_DFULL );
1083        case EACCES:
1084            return AFPERR_ACCESS;
1085        default :
1086            LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1087            return( AFPERR_PARAM );
1088        }
1089    }
1090
1091    return cc;
1092}
1093
1094
1095/*
1096 * FPWrite. NOTE: on an error, we always use afp_write_err as
1097 * the client may have sent us a bunch of data that's not reflected
1098 * in reqcount et al.
1099 */
1100static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1101{
1102    struct ofork    *ofork;
1103    off_t           offset, saveoff, reqcount, oldsize, newsize;
1104    int             endflag, eid, err = AFP_OK;
1105    uint16_t        ofrefnum;
1106    ssize_t         cc;
1107    DSI             *dsi = obj->dsi;
1108    char            *rcvbuf = (char *)dsi->commands;
1109    size_t          rcvbuflen = dsi->server_quantum;
1110
1111    /* figure out parameters */
1112    ibuf++;
1113    endflag = ENDBIT(*ibuf);
1114    ibuf++;
1115    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1116    ibuf += sizeof( ofrefnum );
1117
1118    offset   = get_off_t(&ibuf, is64);
1119    reqcount = get_off_t(&ibuf, is64);
1120
1121    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1122        LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1123        err = AFPERR_PARAM;
1124        goto afp_write_err;
1125    }
1126
1127    LOG(log_debug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu64 ")",
1128        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount);
1129
1130    if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1131        err = AFPERR_ACCESS;
1132        goto afp_write_err;
1133    }
1134
1135#ifdef AFS
1136    writtenfork = ofork;
1137#endif /* AFS */
1138
1139    if ( ofork->of_flags & AFPFORK_DATA) {
1140        eid = ADEID_DFORK;
1141    } else if (ofork->of_flags & AFPFORK_RSRC) {
1142        eid = ADEID_RFORK;
1143    } else {
1144        err = AFPERR_ACCESS; /* should never happen */
1145        goto afp_write_err;
1146    }
1147
1148    oldsize = ad_size(ofork->of_ad, eid);
1149    if (endflag)
1150        offset += oldsize;
1151
1152    /* handle bogus parameters */
1153    if (reqcount < 0 || offset < 0) {
1154        err = AFPERR_PARAM;
1155        goto afp_write_err;
1156    }
1157
1158    newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1159
1160    /* offset can overflow on 64-bit capable filesystems.
1161     * report disk full if that's going to happen. */
1162    if (sum_neg(is64, offset, reqcount)) {
1163        LOG(log_error, logtype_afpd, "write_fork: DISK FULL");
1164        err = AFPERR_DFULL;
1165        goto afp_write_err;
1166    }
1167
1168    if (!reqcount) { /* handle request counts of 0 */
1169        err = AFP_OK;
1170        *rbuflen = set_off_t (offset, rbuf, is64);
1171        goto afp_write_err;
1172    }
1173
1174    AFP_WRITE_START((long)reqcount);
1175
1176    saveoff = offset;
1177    if (obj->options.flags & OPTION_AFP_READ_LOCK) {
1178        if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) {
1179            err = AFPERR_LOCK;
1180            goto afp_write_err;
1181        }
1182    }
1183
1184    /* find out what we have already */
1185    cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen);
1186
1187    if (!cc || (cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
1188        dsi_writeflush(dsi);
1189        *rbuflen = 0;
1190        if (obj->options.flags & OPTION_AFP_READ_LOCK)
1191            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1192        return cc;
1193    }
1194
1195    offset += cc;
1196
1197#if 0 /*def HAVE_SENDFILE_WRITE*/
1198    if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize)) < 0) {
1199        switch (errno) {
1200        case EDQUOT:
1201        case EFBIG:
1202        case ENOSPC:
1203            cc = AFPERR_DFULL;
1204            break;
1205        default:
1206            LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1207            goto afp_write_loop;
1208        }
1209        dsi_writeflush(dsi);
1210        *rbuflen = 0;
1211        if (obj->options.flags & OPTION_AFP_READ_LOCK)
1212            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1213        return cc;
1214    }
1215
1216    offset += cc;
1217    goto afp_write_done;
1218#endif /* 0, was HAVE_SENDFILE_WRITE */
1219
1220    /* loop until everything gets written. currently
1221     * dsi_write handles the end case by itself. */
1222    while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) {
1223
1224        if ((cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
1225            dsi_writeflush(dsi);
1226            *rbuflen = 0;
1227            if (obj->options.flags & OPTION_AFP_READ_LOCK)
1228                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1229            return cc;
1230        }
1231
1232        LOG(log_debug, logtype_afpd, "afp_write: wrote: %jd, offset: %jd",
1233            (intmax_t)cc, (intmax_t)offset);
1234
1235        offset += cc;
1236    }
1237
1238    if (obj->options.flags & OPTION_AFP_READ_LOCK)
1239        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1240    if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1241        ofork->of_flags |= AFPFORK_DIRTY;
1242
1243    /* we have modified any fork, remember until close_fork */
1244    ofork->of_flags |= AFPFORK_MODIFIED;
1245
1246    /* update write count */
1247    ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1248
1249    *rbuflen = set_off_t (offset, rbuf, is64);
1250    AFP_WRITE_DONE();
1251    return( AFP_OK );
1252
1253afp_write_err:
1254    dsi_writeinit(dsi, rcvbuf, rcvbuflen);
1255    dsi_writeflush(dsi);
1256
1257    if (err != AFP_OK) {
1258        *rbuflen = 0;
1259    }
1260    return err;
1261}
1262
1263/* ---------------------------- */
1264int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1265{
1266    return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1267}
1268
1269/* ----------------------------
1270 * FIXME need to deal with SIGXFSZ signal
1271 */
1272int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1273{
1274    return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1275}
1276
1277/* ---------------------------- */
1278int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1279{
1280    struct ofork    *ofork;
1281    int             ret;
1282    uint16_t       ofrefnum, bitmap;
1283    size_t          buflen;
1284    ibuf += 2;
1285    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1286    ibuf += sizeof( ofrefnum );
1287    memcpy(&bitmap, ibuf, sizeof( bitmap ));
1288    bitmap = ntohs( bitmap );
1289    ibuf += sizeof( bitmap );
1290
1291    *rbuflen = 0;
1292    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1293        LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1294        return( AFPERR_PARAM );
1295    }
1296
1297    if (AD_META_OPEN(ofork->of_ad)) {
1298        if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
1299            LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1300            return( AFPERR_PARAM );
1301        }
1302    }
1303
1304    if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
1305        return( ret );
1306    }
1307
1308    *rbuflen = buflen + sizeof( u_short );
1309    bitmap = htons( bitmap );
1310    memcpy(rbuf, &bitmap, sizeof( bitmap ));
1311    return( AFP_OK );
1312}
1313
1314