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