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