]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
Merge master
[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         if (ofork->of_ad->ad_vers == AD_VERSION_EA) {
578 #if HAVE_EAFD
579             err = sys_lremovexattr(of_name(ofork), AD_EA_RESO);
580 #else
581             err = unlink(ofork->of_vol->ad_path(of_name(ofork), 0));
582 #endif
583         } else {
584             err = ad_rtruncate(ofork->of_ad, size);
585         }
586         if (st_size > size)
587             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
588         if (err < 0)
589             goto afp_setfork_err;
590
591         if (ad_flush( ofork->of_ad ) < 0) {
592             LOG(log_error, logtype_afpd, "afp_setforkparams(%s): ad_flush: %s", of_name(ofork), strerror(errno) );
593             return AFPERR_PARAM;
594         }
595     } else
596         return AFPERR_BITMAP;
597
598 #ifdef AFS
599     if ( flushfork( ofork ) < 0 ) {
600         LOG(log_error, logtype_afpd, "afp_setforkparams(%s): flushfork: %s", of_name(ofork), strerror(errno) );
601     }
602 #endif /* AFS */
603
604     return( AFP_OK );
605
606 afp_setfork_err:
607     if (err == -2)
608         return AFPERR_LOCK;
609     else {
610         switch (errno) {
611         case EROFS:
612             return AFPERR_VLOCK;
613         case EPERM:
614         case EACCES:
615             return AFPERR_ACCESS;
616         case EDQUOT:
617         case EFBIG:
618         case ENOSPC:
619             return AFPERR_DFULL;
620         default:
621             return AFPERR_PARAM;
622         }
623     }
624 }
625
626 /* for this to work correctly, we need to check for locks before each
627  * read and write. that's most easily handled by always doing an
628  * appropriate check before each ad_read/ad_write. other things
629  * that can change files like truncate are handled internally to those
630  * functions.
631  */
632 #define ENDBIT(a)  ((a) & 0x80)
633 #define UNLOCKBIT(a) ((a) & 0x01)
634
635
636 /* ---------------------- */
637 static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
638 {
639     struct ofork    *ofork;
640     off_t               offset, length;
641     int                 eid;
642     uint16_t       ofrefnum;
643     uint8_t            flags;
644     int                 lockop;
645
646     *rbuflen = 0;
647
648     /* figure out parameters */
649     ibuf++;
650     flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
651     ibuf++;
652     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
653     ibuf += sizeof(ofrefnum);
654
655     if (NULL == ( ofork = of_find( ofrefnum )) ) {
656         LOG(log_error, logtype_afpd, "byte_lock: of_find(%d) could not locate fork", ofrefnum );
657         return( AFPERR_PARAM );
658     }
659
660     if ( ofork->of_flags & AFPFORK_DATA) {
661         eid = ADEID_DFORK;
662     } else if (ofork->of_flags & AFPFORK_RSRC) {
663         eid = ADEID_RFORK;
664     } else
665         return AFPERR_PARAM;
666
667     offset = get_off_t(&ibuf, is64);
668     length = get_off_t(&ibuf, is64);
669
670     if (length == -1)
671         length = BYTELOCK_MAX;
672     else if (!length || is_neg(is64, length)) {
673         return AFPERR_PARAM;
674     } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
675         return AFPERR_LOCK;
676     }
677
678     if (ENDBIT(flags)) {
679         offset += ad_size(ofork->of_ad, eid);
680         /* FIXME what do we do if file size > 2 GB and
681            it's not byte_lock_ext?
682         */
683     }
684     if (offset < 0)    /* error if we have a negative offset */
685         return AFPERR_PARAM;
686
687     /* if the file is a read-only file, we use read locks instead of
688      * write locks. that way, we can prevent anyone from initiating
689      * a write lock. */
690     lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
691     if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
692                 ofork->of_refnum) < 0) {
693         switch (errno) {
694         case EACCES:
695         case EAGAIN:
696             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
697             break;
698         case ENOLCK:
699             return AFPERR_NLOCK;
700             break;
701         case EINVAL:
702             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
703             break;
704         case EBADF:
705         default:
706             return AFPERR_PARAM;
707             break;
708         }
709     }
710     *rbuflen = set_off_t (offset, rbuf, is64);
711     return( AFP_OK );
712 }
713
714 /* --------------------------- */
715 int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
716 {
717     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
718 }
719
720 /* --------------------------- */
721 int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
722 {
723     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
724 }
725
726 #undef UNLOCKBIT
727
728 static ssize_t read_file(struct ofork *ofork, int eid,
729                          off_t offset, u_char nlmask,
730                          u_char nlchar, char *rbuf,
731                          size_t *rbuflen)
732 {
733     ssize_t cc;
734     int eof = 0;
735     char *p, *q;
736
737     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
738     if ( cc < 0 ) {
739         LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
740         *rbuflen = 0;
741         return( AFPERR_PARAM );
742     }
743     if ( (size_t)cc < *rbuflen ) {
744         eof = 1;
745     }
746
747     /*
748      * Do Newline check.
749      */
750     if ( nlmask != 0 ) {
751         for ( p = rbuf, q = p + cc; p < q; ) {
752             if (( *p++ & nlmask ) == nlchar ) {
753                 break;
754             }
755         }
756         if ( p != q ) {
757             cc = p - rbuf;
758             eof = 0;
759         }
760     }
761
762     *rbuflen = cc;
763     if ( eof ) {
764         return( AFPERR_EOF );
765     }
766     return AFP_OK;
767 }
768
769 /* -----------------------------
770  * with ddp, afp_read can return fewer bytes than in reqcount
771  * so return EOF only if read actually past end of file not
772  * if offset +reqcount > size of file
773  * e.g.:
774  * getfork size ==> 10430
775  * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
776  * read fork offset 4264 size 6128 ==> 4264 (without EOF)
777  * read fork offset 9248 size 1508 ==> 1182 (EOF)
778  * 10752 is a bug in Mac 7.5.x finder
779  *
780  * with dsi, should we check that reqcount < server quantum?
781  */
782 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
783 {
784     struct ofork    *ofork;
785     off_t       offset, saveoff, reqcount, savereqcount;
786     ssize_t     cc, err;
787     int         eid;
788     uint16_t       ofrefnum;
789     u_char      nlmask, nlchar;
790
791     ibuf += 2;
792     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
793     ibuf += sizeof( u_short );
794
795     if (NULL == ( ofork = of_find( ofrefnum )) ) {
796         LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
797         err = AFPERR_PARAM;
798         goto afp_read_err;
799     }
800
801     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
802         err = AFPERR_ACCESS;
803         goto afp_read_err;
804     }
805     offset   = get_off_t(&ibuf, is64);
806     reqcount = get_off_t(&ibuf, is64);
807
808     LOG(log_debug, logtype_afpd,
809         "afp_read(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)", offset, reqcount,
810         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
811
812     if (is64) {
813         nlmask = nlchar = 0;
814     }
815     else {
816         nlmask = *ibuf++;
817         nlchar = *ibuf++;
818     }
819     /* if we wanted to be picky, we could add in the following
820      * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
821      */
822     if (reqcount < 0 || offset < 0) {
823         err = AFPERR_PARAM;
824         goto afp_read_err;
825     }
826
827     if ( ofork->of_flags & AFPFORK_DATA) {
828         eid = ADEID_DFORK;
829     } else if (ofork->of_flags & AFPFORK_RSRC) {
830         eid = ADEID_RFORK;
831     } else { /* fork wasn't opened. this should never really happen. */
832         err = AFPERR_ACCESS;
833         goto afp_read_err;
834     }
835
836     /* zero request count */
837     err = AFP_OK;
838     if (!reqcount) {
839         goto afp_read_err;
840     }
841
842     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)",
843         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount);
844
845     savereqcount = reqcount;
846     saveoff = offset;
847     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
848         err = AFPERR_LOCK;
849         goto afp_read_err;
850     }
851
852     *rbuflen = MIN(reqcount, *rbuflen);
853     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): reading %jd bytes from file",
854         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
855
856     err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen);
857     if (err < 0)
858         goto afp_read_done;
859     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
860         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
861
862     /* dsi can stream requests. we can only do this if we're not checking
863      * for an end-of-line character. oh well. */
864     if ((*rbuflen < reqcount) && !nlmask) {
865         DSI    *dsi = obj->dsi;
866         off_t  size;
867
868         /* reqcount isn't always truthful. we need to deal with that. */
869         size = ad_size(ofork->of_ad, eid);
870
871         /* subtract off the offset */
872         size -= offset;
873         if (reqcount > size) {
874             reqcount = size;
875             err = AFPERR_EOF;
876         }
877
878         offset += *rbuflen;
879
880         /* dsi_readinit() returns size of next read buffer. by this point,
881          * we know that we're sending some data. if we fail, something
882          * horrible happened. */
883         if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
884             goto afp_read_exit;
885         *rbuflen = cc;
886         /* due to the nature of afp packets, we have to exit if we get
887            an error. we can't do this with translation on. */
888 #ifdef WITH_SENDFILE
889         int fd;
890
891         fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
892
893         if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) {
894             if (errno == EINVAL || errno == ENOSYS)
895                 goto afp_read_loop;
896             else {
897                 LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
898                 goto afp_read_exit;
899             }
900         }
901
902         dsi_readdone(dsi);
903         goto afp_read_done;
904
905     afp_read_loop:
906 #endif
907
908         /* fill up our buffer. */
909         while (*rbuflen > 0) {
910             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen);
911             if (cc < 0)
912                 goto afp_read_exit;
913
914             offset += *rbuflen;
915             /* dsi_read() also returns buffer size of next allocation */
916             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
917             if (cc < 0)
918                 goto afp_read_exit;
919             *rbuflen = cc;
920         }
921         dsi_readdone(dsi);
922         goto afp_read_done;
923
924     afp_read_exit:
925         LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
926         dsi_readdone(dsi);
927         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
928         obj->exit(EXITERR_CLNT);
929     }
930
931 afp_read_done:
932     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
933     return err;
934
935 afp_read_err:
936     *rbuflen = 0;
937     return err;
938 }
939
940 /* ---------------------- */
941 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
942 {
943     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
944 }
945
946 /* ---------------------- */
947 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
948 {
949     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
950 }
951
952 /* ---------------------- */
953 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
954 {
955     struct vol *vol;
956     uint16_t vid;
957
958     *rbuflen = 0;
959     ibuf += 2;
960
961     memcpy(&vid, ibuf, sizeof(vid));
962     if (NULL == ( vol = getvolbyvid( vid )) ) {
963         return( AFPERR_PARAM );
964     }
965
966     of_flush(vol);
967     return( AFP_OK );
968 }
969
970 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
971 {
972     struct ofork    *ofork;
973     uint16_t       ofrefnum;
974
975     *rbuflen = 0;
976     ibuf += 2;
977     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
978
979     if (NULL == ( ofork = of_find( ofrefnum )) ) {
980         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
981         return( AFPERR_PARAM );
982     }
983
984     LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
985         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
986
987     if ( flushfork( ofork ) < 0 ) {
988         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
989     }
990
991     return( AFP_OK );
992 }
993
994 /*
995   FIXME
996   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
997   fsync(2) on OSX is implemented differently than on other platforms.
998   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
999 */
1000 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1001 {
1002     struct ofork        *ofork;
1003     uint16_t           ofrefnum;
1004
1005     *rbuflen = 0;
1006     ibuf += 2;
1007
1008     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
1009     ibuf += sizeof( ofrefnum );
1010
1011     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1012         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
1013         return( AFPERR_PARAM );
1014     }
1015
1016     LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
1017         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1018
1019     if ( flushfork( ofork ) < 0 ) {
1020         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1021         return AFPERR_MISC;
1022     }
1023
1024     return( AFP_OK );
1025 }
1026
1027 /* this is very similar to closefork */
1028 int flushfork(struct ofork *ofork)
1029 {
1030     struct timeval tv;
1031
1032     int err = 0, doflush = 0;
1033
1034     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1035          fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1036         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1037             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1038         err = -1;
1039     }
1040
1041     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1042          (ofork->of_flags & AFPFORK_RSRC)) {
1043
1044         /* read in the rfork length */
1045         ad_refresh(NULL, ofork->of_ad);
1046
1047         /* set the date if we're dirty */
1048         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1049             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1050             ofork->of_flags &= ~AFPFORK_DIRTY;
1051             doflush++;
1052         }
1053
1054         /* flush the header */
1055         if (doflush && ad_flush(ofork->of_ad) < 0)
1056             err = -1;
1057
1058         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1059             err = -1;
1060
1061         if (err < 0)
1062             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1063                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1064     }
1065
1066     return( err );
1067 }
1068
1069 int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1070 {
1071     struct ofork    *ofork;
1072     uint16_t       ofrefnum;
1073
1074     *rbuflen = 0;
1075     ibuf += 2;
1076     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1077
1078     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1079         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1080         return( AFPERR_PARAM );
1081     }
1082
1083     LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)",
1084         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1085
1086     if ( of_closefork( ofork ) < 0 ) {
1087         LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1088         return( AFPERR_PARAM );
1089     }
1090
1091     return( AFP_OK );
1092 }
1093
1094
1095 static ssize_t write_file(struct ofork *ofork, int eid,
1096                           off_t offset, char *rbuf,
1097                           size_t rbuflen)
1098 {
1099     char *p, *q;
1100     ssize_t cc;
1101
1102     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
1103         (uintmax_t)offset, rbuflen);
1104
1105     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1106                         rbuf, rbuflen)) < 0 ) {
1107         switch ( errno ) {
1108         case EDQUOT :
1109         case EFBIG :
1110         case ENOSPC :
1111             return( AFPERR_DFULL );
1112         case EACCES:
1113             return AFPERR_ACCESS;
1114         default :
1115             LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1116             return( AFPERR_PARAM );
1117         }
1118     }
1119
1120     return cc;
1121 }
1122
1123
1124 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1125  * the client may have sent us a bunch of data that's not reflected
1126  * in reqcount et al. */
1127 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1128 {
1129     struct ofork    *ofork;
1130     off_t               offset, saveoff, reqcount, oldsize, newsize;
1131     int             endflag, eid, err = AFP_OK;
1132     uint16_t       ofrefnum;
1133     ssize_t             cc;
1134
1135     /* figure out parameters */
1136     ibuf++;
1137     endflag = ENDBIT(*ibuf);
1138     ibuf++;
1139     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1140     ibuf += sizeof( ofrefnum );
1141
1142     offset   = get_off_t(&ibuf, is64);
1143     reqcount = get_off_t(&ibuf, is64);
1144
1145     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1146         LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1147         err = AFPERR_PARAM;
1148         goto afp_write_err;
1149     }
1150
1151     LOG(log_debug, logtype_afpd, "afp_write(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)",
1152         offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1153
1154     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1155         err = AFPERR_ACCESS;
1156         goto afp_write_err;
1157     }
1158
1159 #ifdef AFS
1160     writtenfork = ofork;
1161 #endif /* AFS */
1162
1163     if ( ofork->of_flags & AFPFORK_DATA) {
1164         eid = ADEID_DFORK;
1165     } else if (ofork->of_flags & AFPFORK_RSRC) {
1166         eid = ADEID_RFORK;
1167     } else {
1168         err = AFPERR_ACCESS; /* should never happen */
1169         goto afp_write_err;
1170     }
1171
1172     oldsize = ad_size(ofork->of_ad, eid);
1173     if (endflag)
1174         offset += oldsize;
1175
1176     /* handle bogus parameters */
1177     if (reqcount < 0 || offset < 0) {
1178         err = AFPERR_PARAM;
1179         goto afp_write_err;
1180     }
1181
1182     newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1183
1184     /* offset can overflow on 64-bit capable filesystems.
1185      * report disk full if that's going to happen. */
1186     if (sum_neg(is64, offset, reqcount)) {
1187         err = AFPERR_DFULL;
1188         goto afp_write_err;
1189     }
1190
1191     if (!reqcount) { /* handle request counts of 0 */
1192         err = AFP_OK;
1193         *rbuflen = set_off_t (offset, rbuf, is64);
1194         goto afp_write_err;
1195     }
1196
1197     saveoff = offset;
1198     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1199                    reqcount, ofork->of_refnum) < 0) {
1200         err = AFPERR_LOCK;
1201         goto afp_write_err;
1202     }
1203
1204     DSI *dsi = obj->dsi;
1205     /* find out what we have already and write it out. */
1206     cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1207
1208     if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) {
1209         dsi_writeflush(dsi);
1210         *rbuflen = 0;
1211         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1212         return cc;
1213     }
1214
1215     offset += cc;
1216
1217 #if 0 /*def HAVE_SENDFILE_WRITE*/
1218     if (!(obj->options.flags & OPTION_DEBUG)) {
1219         if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1220                                offset, dsi->datasize)) < 0) {
1221             switch (errno) {
1222             case EDQUOT :
1223             case EFBIG :
1224             case ENOSPC :
1225                 cc = AFPERR_DFULL;
1226                 break;
1227             default :
1228                 LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1229                 goto afp_write_loop;
1230             }
1231             dsi_writeflush(dsi);
1232             *rbuflen = 0;
1233             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1234                        reqcount,  ofork->of_refnum);
1235             return cc;
1236         }
1237
1238         offset += cc;
1239         goto afp_write_done;
1240     }
1241 #endif /* 0, was HAVE_SENDFILE_WRITE */
1242
1243     /* loop until everything gets written. currently
1244      * dsi_write handles the end case by itself. */
1245     while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1246         if ((cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) {
1247             dsi_writeflush(dsi);
1248             *rbuflen = 0;
1249             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1250                        reqcount,  ofork->of_refnum);
1251             return cc;
1252         }
1253         offset += cc;
1254     }
1255
1256     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1257     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1258         ofork->of_flags |= AFPFORK_DIRTY;
1259
1260     /* we have modified any fork, remember until close_fork */
1261     ofork->of_flags |= AFPFORK_MODIFIED;
1262
1263     /* update write count */
1264     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1265
1266     *rbuflen = set_off_t (offset, rbuf, is64);
1267     return( AFP_OK );
1268
1269 afp_write_err:
1270     dsi_writeinit(obj->dsi, rbuf, *rbuflen);
1271     dsi_writeflush(obj->dsi);
1272
1273     if (err != AFP_OK) {
1274         *rbuflen = 0;
1275     }
1276     return err;
1277 }
1278
1279 /* ---------------------------- */
1280 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1281 {
1282     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1283 }
1284
1285 /* ----------------------------
1286  * FIXME need to deal with SIGXFSZ signal
1287  */
1288 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1289 {
1290     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1291 }
1292
1293 /* ---------------------------- */
1294 int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1295 {
1296     struct ofork    *ofork;
1297     int             ret;
1298     uint16_t       ofrefnum, bitmap;
1299     size_t          buflen;
1300     ibuf += 2;
1301     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1302     ibuf += sizeof( ofrefnum );
1303     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1304     bitmap = ntohs( bitmap );
1305     ibuf += sizeof( bitmap );
1306
1307     *rbuflen = 0;
1308     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1309         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1310         return( AFPERR_PARAM );
1311     }
1312
1313     if (AD_META_OPEN(ofork->of_ad)) {
1314         if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
1315             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1316             return( AFPERR_PARAM );
1317         }
1318     }
1319
1320     if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
1321         return( ret );
1322     }
1323
1324     *rbuflen = buflen + sizeof( u_short );
1325     bitmap = htons( bitmap );
1326     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1327     return( AFP_OK );
1328 }
1329