]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
Reactivate fcntl locking and fixes
[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_reso_fileno( ofork->of_ad ) == -1 ) { /* META ? */
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     if ( access == (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD)) {
212         return ad_excl_lock(adp, eid);
213     }
214     return 0;
215 }
216
217 /* ----------------------- */
218 int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
219 {
220     struct vol      *vol;
221     struct dir      *dir;
222     struct ofork    *ofork, *opened;
223     struct adouble  *adsame = NULL;
224     size_t          buflen;
225     int             ret, adflags, eid;
226     uint32_t        did;
227     uint16_t        vid, bitmap, access, ofrefnum;
228     char            fork, *path, *upath;
229     struct stat     *st;
230     uint16_t        bshort;
231     struct path     *s_path;
232
233     ibuf++;
234     fork = *ibuf++;
235     memcpy(&vid, ibuf, sizeof( vid ));
236     ibuf += sizeof(vid);
237
238     *rbuflen = 0;
239     if (NULL == ( vol = getvolbyvid( vid ))) {
240         return( AFPERR_PARAM );
241     }
242
243     memcpy(&did, ibuf, sizeof( did ));
244     ibuf += sizeof( int );
245
246     if (NULL == ( dir = dirlookup( vol, did ))) {
247         return afp_errno;
248     }
249
250     memcpy(&bitmap, ibuf, sizeof( bitmap ));
251     bitmap = ntohs( bitmap );
252     ibuf += sizeof( bitmap );
253     memcpy(&access, ibuf, sizeof( access ));
254     access = ntohs( access );
255     ibuf += sizeof( access );
256
257     if ((vol->v_flags & AFPVOL_RO) && (access & OPENACC_WR)) {
258         return AFPERR_VLOCK;
259     }
260
261     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
262         return get_afp_errno(AFPERR_PARAM);
263     }
264
265     if (*s_path->m_name == '\0') {
266         /* it's a dir ! */
267         return  AFPERR_BADTYPE;
268     }
269
270     LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\", %s)",
271         fullpathname(s_path->u_name), (fork & OPENFORK_RSCS) ? "RSRC" : "DATA");
272
273     /* stat() data fork st is set because it's not a dir */
274     switch ( s_path->st_errno ) {
275     case 0:
276         break;
277     case ENOENT:
278         return AFPERR_NOOBJ;
279     case EACCES:
280         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
281     default:
282         LOG(log_error, logtype_afpd, "afp_openfork(%s): %s", s_path->m_name, strerror(errno));
283         return AFPERR_PARAM;
284     }
285     /* FIXME should we check it first ? */
286     upath = s_path->u_name;
287     if (!vol_unix_priv(vol)) {
288         if (check_access(upath, access ) < 0) {
289             return AFPERR_ACCESS;
290         }
291     } else {
292         if (file_access(s_path, access ) < 0) {
293             return AFPERR_ACCESS;
294         }
295     }
296
297     st   = &s_path->st;
298     /* XXX: this probably isn't the best way to do this. the already
299        open bits should really be set if the fork is opened by any
300        program, not just this one. however, that's problematic to do
301        if we can't write lock files somewhere. opened is also passed to
302        ad_open so that we can keep file locks together.
303        FIXME: add the fork we are opening?
304     */
305     if ((opened = of_findname(s_path))) {
306         adsame = opened->of_ad;
307     }
308
309     if ( fork == OPENFORK_DATA ) {
310         eid = ADEID_DFORK;
311         adflags = ADFLAGS_DF | ADFLAGS_HF ;
312     } else {
313         eid = ADEID_RFORK;
314         adflags = ADFLAGS_RF | ADFLAGS_HF;
315     }
316
317     path = s_path->m_name;
318     if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid,
319                            adsame, st)) == NULL ) {
320         return( AFPERR_NFILE );
321     }
322
323     ret = AFPERR_NOOBJ;
324     if (access & OPENACC_WR) {
325         /* try opening in read-write mode */
326         if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDWR) < 0) {
327             switch ( errno ) {
328             case EROFS:
329                 ret = AFPERR_VLOCK;
330             case EACCES:
331                 goto openfork_err;
332                 break;
333             case ENOENT:
334                 if (fork == OPENFORK_DATA) {
335                     /* try to open only the data fork */
336                     if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDWR) < 0) {
337                         goto openfork_err;
338                     }
339                     adflags = ADFLAGS_DF;
340                 } else {
341                     /* here's the deal. we only try to create the resource
342                      * fork if the user wants to open it for write acess. */
343                     if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0)
344                         goto openfork_err;
345                     ofork->of_flags |= AFPFORK_OPEN;
346                 }
347                 break;
348             case EMFILE :
349             case ENFILE :
350                 ret = AFPERR_NFILE;
351                 goto openfork_err;
352                 break;
353             case EISDIR :
354                 ret = AFPERR_BADTYPE;
355                 goto openfork_err;
356                 break;
357             default:
358                 LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
359                 ret = AFPERR_PARAM;
360                 goto openfork_err;
361                 break;
362             }
363         }
364         else {
365             /* the ressource fork is open too */
366             ofork->of_flags |= AFPFORK_OPEN;
367         }
368     } else {
369         /* try opening in read-only mode */
370         ret = AFPERR_NOOBJ;
371         if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDONLY) < 0) {
372             switch ( errno ) {
373             case EROFS:
374                 ret = AFPERR_VLOCK;
375             case EACCES:
376                 goto openfork_err;
377                 break;
378             case ENOENT:
379                 /* see if client asked for a read only data fork */
380                 if (fork == OPENFORK_DATA) {
381                     if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDONLY) < 0) {
382                         goto openfork_err;
383                     }
384                     adflags = ADFLAGS_DF;
385                 }
386                 /* else we don't set AFPFORK_OPEN because there's no ressource fork file
387                  * We need to check AFPFORK_OPEN in afp_closefork(). eg fork open read-only
388                  * then create in open read-write.
389                  * FIXME , it doesn't play well with byte locking example:
390                  * ressource fork open read only
391                  * locking set on it (no effect, there's no file!)
392                  * ressource fork open read write now
393                  */
394                 break;
395             case EMFILE :
396             case ENFILE :
397                 ret = AFPERR_NFILE;
398                 goto openfork_err;
399                 break;
400             case EISDIR :
401                 ret = AFPERR_BADTYPE;
402                 goto openfork_err;
403                 break;
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                 break;
409             }
410         }
411         else {
412             /* the ressource fork is open too */
413             ofork->of_flags |= AFPFORK_OPEN;
414         }
415     }
416
417     if ((adflags & ADFLAGS_RF) && (ad_get_RF_flags( ofork->of_ad) & O_CREAT)) {
418         if (ad_setname(ofork->of_ad, path)) {
419             ad_flush( ofork->of_ad );
420         }
421     }
422
423     if ((ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof(int16_t), &buflen)) != AFP_OK) {
424         ad_close( ofork->of_ad, adflags );
425         goto openfork_err;
426     }
427
428     *rbuflen = buflen + 2 * sizeof( uint16_t );
429     bitmap = htons( bitmap );
430     memcpy(rbuf, &bitmap, sizeof( uint16_t ));
431     rbuf += sizeof( uint16_t );
432
433     /* check  WriteInhibit bit if we have a ressource fork
434      * the test is done here, after some Mac trafic capture
435      */
436     if (ad_meta_fileno(ofork->of_ad) != -1) {   /* META */
437         ad_getattr(ofork->of_ad, &bshort);
438         if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
439             ad_close( ofork->of_ad, adflags );
440             of_dealloc( ofork );
441             ofrefnum = 0;
442             memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
443             return(AFPERR_OLOCK);
444         }
445     }
446
447     /*
448      * synchronization locks:
449      */
450
451     /* don't try to lock non-existent rforks. */
452     if ((eid == ADEID_DFORK) || (ad_meta_fileno(ofork->of_ad) != -1)) { /* META */
453
454         ret = fork_setmode(ofork->of_ad, eid, access, ofrefnum);
455         /* can we access the fork? */
456         if (ret < 0) {
457             ret = errno;
458             ad_close( ofork->of_ad, adflags );
459             of_dealloc( ofork );
460             switch (ret) {
461             case EAGAIN: /* return data anyway */
462             case EACCES:
463             case EINVAL:
464                 ofrefnum = 0;
465                 memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
466                 return( AFPERR_DENYCONF );
467                 break;
468             default:
469                 *rbuflen = 0;
470                 LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_lock: %s", s_path->m_name, strerror(ret) );
471                 return( AFPERR_PARAM );
472             }
473         }
474         if ((access & OPENACC_WR))
475             ofork->of_flags |= AFPFORK_ACCWR;
476     }
477     /* the file may be open read only without ressource fork */
478     if ((access & OPENACC_RD))
479         ofork->of_flags |= AFPFORK_ACCRD;
480
481     memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
482     return( AFP_OK );
483
484 openfork_err:
485     of_dealloc( ofork );
486     if (errno == EACCES)
487         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
488     return ret;
489 }
490
491 int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
492 {
493     struct ofork    *ofork;
494     off_t       size;
495     uint16_t       ofrefnum, bitmap;
496     int                 err;
497     int                 is64;
498     int                 eid;
499     off_t       st_size;
500
501     ibuf += 2;
502
503     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
504     ibuf += sizeof( ofrefnum );
505
506     memcpy(&bitmap, ibuf, sizeof(bitmap));
507     bitmap = ntohs(bitmap);
508     ibuf += sizeof( bitmap );
509
510     *rbuflen = 0;
511     if (NULL == ( ofork = of_find( ofrefnum )) ) {
512         LOG(log_error, logtype_afpd, "afp_setforkparams: of_find(%d) could not locate fork", ofrefnum );
513         return( AFPERR_PARAM );
514     }
515
516     if (ofork->of_vol->v_flags & AFPVOL_RO)
517         return AFPERR_VLOCK;
518
519     if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
520         return AFPERR_ACCESS;
521
522     if ( ofork->of_flags & AFPFORK_DATA) {
523         eid = ADEID_DFORK;
524     } else if (ofork->of_flags & AFPFORK_RSRC) {
525         eid = ADEID_RFORK;
526     } else
527         return AFPERR_PARAM;
528
529     if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) ))
530            && eid == ADEID_RFORK
531              ) ||
532          ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) ))
533            && eid == ADEID_DFORK)) {
534         return AFPERR_BITMAP;
535     }
536
537     is64 = 0;
538     if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
539         if (afp_version >= 30) {
540             is64 = 4;
541         }
542         else
543             return AFPERR_BITMAP;
544     }
545
546     if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
547         return AFPERR_PARAM ;
548
549     size = get_off_t(&ibuf, is64);
550
551     if (size < 0)
552         return AFPERR_PARAM; /* Some MacOS don't return an error they just don't change the size! */
553
554
555     if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
556         st_size = ad_size(ofork->of_ad, eid);
557         err = -2;
558         if (st_size > size &&
559             ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0)
560             goto afp_setfork_err;
561
562         err = ad_dtruncate( ofork->of_ad, size );
563         if (st_size > size)
564             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
565         if (err < 0)
566             goto afp_setfork_err;
567     } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
568         ad_refresh( ofork->of_ad );
569
570         st_size = ad_size(ofork->of_ad, eid);
571         err = -2;
572         if (st_size > size &&
573             ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
574             goto afp_setfork_err;
575         }
576         err = ad_rtruncate(ofork->of_ad, size);
577         if (st_size > size)
578             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
579         if (err < 0)
580             goto afp_setfork_err;
581
582         if (ad_flush( ofork->of_ad ) < 0) {
583             LOG(log_error, logtype_afpd, "afp_setforkparams(%s): ad_flush: %s", of_name(ofork), strerror(errno) );
584             return AFPERR_PARAM;
585         }
586     } else
587         return AFPERR_BITMAP;
588
589 #ifdef AFS
590     if ( flushfork( ofork ) < 0 ) {
591         LOG(log_error, logtype_afpd, "afp_setforkparams(%s): flushfork: %s", of_name(ofork), strerror(errno) );
592     }
593 #endif /* AFS */
594
595     return( AFP_OK );
596
597 afp_setfork_err:
598     if (err == -2)
599         return AFPERR_LOCK;
600     else {
601         switch (errno) {
602         case EROFS:
603             return AFPERR_VLOCK;
604         case EPERM:
605         case EACCES:
606             return AFPERR_ACCESS;
607         case EDQUOT:
608         case EFBIG:
609         case ENOSPC:
610             return AFPERR_DFULL;
611         default:
612             return AFPERR_PARAM;
613         }
614     }
615 }
616
617 /* for this to work correctly, we need to check for locks before each
618  * read and write. that's most easily handled by always doing an
619  * appropriate check before each ad_read/ad_write. other things
620  * that can change files like truncate are handled internally to those
621  * functions.
622  */
623 #define ENDBIT(a)  ((a) & 0x80)
624 #define UNLOCKBIT(a) ((a) & 0x01)
625
626
627 /* ---------------------- */
628 static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
629 {
630     struct ofork    *ofork;
631     off_t               offset, length;
632     int                 eid;
633     uint16_t       ofrefnum;
634     uint8_t            flags;
635     int                 lockop;
636
637     *rbuflen = 0;
638
639     /* figure out parameters */
640     ibuf++;
641     flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
642     ibuf++;
643     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
644     ibuf += sizeof(ofrefnum);
645
646     if (NULL == ( ofork = of_find( ofrefnum )) ) {
647         LOG(log_error, logtype_afpd, "afp_bytelock: of_find(%d) could not locate fork", ofrefnum );
648         return( AFPERR_PARAM );
649     }
650
651     if ( ofork->of_flags & AFPFORK_DATA) {
652         eid = ADEID_DFORK;
653     } else if (ofork->of_flags & AFPFORK_RSRC) {
654         eid = ADEID_RFORK;
655     } else
656         return AFPERR_PARAM;
657
658     offset = get_off_t(&ibuf, is64);
659     length = get_off_t(&ibuf, is64);
660
661     /* FIXME AD_FILELOCK test is surely wrong */
662     if (length == -1)
663         length = BYTELOCK_MAX;
664     else if (!length || is_neg(is64, length)) {
665         return AFPERR_PARAM;
666     } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
667         return AFPERR_LOCK;
668     }
669
670     if (ENDBIT(flags)) {
671         offset += ad_size(ofork->of_ad, eid);
672         /* FIXME what do we do if file size > 2 GB and
673            it's not byte_lock_ext?
674         */
675     }
676     if (offset < 0)    /* error if we have a negative offset */
677         return AFPERR_PARAM;
678
679     /* if the file is a read-only file, we use read locks instead of
680      * write locks. that way, we can prevent anyone from initiating
681      * a write lock. */
682     lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
683     if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
684                 ofork->of_refnum) < 0) {
685         switch (errno) {
686         case EACCES:
687         case EAGAIN:
688             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
689             break;
690         case ENOLCK:
691             return AFPERR_NLOCK;
692             break;
693         case EINVAL:
694             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
695             break;
696         case EBADF:
697         default:
698             return AFPERR_PARAM;
699             break;
700         }
701     }
702     *rbuflen = set_off_t (offset, rbuf, is64);
703     return( AFP_OK );
704 }
705
706 /* --------------------------- */
707 int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
708 {
709     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
710 }
711
712 /* --------------------------- */
713 int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
714 {
715     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
716 }
717
718 #undef UNLOCKBIT
719
720 /* --------------------------- */
721 static int crlf(struct ofork *of)
722 {
723     struct extmap   *em;
724
725     if ( ad_meta_fileno( of->of_ad ) == -1 || !memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI),8)) { /* META */
726         /* no resource fork or no finderinfo, use our files extension mapping */
727         if (!( em = getextmap( of_name(of) )) || memcmp( "TEXT", em->em_type, sizeof( em->em_type ))) {
728             return 0;
729         }
730         /* file type is TEXT */
731         return 1;
732
733     } else if ( !memcmp( "TEXT", ad_entry( of->of_ad, ADEID_FINDERI ), 4 )) {
734         return 1;
735     }
736     return 0;
737 }
738
739
740 static ssize_t read_file(struct ofork *ofork, int eid,
741                          off_t offset, u_char nlmask,
742                          u_char nlchar, char *rbuf,
743                          size_t *rbuflen, const int xlate)
744 {
745     ssize_t cc;
746     int eof = 0;
747     char *p, *q;
748
749     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
750     if ( cc < 0 ) {
751         LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
752         *rbuflen = 0;
753         return( AFPERR_PARAM );
754     }
755     if ( (size_t)cc < *rbuflen ) {
756         eof = 1;
757     }
758
759     /*
760      * Do Newline check.
761      */
762     if ( nlmask != 0 ) {
763         for ( p = rbuf, q = p + cc; p < q; ) {
764             if (( *p++ & nlmask ) == nlchar ) {
765                 break;
766             }
767         }
768         if ( p != q ) {
769             cc = p - rbuf;
770             eof = 0;
771         }
772     }
773
774     /*
775      * If this file is of type TEXT, then swap \012 to \015.
776      */
777     if (xlate) {
778         for ( p = rbuf, q = p + cc; p < q; p++ ) {
779             if ( *p == '\012' ) {
780                 *p = '\015';
781             } else if ( *p == '\015' ) {
782                 *p = '\012';
783             }
784
785         }
786     }
787
788     *rbuflen = cc;
789     if ( eof ) {
790         return( AFPERR_EOF );
791     }
792     return AFP_OK;
793 }
794
795 /* -----------------------------
796  * with ddp, afp_read can return fewer bytes than in reqcount
797  * so return EOF only if read actually past end of file not
798  * if offset +reqcount > size of file
799  * e.g.:
800  * getfork size ==> 10430
801  * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
802  * read fork offset 4264 size 6128 ==> 4264 (without EOF)
803  * read fork offset 9248 size 1508 ==> 1182 (EOF)
804  * 10752 is a bug in Mac 7.5.x finder
805  *
806  * with dsi, should we check that reqcount < server quantum?
807  */
808 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
809 {
810     struct ofork    *ofork;
811     off_t       offset, saveoff, reqcount, savereqcount;
812     ssize_t     cc, err;
813     int         eid, xlate = 0;
814     uint16_t       ofrefnum;
815     u_char      nlmask, nlchar;
816
817     ibuf += 2;
818     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
819     ibuf += sizeof( u_short );
820
821     if (NULL == ( ofork = of_find( ofrefnum )) ) {
822         LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
823         err = AFPERR_PARAM;
824         goto afp_read_err;
825     }
826
827     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
828         err = AFPERR_ACCESS;
829         goto afp_read_err;
830     }
831     offset   = get_off_t(&ibuf, is64);
832     reqcount = get_off_t(&ibuf, is64);
833
834     LOG(log_debug, logtype_afpd,
835         "afp_read(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)", offset, reqcount,
836         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
837
838     if (is64) {
839         nlmask = nlchar = 0;
840     }
841     else {
842         nlmask = *ibuf++;
843         nlchar = *ibuf++;
844     }
845     /* if we wanted to be picky, we could add in the following
846      * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
847      */
848     if (reqcount < 0 || offset < 0) {
849         err = AFPERR_PARAM;
850         goto afp_read_err;
851     }
852
853     if ( ofork->of_flags & AFPFORK_DATA) {
854         eid = ADEID_DFORK;
855         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
856     } else if (ofork->of_flags & AFPFORK_RSRC) {
857         eid = ADEID_RFORK;
858     } else { /* fork wasn't opened. this should never really happen. */
859         err = AFPERR_ACCESS;
860         goto afp_read_err;
861     }
862
863     /* zero request count */
864     err = AFP_OK;
865     if (!reqcount) {
866         goto afp_read_err;
867     }
868
869     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)",
870         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount);
871
872     savereqcount = reqcount;
873     saveoff = offset;
874     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
875         err = AFPERR_LOCK;
876         goto afp_read_err;
877     }
878
879     *rbuflen = MIN(reqcount, *rbuflen);
880     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): reading %jd bytes from file",
881         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
882
883     err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen, xlate);
884     if (err < 0)
885         goto afp_read_done;
886     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
887         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
888
889     /* dsi can stream requests. we can only do this if we're not checking
890      * for an end-of-line character. oh well. */
891     if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
892         DSI    *dsi = obj->handle;
893         off_t  size;
894
895         /* reqcount isn't always truthful. we need to deal with that. */
896         size = ad_size(ofork->of_ad, eid);
897
898         /* subtract off the offset */
899         size -= offset;
900         if (reqcount > size) {
901             reqcount = size;
902             err = AFPERR_EOF;
903         }
904
905         offset += *rbuflen;
906
907         /* dsi_readinit() returns size of next read buffer. by this point,
908          * we know that we're sending some data. if we fail, something
909          * horrible happened. */
910         if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
911             goto afp_read_exit;
912         *rbuflen = cc;
913         /* due to the nature of afp packets, we have to exit if we get
914            an error. we can't do this with translation on. */
915 #ifdef WITH_SENDFILE
916         if (!(xlate)) {
917             int fd;
918
919             fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
920
921             if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) {
922                 if (errno == EINVAL || errno == ENOSYS)
923                     goto afp_read_loop;
924                 else {
925                     LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
926                     goto afp_read_exit;
927                 }
928             }
929
930             dsi_readdone(dsi);
931             goto afp_read_done;
932         }
933
934     afp_read_loop:
935 #endif
936
937         /* fill up our buffer. */
938         while (*rbuflen > 0) {
939             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen, xlate);
940             if (cc < 0)
941                 goto afp_read_exit;
942
943             offset += *rbuflen;
944             /* dsi_read() also returns buffer size of next allocation */
945             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
946             if (cc < 0)
947                 goto afp_read_exit;
948             *rbuflen = cc;
949         }
950         dsi_readdone(dsi);
951         goto afp_read_done;
952
953     afp_read_exit:
954         LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
955         dsi_readdone(dsi);
956         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
957         obj->exit(EXITERR_CLNT);
958     }
959
960 afp_read_done:
961     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
962     return err;
963
964 afp_read_err:
965     *rbuflen = 0;
966     return err;
967 }
968
969 /* ---------------------- */
970 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
971 {
972     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
973 }
974
975 /* ---------------------- */
976 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
977 {
978     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
979 }
980
981 /* ---------------------- */
982 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
983 {
984     struct vol *vol;
985     uint16_t vid;
986
987     *rbuflen = 0;
988     ibuf += 2;
989
990     memcpy(&vid, ibuf, sizeof(vid));
991     if (NULL == ( vol = getvolbyvid( vid )) ) {
992         return( AFPERR_PARAM );
993     }
994
995     of_flush(vol);
996     return( AFP_OK );
997 }
998
999 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1000 {
1001     struct ofork    *ofork;
1002     uint16_t       ofrefnum;
1003
1004     *rbuflen = 0;
1005     ibuf += 2;
1006     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1007
1008     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1009         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
1010         return( AFPERR_PARAM );
1011     }
1012
1013     LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
1014         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1015
1016     if ( flushfork( ofork ) < 0 ) {
1017         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
1018     }
1019
1020     return( AFP_OK );
1021 }
1022
1023 /*
1024   FIXME
1025   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
1026   fsync(2) on OSX is implemented differently than on other platforms.
1027   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
1028 */
1029 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1030 {
1031     struct ofork        *ofork;
1032     uint16_t           ofrefnum;
1033
1034     *rbuflen = 0;
1035     ibuf += 2;
1036
1037     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
1038     ibuf += sizeof( ofrefnum );
1039
1040     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1041         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
1042         return( AFPERR_PARAM );
1043     }
1044
1045     LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
1046         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1047
1048     if ( flushfork( ofork ) < 0 ) {
1049         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1050         return AFPERR_MISC;
1051     }
1052
1053     return( AFP_OK );
1054 }
1055
1056 /* this is very similar to closefork */
1057 int flushfork(struct ofork *ofork)
1058 {
1059     struct timeval tv;
1060
1061     int err = 0, doflush = 0;
1062
1063     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1064          fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1065         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1066             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1067         err = -1;
1068     }
1069
1070     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1071          (ofork->of_flags & AFPFORK_RSRC)) {
1072
1073         /* read in the rfork length */
1074         ad_refresh(ofork->of_ad);
1075
1076         /* set the date if we're dirty */
1077         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1078             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1079             ofork->of_flags &= ~AFPFORK_DIRTY;
1080             doflush++;
1081         }
1082
1083         /* flush the header */
1084         if (doflush && ad_flush(ofork->of_ad) < 0)
1085             err = -1;
1086
1087         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1088             err = -1;
1089
1090         if (err < 0)
1091             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1092                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1093     }
1094
1095     return( err );
1096 }
1097
1098 int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1099 {
1100     struct ofork    *ofork;
1101     uint16_t       ofrefnum;
1102
1103     *rbuflen = 0;
1104     ibuf += 2;
1105     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1106
1107     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1108         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1109         return( AFPERR_PARAM );
1110     }
1111
1112     LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)",
1113         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1114
1115     if ( of_closefork( ofork ) < 0 ) {
1116         LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1117         return( AFPERR_PARAM );
1118     }
1119
1120     return( AFP_OK );
1121 }
1122
1123
1124 static ssize_t write_file(struct ofork *ofork, int eid,
1125                           off_t offset, char *rbuf,
1126                           size_t rbuflen, const int xlate)
1127 {
1128     char *p, *q;
1129     ssize_t cc;
1130
1131     /*
1132      * If this file is of type TEXT, swap \015 to \012.
1133      */
1134     if (xlate) {
1135         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1136             if ( *p == '\015' ) {
1137                 *p = '\012';
1138             } else if ( *p == '\012' ) {
1139                 *p = '\015';
1140             }
1141         }
1142     }
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
1250         /* find out what we have already and write it out. */
1251         cc = dsi_writeinit(dsi, rbuf, *rbuflen);
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         offset += cc;
1259
1260 #if 0 /*def HAVE_SENDFILE_WRITE*/
1261         if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1262             if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1263                                    offset, dsi->datasize)) < 0) {
1264                 switch (errno) {
1265                 case EDQUOT :
1266                 case EFBIG :
1267                 case ENOSPC :
1268                     cc = AFPERR_DFULL;
1269                     break;
1270                 default :
1271                     LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1272                     goto afp_write_loop;
1273                 }
1274                 dsi_writeflush(dsi);
1275                 *rbuflen = 0;
1276                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1277                            reqcount,  ofork->of_refnum);
1278                 return cc;
1279             }
1280
1281             offset += cc;
1282             goto afp_write_done;
1283         }
1284 #endif /* 0, was HAVE_SENDFILE_WRITE */
1285
1286         /* loop until everything gets written. currently
1287          * dsi_write handles the end case by itself. */
1288         while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1289             if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1290                 dsi_writeflush(dsi);
1291                 *rbuflen = 0;
1292                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1293                            reqcount,  ofork->of_refnum);
1294                 return cc;
1295             }
1296             offset += cc;
1297         }
1298     }
1299     break;
1300     }
1301
1302     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1303     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1304         ofork->of_flags |= AFPFORK_DIRTY;
1305
1306     /* we have modified any fork, remember until close_fork */
1307     ofork->of_flags |= AFPFORK_MODIFIED;
1308
1309     /* update write count */
1310     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1311
1312     *rbuflen = set_off_t (offset, rbuf, is64);
1313     return( AFP_OK );
1314
1315 afp_write_err:
1316     if (obj->proto == AFPPROTO_DSI) {
1317         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1318         dsi_writeflush(obj->handle);
1319     }
1320     if (err != AFP_OK) {
1321         *rbuflen = 0;
1322     }
1323     return err;
1324 }
1325
1326 /* ---------------------------- */
1327 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1328 {
1329     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1330 }
1331
1332 /* ----------------------------
1333  * FIXME need to deal with SIGXFSZ signal
1334  */
1335 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1336 {
1337     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1338 }
1339
1340 /* ---------------------------- */
1341 int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1342 {
1343     struct ofork    *ofork;
1344     int             ret;
1345     uint16_t       ofrefnum, bitmap;
1346     size_t          buflen;
1347     ibuf += 2;
1348     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1349     ibuf += sizeof( ofrefnum );
1350     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1351     bitmap = ntohs( bitmap );
1352     ibuf += sizeof( bitmap );
1353
1354     *rbuflen = 0;
1355     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1356         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1357         return( AFPERR_PARAM );
1358     }
1359
1360     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) { /* META */
1361         if ( ad_refresh( ofork->of_ad ) < 0 ) {
1362             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1363             return( AFPERR_PARAM );
1364         }
1365     }
1366
1367     if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
1368                                          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