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