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