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