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