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