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