]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
2216a45c1b32a0d40be5b89698a9a95bc8553ffc
[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             int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
884             if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) { 
885                switch (errno) {
886                 case EINVAL:
887                 case ENOSYS:
888                     goto afp_read_loop;
889                 default:
890                     LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
891                     goto afp_read_exit;
892                 }
893             }
894
895             dsi_readdone(dsi);
896             goto afp_read_done;
897         }
898     afp_read_loop:
899 #endif
900
901         /* fill up our buffer. */
902         while (*rbuflen > 0) {
903             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen);
904             if (cc < 0)
905                 goto afp_read_exit;
906
907             offset += *rbuflen;
908             /* dsi_read() also returns buffer size of next allocation */
909             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
910             if (cc < 0)
911                 goto afp_read_exit;
912             *rbuflen = cc;
913         }
914         dsi_readdone(dsi);
915         goto afp_read_done;
916
917     afp_read_exit:
918         LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
919         dsi_readdone(dsi);
920         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
921         obj->exit(EXITERR_CLNT);
922     }
923
924 afp_read_done:
925     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
926     return err;
927
928 afp_read_err:
929     *rbuflen = 0;
930     return err;
931 }
932
933 /* ---------------------- */
934 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
935 {
936     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
937 }
938
939 /* ---------------------- */
940 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
941 {
942     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
943 }
944
945 /* ---------------------- */
946 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
947 {
948     struct vol *vol;
949     uint16_t vid;
950
951     *rbuflen = 0;
952     ibuf += 2;
953
954     memcpy(&vid, ibuf, sizeof(vid));
955     if (NULL == ( vol = getvolbyvid( vid )) ) {
956         return( AFPERR_PARAM );
957     }
958
959     of_flush(vol);
960     return( AFP_OK );
961 }
962
963 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
964 {
965     struct ofork    *ofork;
966     uint16_t       ofrefnum;
967
968     *rbuflen = 0;
969     ibuf += 2;
970     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
971
972     if (NULL == ( ofork = of_find( ofrefnum )) ) {
973         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
974         return( AFPERR_PARAM );
975     }
976
977     LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
978         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
979
980     if ( flushfork( ofork ) < 0 ) {
981         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
982     }
983
984     return( AFP_OK );
985 }
986
987 /*
988   FIXME
989   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
990   fsync(2) on OSX is implemented differently than on other platforms.
991   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
992 */
993 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
994 {
995     struct ofork        *ofork;
996     uint16_t           ofrefnum;
997
998     *rbuflen = 0;
999     ibuf += 2;
1000
1001     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
1002     ibuf += sizeof( ofrefnum );
1003
1004     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1005         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
1006         return( AFPERR_PARAM );
1007     }
1008
1009     LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
1010         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1011
1012     if ( flushfork( ofork ) < 0 ) {
1013         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1014         return AFPERR_MISC;
1015     }
1016
1017     return( AFP_OK );
1018 }
1019
1020 /* this is very similar to closefork */
1021 int flushfork(struct ofork *ofork)
1022 {
1023     struct timeval tv;
1024
1025     int err = 0, doflush = 0;
1026
1027     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1028          fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1029         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1030             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1031         err = -1;
1032     }
1033
1034     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1035          (ofork->of_flags & AFPFORK_RSRC)) {
1036
1037         /* read in the rfork length */
1038         ad_refresh(NULL, ofork->of_ad);
1039
1040         /* set the date if we're dirty */
1041         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1042             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1043             ofork->of_flags &= ~AFPFORK_DIRTY;
1044             doflush++;
1045         }
1046
1047         /* flush the header */
1048         if (doflush && ad_flush(ofork->of_ad) < 0)
1049             err = -1;
1050
1051         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1052             err = -1;
1053
1054         if (err < 0)
1055             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1056                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1057     }
1058
1059     return( err );
1060 }
1061
1062 int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1063 {
1064     struct ofork    *ofork;
1065     uint16_t       ofrefnum;
1066
1067     *rbuflen = 0;
1068     ibuf += 2;
1069     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1070
1071     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1072         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1073         return( AFPERR_PARAM );
1074     }
1075
1076     LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)",
1077         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1078
1079     if ( of_closefork( ofork ) < 0 ) {
1080         LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1081         return( AFPERR_PARAM );
1082     }
1083
1084     return( AFP_OK );
1085 }
1086
1087
1088 static ssize_t write_file(struct ofork *ofork, int eid,
1089                           off_t offset, char *rbuf,
1090                           size_t rbuflen)
1091 {
1092     char *p, *q;
1093     ssize_t cc;
1094
1095     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
1096         (uintmax_t)offset, rbuflen);
1097
1098     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1099                         rbuf, rbuflen)) < 0 ) {
1100         switch ( errno ) {
1101         case EDQUOT :
1102         case EFBIG :
1103         case ENOSPC :
1104             return( AFPERR_DFULL );
1105         case EACCES:
1106             return AFPERR_ACCESS;
1107         default :
1108             LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1109             return( AFPERR_PARAM );
1110         }
1111     }
1112
1113     return cc;
1114 }
1115
1116
1117 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1118  * the client may have sent us a bunch of data that's not reflected
1119  * in reqcount et al. */
1120 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1121 {
1122     struct ofork    *ofork;
1123     off_t               offset, saveoff, reqcount, oldsize, newsize;
1124     int             endflag, eid, err = AFP_OK;
1125     uint16_t       ofrefnum;
1126     ssize_t             cc;
1127
1128     /* figure out parameters */
1129     ibuf++;
1130     endflag = ENDBIT(*ibuf);
1131     ibuf++;
1132     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1133     ibuf += sizeof( ofrefnum );
1134
1135     offset   = get_off_t(&ibuf, is64);
1136     reqcount = get_off_t(&ibuf, is64);
1137
1138     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1139         LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1140         err = AFPERR_PARAM;
1141         goto afp_write_err;
1142     }
1143
1144     LOG(log_debug, logtype_afpd, "afp_write(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)",
1145         offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1146
1147     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1148         err = AFPERR_ACCESS;
1149         goto afp_write_err;
1150     }
1151
1152 #ifdef AFS
1153     writtenfork = ofork;
1154 #endif /* AFS */
1155
1156     if ( ofork->of_flags & AFPFORK_DATA) {
1157         eid = ADEID_DFORK;
1158     } else if (ofork->of_flags & AFPFORK_RSRC) {
1159         eid = ADEID_RFORK;
1160     } else {
1161         err = AFPERR_ACCESS; /* should never happen */
1162         goto afp_write_err;
1163     }
1164
1165     oldsize = ad_size(ofork->of_ad, eid);
1166     if (endflag)
1167         offset += oldsize;
1168
1169     /* handle bogus parameters */
1170     if (reqcount < 0 || offset < 0) {
1171         err = AFPERR_PARAM;
1172         goto afp_write_err;
1173     }
1174
1175     newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1176
1177     /* offset can overflow on 64-bit capable filesystems.
1178      * report disk full if that's going to happen. */
1179     if (sum_neg(is64, offset, reqcount)) {
1180         err = AFPERR_DFULL;
1181         goto afp_write_err;
1182     }
1183
1184     if (!reqcount) { /* handle request counts of 0 */
1185         err = AFP_OK;
1186         *rbuflen = set_off_t (offset, rbuf, is64);
1187         goto afp_write_err;
1188     }
1189
1190     saveoff = offset;
1191     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1192                    reqcount, ofork->of_refnum) < 0) {
1193         err = AFPERR_LOCK;
1194         goto afp_write_err;
1195     }
1196
1197     DSI *dsi = obj->dsi;
1198     /* find out what we have already and write it out. */
1199     cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1200
1201     if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) {
1202         dsi_writeflush(dsi);
1203         *rbuflen = 0;
1204         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1205         return cc;
1206     }
1207
1208     offset += cc;
1209
1210 #if 0 /*def HAVE_SENDFILE_WRITE*/
1211     if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1212                            offset, dsi->datasize)) < 0) {
1213         switch (errno) {
1214         case EDQUOT:
1215         case EFBIG:
1216         case ENOSPC:
1217             cc = AFPERR_DFULL;
1218             break;
1219         default:
1220             LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1221             goto afp_write_loop;
1222         }
1223         dsi_writeflush(dsi);
1224         *rbuflen = 0;
1225         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1226                    reqcount,  ofork->of_refnum);
1227         return cc;
1228     }
1229
1230     offset += cc;
1231     goto afp_write_done;
1232 #endif /* 0, was HAVE_SENDFILE_WRITE */
1233
1234     /* loop until everything gets written. currently
1235      * dsi_write handles the end case by itself. */
1236     while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1237         if ((cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) {
1238             dsi_writeflush(dsi);
1239             *rbuflen = 0;
1240             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1241                        reqcount,  ofork->of_refnum);
1242             return cc;
1243         }
1244         offset += cc;
1245     }
1246
1247     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1248     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1249         ofork->of_flags |= AFPFORK_DIRTY;
1250
1251     /* we have modified any fork, remember until close_fork */
1252     ofork->of_flags |= AFPFORK_MODIFIED;
1253
1254     /* update write count */
1255     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1256
1257     *rbuflen = set_off_t (offset, rbuf, is64);
1258     return( AFP_OK );
1259
1260 afp_write_err:
1261     dsi_writeinit(obj->dsi, rbuf, *rbuflen);
1262     dsi_writeflush(obj->dsi);
1263
1264     if (err != AFP_OK) {
1265         *rbuflen = 0;
1266     }
1267     return err;
1268 }
1269
1270 /* ---------------------------- */
1271 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1272 {
1273     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1274 }
1275
1276 /* ----------------------------
1277  * FIXME need to deal with SIGXFSZ signal
1278  */
1279 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1280 {
1281     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1282 }
1283
1284 /* ---------------------------- */
1285 int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1286 {
1287     struct ofork    *ofork;
1288     int             ret;
1289     uint16_t       ofrefnum, bitmap;
1290     size_t          buflen;
1291     ibuf += 2;
1292     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1293     ibuf += sizeof( ofrefnum );
1294     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1295     bitmap = ntohs( bitmap );
1296     ibuf += sizeof( bitmap );
1297
1298     *rbuflen = 0;
1299     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1300         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1301         return( AFPERR_PARAM );
1302     }
1303
1304     if (AD_META_OPEN(ofork->of_ad)) {
1305         if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
1306             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1307             return( AFPERR_PARAM );
1308         }
1309     }
1310
1311     if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
1312         return( ret );
1313     }
1314
1315     *rbuflen = buflen + sizeof( u_short );
1316     bitmap = htons( bitmap );
1317     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1318     return( AFP_OK );
1319 }
1320