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