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, off_t 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, off_t 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(vol, 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            LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL");
642            return AFPERR_DFULL;
643        default:
644            return AFPERR_PARAM;
645        }
646    }
647}
648
649/* for this to work correctly, we need to check for locks before each
650 * read and write. that's most easily handled by always doing an
651 * appropriate check before each ad_read/ad_write. other things
652 * that can change files like truncate are handled internally to those
653 * functions.
654 */
655#define ENDBIT(a)  ((a) & 0x80)
656#define UNLOCKBIT(a) ((a) & 0x01)
657
658
659/* ---------------------- */
660static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
661{
662    struct ofork	*ofork;
663    off_t               offset, length;
664    int                 eid;
665    u_int16_t		ofrefnum;
666    u_int8_t            flags;
667    int                 lockop;
668
669    *rbuflen = 0;
670
671    /* figure out parameters */
672    ibuf++;
673    flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
674    ibuf++;
675    memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
676    ibuf += sizeof(ofrefnum);
677
678    if (NULL == ( ofork = of_find( ofrefnum )) ) {
679        LOG(log_error, logtype_afpd, "afp_bytelock: of_find(%d) could not locate fork", ofrefnum );
680        return( AFPERR_PARAM );
681    }
682
683    if ( ofork->of_flags & AFPFORK_DATA) {
684        eid = ADEID_DFORK;
685    } else if (ofork->of_flags & AFPFORK_RSRC) {
686        eid = ADEID_RFORK;
687    } else
688        return AFPERR_PARAM;
689
690    offset = get_off_t(&ibuf, is64);
691    length = get_off_t(&ibuf, is64);
692
693    /* FIXME AD_FILELOCK test is surely wrong */
694    if (length == -1)
695        length = BYTELOCK_MAX;
696     else if (!length || is_neg(is64, length)) {
697     	return AFPERR_PARAM;
698     } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
699        return AFPERR_LOCK;
700    }
701
702    if (ENDBIT(flags)) {
703        offset += ad_size(ofork->of_ad, eid);
704        /* FIXME what do we do if file size > 2 GB and
705           it's not byte_lock_ext?
706        */
707    }
708    if (offset < 0)    /* error if we have a negative offset */
709        return AFPERR_PARAM;
710
711    /* if the file is a read-only file, we use read locks instead of
712     * write locks. that way, we can prevent anyone from initiating
713     * a write lock. */
714    lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
715    if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
716                ofork->of_refnum) < 0) {
717        switch (errno) {
718        case EACCES:
719        case EAGAIN:
720            return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
721            break;
722        case ENOLCK:
723            return AFPERR_NLOCK;
724            break;
725        case EINVAL:
726            return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
727            break;
728        case EBADF:
729        default:
730            return AFPERR_PARAM;
731            break;
732        }
733    }
734    *rbuflen = set_off_t (offset, rbuf, is64);
735    return( AFP_OK );
736}
737
738/* --------------------------- */
739int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
740{
741   return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
742}
743
744/* --------------------------- */
745int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
746{
747   return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
748}
749
750#undef UNLOCKBIT
751
752/* --------------------------- */
753static int crlf(struct ofork *of)
754{
755    struct extmap	*em;
756
757    if ( ad_meta_fileno( of->of_ad ) == -1 || !memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI),8)) { /* META */
758        /* no resource fork or no finderinfo, use our files extension mapping */
759        if (!( em = getextmap( of_name(of) )) || memcmp( "TEXT", em->em_type, sizeof( em->em_type ))) {
760            return 0;
761        }
762        /* file type is TEXT */
763        return 1;
764
765    } else if ( !memcmp( "TEXT", ad_entry( of->of_ad, ADEID_FINDERI ), 4 )) {
766        return 1;
767    }
768    return 0;
769}
770
771
772static ssize_t read_file(struct ofork *ofork, int eid,
773                                    off_t offset, u_char nlmask,
774                                    u_char nlchar, char *rbuf,
775                                    size_t *rbuflen, const int xlate)
776{
777    ssize_t cc;
778    int eof = 0;
779    char *p, *q;
780
781    cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
782    if ( cc < 0 ) {
783        LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
784        *rbuflen = 0;
785        return( AFPERR_PARAM );
786    }
787    if ( (size_t)cc < *rbuflen ) {
788        eof = 1;
789    }
790
791    /*
792     * Do Newline check.
793     */
794    if ( nlmask != 0 ) {
795        for ( p = rbuf, q = p + cc; p < q; ) {
796            if (( *p++ & nlmask ) == nlchar ) {
797                break;
798            }
799        }
800        if ( p != q ) {
801            cc = p - rbuf;
802            eof = 0;
803        }
804    }
805
806    /*
807     * If this file is of type TEXT, then swap \012 to \015.
808     */
809    if (xlate) {
810        for ( p = rbuf, q = p + cc; p < q; p++ ) {
811            if ( *p == '\012' ) {
812                *p = '\015';
813            } else if ( *p == '\015' ) {
814                *p = '\012';
815            }
816
817        }
818    }
819
820    *rbuflen = cc;
821    if ( eof ) {
822        return( AFPERR_EOF );
823    }
824    return AFP_OK;
825}
826
827/* -----------------------------
828 * with ddp, afp_read can return fewer bytes than in reqcount
829 * so return EOF only if read actually past end of file not
830 * if offset +reqcount > size of file
831 * e.g.:
832 * getfork size ==> 10430
833 * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
834 * read fork offset 4264 size 6128 ==> 4264 (without EOF)
835 * read fork offset 9248 size 1508 ==> 1182 (EOF)
836 * 10752 is a bug in Mac 7.5.x finder
837 *
838 * with dsi, should we check that reqcount < server quantum?
839*/
840static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
841{
842    struct ofork	*ofork;
843    off_t		offset, saveoff, reqcount, savereqcount;
844    ssize_t		cc, err;
845    int			eid, xlate = 0;
846    u_int16_t		ofrefnum;
847    u_char		nlmask, nlchar;
848
849    ibuf += 2;
850    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
851    ibuf += sizeof( u_short );
852
853    if (NULL == ( ofork = of_find( ofrefnum )) ) {
854        LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
855        err = AFPERR_PARAM;
856        goto afp_read_err;
857    }
858
859    if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
860        err = AFPERR_ACCESS;
861        goto afp_read_err;
862    }
863    offset   = get_off_t(&ibuf, is64);
864    reqcount = get_off_t(&ibuf, is64);
865
866    if (is64) {
867        nlmask = nlchar = 0;
868    }
869    else {
870        nlmask = *ibuf++;
871        nlchar = *ibuf++;
872    }
873    /* if we wanted to be picky, we could add in the following
874     * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
875     */
876    if (reqcount < 0 || offset < 0) {
877        err = AFPERR_PARAM;
878        goto afp_read_err;
879    }
880
881    if ( ofork->of_flags & AFPFORK_DATA) {
882        eid = ADEID_DFORK;
883        xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
884    } else if (ofork->of_flags & AFPFORK_RSRC) {
885        eid = ADEID_RFORK;
886    } else { /* fork wasn't opened. this should never really happen. */
887        err = AFPERR_ACCESS;
888        goto afp_read_err;
889    }
890
891    /* zero request count */
892    err = AFP_OK;
893    if (!reqcount) {
894        goto afp_read_err;
895    }
896
897    LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)",
898        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount);
899
900    savereqcount = reqcount;
901    saveoff = offset;
902    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
903        err = AFPERR_LOCK;
904        goto afp_read_err;
905    }
906
907    *rbuflen = MIN(reqcount, *rbuflen);
908    LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): reading %jd bytes from file",
909        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
910    err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen, xlate);
911    if (err < 0)
912        goto afp_read_done;
913    LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
914        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
915
916    /* dsi can stream requests. we can only do this if we're not checking
917     * for an end-of-line character. oh well. */
918    if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
919        DSI    *dsi = obj->handle;
920        off_t  size;
921
922        /* reqcount isn't always truthful. we need to deal with that. */
923        size = ad_size(ofork->of_ad, eid);
924
925        /* subtract off the offset */
926        size -= offset;
927        if (reqcount > size) {
928    	   reqcount = size;
929           err = AFPERR_EOF;
930        }
931
932        offset += *rbuflen;
933
934        /* dsi_readinit() returns size of next read buffer. by this point,
935         * we know that we're sending some data. if we fail, something
936         * horrible happened. */
937        if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
938            goto afp_read_exit;
939        *rbuflen = cc;
940        /* due to the nature of afp packets, we have to exit if we get
941           an error. we can't do this with translation on. */
942#ifdef WITH_SENDFILE
943        if (!(xlate || Debug(obj) )) {
944            int fd;
945
946            fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
947
948            if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) {
949                if (errno == EINVAL || errno == ENOSYS)
950                    goto afp_read_loop;
951                else {
952                    LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
953                    goto afp_read_exit;
954                }
955            }
956
957            dsi_readdone(dsi);
958            goto afp_read_done;
959        }
960
961afp_read_loop:
962#endif
963
964        /* fill up our buffer. */
965        while (*rbuflen > 0) {
966            cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen, xlate);
967            if (cc < 0)
968                goto afp_read_exit;
969
970            offset += *rbuflen;
971            /* dsi_read() also returns buffer size of next allocation */
972            cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
973            if (cc < 0)
974                goto afp_read_exit;
975            *rbuflen = cc;
976        }
977        dsi_readdone(dsi);
978        goto afp_read_done;
979
980afp_read_exit:
981        LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
982        dsi_readdone(dsi);
983        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
984        obj->exit(EXITERR_CLNT);
985    }
986
987afp_read_done:
988    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
989    return err;
990
991afp_read_err:
992    *rbuflen = 0;
993    return err;
994}
995
996/* ---------------------- */
997int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
998{
999    return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1000}
1001
1002/* ---------------------- */
1003int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1004{
1005    return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1006}
1007
1008/* ---------------------- */
1009int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1010{
1011    struct vol *vol;
1012    u_int16_t vid;
1013
1014    *rbuflen = 0;
1015    ibuf += 2;
1016
1017    memcpy(&vid, ibuf, sizeof(vid));
1018    if (NULL == ( vol = getvolbyvid( vid )) ) {
1019        return( AFPERR_PARAM );
1020    }
1021
1022    of_flush(vol);
1023    return( AFP_OK );
1024}
1025
1026int afp_flushfork(AFPObj *obj _U_, char	*ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1027{
1028    struct ofork	*ofork;
1029    u_int16_t		ofrefnum;
1030
1031    *rbuflen = 0;
1032    ibuf += 2;
1033    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1034
1035    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1036        LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
1037        return( AFPERR_PARAM );
1038    }
1039
1040    if ( flushfork( ofork ) < 0 ) {
1041        LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
1042    }
1043
1044    return( AFP_OK );
1045}
1046
1047/*
1048  FIXME
1049  There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
1050  fsync(2) on OSX is implemented differently than on other platforms.
1051  see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
1052 */
1053int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1054{
1055    struct ofork        *ofork;
1056    u_int16_t           ofrefnum;
1057
1058    *rbuflen = 0;
1059    ibuf += 2;
1060
1061    memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
1062    ibuf += sizeof( ofrefnum );
1063
1064    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1065        LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
1066        return( AFPERR_PARAM );
1067    }
1068
1069    if ( flushfork( ofork ) < 0 ) {
1070	LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1071	return AFPERR_MISC;
1072    }
1073
1074    return( AFP_OK );
1075}
1076
1077/* this is very similar to closefork */
1078int flushfork(struct ofork *ofork)
1079{
1080    struct timeval tv;
1081
1082    int err = 0, doflush = 0;
1083
1084    if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1085            fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1086        LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1087            of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1088        err = -1;
1089    }
1090
1091    if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1092	 (ofork->of_flags & AFPFORK_RSRC)) {
1093
1094        /* read in the rfork length */
1095        ad_refresh(ofork->of_ad);
1096
1097        /* set the date if we're dirty */
1098        if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1099            ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1100            ofork->of_flags &= ~AFPFORK_DIRTY;
1101            doflush++;
1102        }
1103
1104        /* flush the header */
1105        if (doflush && ad_flush(ofork->of_ad) < 0)
1106                err = -1;
1107
1108        if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1109            err = -1;
1110
1111        if (err < 0)
1112            LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1113                of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1114    }
1115
1116    return( err );
1117}
1118
1119int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1120{
1121    struct ofork	*ofork;
1122    u_int16_t		ofrefnum;
1123
1124    *rbuflen = 0;
1125    ibuf += 2;
1126    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1127
1128    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1129        LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1130        return( AFPERR_PARAM );
1131    }
1132    if ( of_closefork( ofork ) < 0 ) {
1133        LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1134        return( AFPERR_PARAM );
1135    }
1136
1137    return( AFP_OK );
1138}
1139
1140
1141static ssize_t write_file(struct ofork *ofork, int eid,
1142                                     off_t offset, char *rbuf,
1143                                     size_t rbuflen, const int xlate)
1144{
1145    char *p, *q;
1146    ssize_t cc;
1147
1148    /*
1149     * If this file is of type TEXT, swap \015 to \012.
1150     */
1151    if (xlate) {
1152        for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1153            if ( *p == '\015' ) {
1154                *p = '\012';
1155            } else if ( *p == '\012' ) {
1156                *p = '\015';
1157            }
1158        }
1159    }
1160
1161    if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1162                        rbuf, rbuflen)) < 0 ) {
1163        switch ( errno ) {
1164        case EDQUOT :
1165        case EFBIG :
1166        case ENOSPC :
1167            LOG(log_error, logtype_afpd, "write_file: DISK FULL");
1168            return( AFPERR_DFULL );
1169        case EACCES:
1170            return AFPERR_ACCESS;
1171        default :
1172            LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1173            return( AFPERR_PARAM );
1174        }
1175    }
1176
1177    return cc;
1178}
1179
1180
1181/* FPWrite. NOTE: on an error, we always use afp_write_err as
1182 * the client may have sent us a bunch of data that's not reflected
1183 * in reqcount et al. */
1184static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1185{
1186    struct ofork	*ofork;
1187    off_t           offset, saveoff, reqcount, oldsize, newsize;
1188    int		        endflag, eid, xlate = 0, err = AFP_OK;
1189    u_int16_t		ofrefnum;
1190    ssize_t             cc;
1191
1192    /* figure out parameters */
1193    ibuf++;
1194    endflag = ENDBIT(*ibuf);
1195    ibuf++;
1196    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1197    ibuf += sizeof( ofrefnum );
1198
1199    offset   = get_off_t(&ibuf, is64);
1200    reqcount = get_off_t(&ibuf, is64);
1201
1202    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1203        LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1204        err = AFPERR_PARAM;
1205        goto afp_write_err;
1206    }
1207
1208    if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1209        err = AFPERR_ACCESS;
1210        goto afp_write_err;
1211    }
1212
1213#ifdef AFS
1214    writtenfork = ofork;
1215#endif /* AFS */
1216
1217    if ( ofork->of_flags & AFPFORK_DATA) {
1218        eid = ADEID_DFORK;
1219        xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1220    } else if (ofork->of_flags & AFPFORK_RSRC) {
1221        eid = ADEID_RFORK;
1222    } else {
1223        err = AFPERR_ACCESS; /* should never happen */
1224        goto afp_write_err;
1225    }
1226
1227    oldsize = ad_size(ofork->of_ad, eid);
1228    if (endflag)
1229        offset += oldsize;
1230
1231    /* handle bogus parameters */
1232    if (reqcount < 0 || offset < 0) {
1233        err = AFPERR_PARAM;
1234        goto afp_write_err;
1235    }
1236
1237    newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1238
1239    /* offset can overflow on 64-bit capable filesystems.
1240     * report disk full if that's going to happen. */
1241     if (sum_neg(is64, offset, reqcount)) {
1242        LOG(log_error, logtype_afpd, "write_fork: DISK FULL");
1243        err = AFPERR_DFULL;
1244        goto afp_write_err;
1245    }
1246
1247    if (!reqcount) { /* handle request counts of 0 */
1248        err = AFP_OK;
1249        *rbuflen = set_off_t (offset, rbuf, is64);
1250        goto afp_write_err;
1251    }
1252
1253    saveoff = offset;
1254    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1255                   reqcount, ofork->of_refnum) < 0) {
1256        err = AFPERR_LOCK;
1257        goto afp_write_err;
1258    }
1259
1260    /* this is yucky, but dsi can stream i/o and asp can't */
1261    switch (obj->proto) {
1262#ifndef NO_DDP
1263    case AFPPROTO_ASP:
1264        if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1265            *rbuflen = 0;
1266            LOG(log_error, logtype_afpd, "afp_write: asp_wrtcont: %s", strerror(errno) );
1267            return( AFPERR_PARAM );
1268        }
1269
1270#ifdef DEBUG1
1271        if (obj->options.flags & OPTION_DEBUG) {
1272            printf("(write) len: %d\n", *rbuflen);
1273            bprint(rbuf, *rbuflen);
1274        }
1275#endif
1276        if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1277                             xlate)) < 0) {
1278            *rbuflen = 0;
1279            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1280            return cc;
1281        }
1282        offset += cc;
1283        break;
1284#endif /* no afp/asp */
1285
1286    case AFPPROTO_DSI:
1287        {
1288            DSI *dsi = obj->handle;
1289
1290            /* find out what we have already and write it out. */
1291            cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1292            if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1293                dsi_writeflush(dsi);
1294                *rbuflen = 0;
1295                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1296                return cc;
1297            }
1298            offset += cc;
1299
1300#if 0 /*def HAVE_SENDFILE_WRITE*/
1301            if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1302                if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1303                                       offset, dsi->datasize)) < 0) {
1304                    switch (errno) {
1305                    case EDQUOT :
1306                    case EFBIG :
1307                    case ENOSPC :
1308                        cc = AFPERR_DFULL;
1309                        break;
1310                    default :
1311                        LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1312                        goto afp_write_loop;
1313                    }
1314                    dsi_writeflush(dsi);
1315                    *rbuflen = 0;
1316                    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1317                               reqcount,  ofork->of_refnum);
1318                    return cc;
1319                }
1320
1321                offset += cc;
1322                goto afp_write_done;
1323            }
1324#endif /* 0, was HAVE_SENDFILE_WRITE */
1325
1326            /* loop until everything gets written. currently
1327                    * dsi_write handles the end case by itself. */
1328            while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1329                if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1330                    dsi_writeflush(dsi);
1331                    *rbuflen = 0;
1332                    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1333                               reqcount,  ofork->of_refnum);
1334                    return cc;
1335                }
1336                offset += cc;
1337            }
1338        }
1339        break;
1340    }
1341
1342    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1343    if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1344        ofork->of_flags |= AFPFORK_DIRTY;
1345
1346    /* we have modified any fork, remember until close_fork */
1347    ofork->of_flags |= AFPFORK_MODIFIED;
1348
1349    /* update write count */
1350    ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1351
1352    *rbuflen = set_off_t (offset, rbuf, is64);
1353    return( AFP_OK );
1354
1355afp_write_err:
1356    if (obj->proto == AFPPROTO_DSI) {
1357        dsi_writeinit(obj->handle, rbuf, *rbuflen);
1358        dsi_writeflush(obj->handle);
1359    }
1360    if (err != AFP_OK) {
1361        *rbuflen = 0;
1362    }
1363    return err;
1364}
1365
1366/* ---------------------------- */
1367int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1368{
1369    return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1370}
1371
1372/* ----------------------------
1373 * FIXME need to deal with SIGXFSZ signal
1374*/
1375int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1376{
1377    return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1378}
1379
1380/* ---------------------------- */
1381int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1382{
1383    struct ofork	*ofork;
1384    int             ret;
1385    u_int16_t		ofrefnum, bitmap;
1386    size_t          buflen;
1387    ibuf += 2;
1388    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1389    ibuf += sizeof( ofrefnum );
1390    memcpy(&bitmap, ibuf, sizeof( bitmap ));
1391    bitmap = ntohs( bitmap );
1392    ibuf += sizeof( bitmap );
1393
1394    *rbuflen = 0;
1395    if (NULL == ( ofork = of_find( ofrefnum )) ) {
1396        LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1397        return( AFPERR_PARAM );
1398    }
1399
1400    if ( ad_meta_fileno( ofork->of_ad ) != -1 ) { /* META */
1401        if ( ad_refresh( ofork->of_ad ) < 0 ) {
1402            LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1403            return( AFPERR_PARAM );
1404        }
1405    }
1406
1407    if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
1408                               rbuf + sizeof( u_short ), &buflen ))) {
1409        return( ret );
1410    }
1411
1412    *rbuflen = buflen + sizeof( u_short );
1413    bitmap = htons( bitmap );
1414    memcpy(rbuf, &bitmap, sizeof( bitmap ));
1415    return( AFP_OK );
1416}
1417
1418