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