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