]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
c18c00632504ea1dab7e7647969a6fe7c20cf511
[netatalk.git] / etc / afpd / fork.c
1 /*
2  * $Id: fork.c,v 1.11.2.3 2002-02-07 23:56:58 srittau 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 #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             syslog( LOG_ERR, "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                 syslog( LOG_ERR, "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                 syslog( LOG_ERR, "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                 syslog( LOG_ERR, "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         syslog( LOG_ERR, "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             syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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, savereqcount;
782     int                 cc, err, saveerr, 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         syslog( LOG_ERR, "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     err = AFP_OK;
831     if (!reqcount) {
832         goto afp_read_err;
833     }
834
835     /* reqcount isn't always truthful. we need to deal with that. */
836     size = ad_size(ofork->of_ad, eid);
837
838     if (offset >= size) {
839         err = AFPERR_EOF;
840         goto afp_read_err;
841     }
842
843     /* subtract off the offset */
844     size -= offset;
845     savereqcount = reqcount;
846     if (reqcount > size) {
847         reqcount = size;
848         err = AFPERR_EOF;
849     }
850
851     saveoff = offset;
852     /* if EOF lock on the old reqcount, some prg may need it */
853     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount) < 0) {
854         err = AFPERR_LOCK;
855         goto afp_read_err;
856     }
857
858 #define min(a,b)        ((a)<(b)?(a):(b))
859     *rbuflen = min( reqcount, *rbuflen );
860     saveerr = err;
861     err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen,
862                     xlate);
863     if (err < 0)
864         goto afp_read_done;
865     if (saveerr < 0) {
866        err = saveerr;
867        goto afp_read_done;
868     }
869     /* dsi can stream requests. we can only do this if we're not checking
870      * for an end-of-line character. oh well. */
871     if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
872         DSI *dsi = obj->handle;
873
874         if (obj->options.flags & OPTION_DEBUG) {
875             printf( "(read) reply: %d/%d, %d\n", *rbuflen,
876                     reqcount, dsi->clientID);
877             bprint(rbuf, *rbuflen);
878         }
879
880         offset += *rbuflen;
881
882         /* dsi_readinit() returns size of next read buffer. by this point,
883          * we know that we're sending some data. if we fail, something
884          * horrible happened. */
885         if ((*rbuflen = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
886             goto afp_read_exit;
887
888         /* due to the nature of afp packets, we have to exit if we get
889            an error. we can't do this with translation on. */
890 #ifdef HAVE_SENDFILE_READ
891         if (!(xlate || (obj->options.flags & OPTION_DEBUG))) {
892             if (ad_readfile(ofork->of_ad, eid, dsi->socket, offset,
893                             dsi->datasize) < 0) {
894                 if (errno == EINVAL)
895                     goto afp_read_loop;
896                 else {
897                     syslog(LOG_ERR, "afp_read: ad_readfile: %s", strerror(errno));
898                     goto afp_read_exit;
899                 }
900             }
901
902             dsi_readdone(dsi);
903             goto afp_read_done;
904         }
905
906 afp_read_loop:
907 #endif /* HAVE_SENDFILE_READ */
908
909         /* fill up our buffer. */
910         while (*rbuflen > 0) {
911             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,
912                            rbuflen, xlate);
913             if (cc < 0)
914                 goto afp_read_exit;
915
916             offset += *rbuflen;
917             if (obj->options.flags & OPTION_DEBUG) {
918                 printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
919                 bprint(rbuf, *rbuflen);
920             }
921
922             /* dsi_read() also returns buffer size of next allocation */
923             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
924             if (cc < 0)
925                 goto afp_read_exit;
926             *rbuflen = cc;
927         }
928         dsi_readdone(dsi);
929         goto afp_read_done;
930
931 afp_read_exit:
932         syslog(LOG_ERR, "afp_read: %s", strerror(errno));
933         dsi_readdone(dsi);
934         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount);
935         obj->exit(1);
936     }
937
938 afp_read_done:
939     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount);
940     return err;
941
942 afp_read_err:
943     *rbuflen = 0;
944     return err;
945 }
946
947 int afp_flush(obj, ibuf, ibuflen, rbuf, rbuflen )
948 AFPObj      *obj;
949 char    *ibuf, *rbuf;
950 int             ibuflen, *rbuflen;
951 {
952     struct vol *vol;
953     u_int16_t vid;
954
955     *rbuflen = 0;
956     ibuf += 2;
957
958     memcpy(&vid, ibuf, sizeof(vid));
959     if (( vol = getvolbyvid( vid )) == NULL ) {
960         return( AFPERR_PARAM );
961     }
962
963     of_flush(vol);
964     return( AFP_OK );
965 }
966
967 int afp_flushfork(obj, ibuf, ibuflen, rbuf, rbuflen )
968 AFPObj      *obj;
969 char    *ibuf, *rbuf;
970 int             ibuflen, *rbuflen;
971 {
972     struct ofork        *ofork;
973     u_int16_t           ofrefnum;
974
975     *rbuflen = 0;
976     ibuf += 2;
977     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
978
979     if (( ofork = of_find( ofrefnum )) == NULL ) {
980         syslog( LOG_ERR, "afp_flushfork: of_find: %s", strerror(errno) );
981         return( AFPERR_PARAM );
982     }
983
984     if ( flushfork( ofork ) < 0 ) {
985         syslog( LOG_ERR, "afp_flushfork: %s", strerror(errno) );
986     }
987
988     return( AFP_OK );
989 }
990
991 /* this is very similar to closefork */
992 int flushfork( ofork )
993 struct ofork    *ofork;
994 {
995     struct timeval tv;
996     int len, err = 0, doflush = 0;
997
998     if ( ad_dfileno( ofork->of_ad ) != -1 &&
999             fsync( ad_dfileno( ofork->of_ad )) < 0 ) {
1000         syslog( LOG_ERR, "flushfork: dfile(%d) %s",
1001                 ad_dfileno(ofork->of_ad), strerror(errno) );
1002         err = -1;
1003     }
1004
1005     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1006
1007         /* read in the rfork length */
1008         len = ad_getentrylen(ofork->of_ad, ADEID_RFORK);
1009         ad_refresh(ofork->of_ad);
1010
1011         /* set the date if we're dirty */
1012         if ((ofork->of_flags & AFPFORK_DIRTY) &&
1013                 (gettimeofday(&tv, NULL) == 0)) {
1014             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1015             ofork->of_flags &= ~AFPFORK_DIRTY;
1016             doflush++;
1017         }
1018
1019         /* if we're actually flushing this fork, make sure to set the
1020          * length. otherwise, just use the stored length */
1021         if ((ofork->of_flags & AFPFORK_RSRC) &&
1022                 (len != ad_getentrylen(ofork->of_ad, ADEID_RFORK))) {
1023             ad_setentrylen(ofork->of_ad, ADEID_RFORK, len);
1024             doflush++;
1025         }
1026
1027
1028         /* flush the header (if it is a resource fork) */
1029         if (ofork->of_flags & AFPFORK_RSRC)
1030             if (doflush && (ad_flush(ofork->of_ad, ADFLAGS_HF) < 0))
1031                 err = -1;
1032
1033         if (fsync( ad_hfileno( ofork->of_ad )) < 0)
1034             err = -1;
1035
1036         if (err < 0)
1037             syslog( LOG_ERR, "flushfork: hfile(%d) %s",
1038                     ad_hfileno(ofork->of_ad), strerror(errno) );
1039     }
1040
1041     return( err );
1042 }
1043
1044 int afp_closefork(obj, ibuf, ibuflen, rbuf, rbuflen )
1045 AFPObj      *obj;
1046 char    *ibuf, *rbuf;
1047 int             ibuflen, *rbuflen;
1048 {
1049     struct ofork        *ofork;
1050     struct timeval      tv;
1051     int                 adflags, aint, doflush = 0;
1052     u_int16_t           ofrefnum;
1053
1054     *rbuflen = 0;
1055     ibuf += 2;
1056     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1057
1058     if (( ofork = of_find( ofrefnum )) == NULL ) {
1059         syslog( LOG_ERR, "afp_closefork: of_find: %s", strerror(errno) );
1060         return( AFPERR_PARAM );
1061     }
1062
1063     adflags = 0;
1064     if ((ofork->of_flags & AFPFORK_DATA) &&
1065             (ad_dfileno( ofork->of_ad ) != -1)) {
1066         adflags |= ADFLAGS_DF;
1067     }
1068
1069     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1070         adflags |= ADFLAGS_HF;
1071
1072         aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
1073         ad_refresh( ofork->of_ad );
1074         if ((ofork->of_flags & AFPFORK_DIRTY) &&
1075                 (gettimeofday(&tv, NULL) == 0)) {
1076             ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,
1077                        tv.tv_sec);
1078             doflush++;
1079         }
1080
1081         /*
1082          * Only set the rfork's length if we're closing the rfork.
1083          */
1084         if ((ofork->of_flags & AFPFORK_RSRC) && aint !=
1085                 ad_getentrylen( ofork->of_ad, ADEID_RFORK )) {
1086             ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
1087             doflush++;
1088         }
1089         if ( doflush ) {
1090             ad_flush( ofork->of_ad, adflags );
1091         }
1092     }
1093
1094     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
1095         syslog( LOG_ERR, "afp_closefork: ad_close: %s", strerror(errno) );
1096         return( AFPERR_PARAM );
1097     }
1098
1099     of_dealloc( ofork );
1100     return( AFP_OK );
1101 }
1102
1103
1104 static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
1105                                      off_t offset, char *rbuf,
1106                                      size_t rbuflen, const int xlate)
1107 {
1108     char *p, *q;
1109     ssize_t cc;
1110
1111     /*
1112      * If this file is of type TEXT, swap \015 to \012.
1113      */
1114     if (xlate) {
1115         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1116             if ( *p == '\015' ) {
1117                 *p = '\012';
1118             } else if ( *p == '\012' ) {
1119                 *p = '\015';
1120             }
1121         }
1122     }
1123
1124     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1125                         rbuf, rbuflen)) < 0 ) {
1126         switch ( errno ) {
1127         case EDQUOT :
1128         case EFBIG :
1129         case ENOSPC :
1130             return( AFPERR_DFULL );
1131         default :
1132             syslog( LOG_ERR, "afp_write: ad_write: %s", strerror(errno) );
1133             return( AFPERR_PARAM );
1134         }
1135     }
1136
1137     return cc;
1138 }
1139
1140 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1141  * the client may have sent us a bunch of data that's not reflected 
1142  * in reqcount et al. */
1143 int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
1144 AFPObj              *obj;
1145 char                *ibuf, *rbuf;
1146 int                 ibuflen, *rbuflen;
1147 {
1148     struct ofork        *ofork;
1149     int32_t             offset, saveoff, reqcount;
1150     int                 endflag, eid, xlate = 0, err = AFP_OK;
1151     u_int16_t           ofrefnum;
1152     ssize_t             cc;
1153
1154     /* figure out parameters */
1155     ibuf++;
1156     endflag = ENDBIT(*ibuf);
1157     ibuf++;
1158     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1159     ibuf += sizeof( ofrefnum );
1160     memcpy(&offset, ibuf, sizeof( offset ));
1161     offset = ntohl( offset );
1162     ibuf += sizeof( offset );
1163     memcpy(&reqcount, ibuf, sizeof( reqcount ));
1164     reqcount = ntohl( reqcount );
1165     ibuf += sizeof( reqcount );
1166
1167     if (( ofork = of_find( ofrefnum )) == NULL ) {
1168         syslog( LOG_ERR, "afp_write: of_find: %s", strerror(errno) );
1169         err = AFPERR_PARAM;
1170         goto afp_write_err;
1171     }
1172
1173     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1174         err = AFPERR_ACCESS;
1175         goto afp_write_err;
1176     }
1177
1178 #ifdef AFS
1179     writtenfork = ofork;
1180 #endif /* AFS */
1181
1182     if ( ofork->of_flags & AFPFORK_DATA) {
1183         eid = ADEID_DFORK;
1184         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1185     } else if (ofork->of_flags & AFPFORK_RSRC) {
1186         eid = ADEID_RFORK;
1187     } else {
1188         err = AFPERR_ACCESS; /* should never happen */
1189         goto afp_write_err;
1190     }
1191
1192     if (endflag)
1193         offset += ad_size(ofork->of_ad, eid);
1194
1195     /* handle bogus parameters */
1196     if (reqcount < 0 || offset < 0) {
1197         err = AFPERR_PARAM;
1198         goto afp_write_err;
1199     }
1200
1201     /* offset can overflow on 64-bit capable filesystems.
1202      * report disk full if that's going to happen. */
1203     if (offset + reqcount < 0) {
1204         err = AFPERR_DFULL;
1205         goto afp_write_err;
1206     }
1207
1208     if (!reqcount) { /* handle request counts of 0 */
1209         err = AFP_OK;
1210         offset = htonl(offset);
1211         memcpy(rbuf, &offset, sizeof(offset));
1212         goto afp_write_err;
1213     }
1214
1215     saveoff = offset;
1216     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1217                    reqcount) < 0) {
1218         err = AFPERR_LOCK;
1219         goto afp_write_err;
1220     }
1221
1222     /* this is yucky, but dsi can stream i/o and asp can't */
1223     switch (obj->proto) {
1224 #ifndef NO_DDP
1225     case AFPPROTO_ASP:
1226         if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1227             *rbuflen = 0;
1228             syslog( LOG_ERR, "afp_write: asp_wrtcont: %s", strerror(errno) );
1229             return( AFPERR_PARAM );
1230         }
1231
1232         if (obj->options.flags & OPTION_DEBUG) {
1233             printf("(write) len: %d\n", *rbuflen);
1234             bprint(rbuf, *rbuflen);
1235         }
1236
1237         if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1238                              xlate)) < 0) {
1239             *rbuflen = 0;
1240             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1241             return cc;
1242         }
1243         offset += cc;
1244         break;
1245 #endif /* no afp/asp */
1246
1247     case AFPPROTO_DSI:
1248         {
1249             DSI *dsi = obj->handle;
1250
1251             /* find out what we have already and write it out. */
1252             cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1253             if (!cc ||
1254                     (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1255                 dsi_writeflush(dsi);
1256                 *rbuflen = 0;
1257                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1258                 return cc;
1259             }
1260             offset += cc;
1261
1262 #if 0 /*def HAVE_SENDFILE_WRITE*/
1263             if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1264                 if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1265                                        offset, dsi->datasize)) < 0) {
1266                     switch (errno) {
1267                     case EDQUOT :
1268                     case EFBIG :
1269                     case ENOSPC :
1270                         cc = AFPERR_DFULL;
1271                         break;
1272                     default :
1273                         syslog( LOG_ERR, "afp_write: ad_writefile: %s", strerror(errno) );
1274                         goto afp_write_loop;
1275                     }
1276                     dsi_writeflush(dsi);
1277                     *rbuflen = 0;
1278                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1279                                reqcount);
1280                     return cc;
1281                 }
1282
1283                 offset += cc;
1284                 goto afp_write_done;
1285             }
1286 #endif /* 0, was HAVE_SENDFILE_WRITE */
1287
1288             /* loop until everything gets written. currently
1289                     * dsi_write handles the end case by itself. */
1290 afp_write_loop:
1291             while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1292                 if ( obj->options.flags & OPTION_DEBUG ) {
1293                     printf("(write) command cont'd: %d\n", cc);
1294                     bprint(rbuf, cc);
1295                 }
1296
1297                 if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1298                     dsi_writeflush(dsi);
1299                     *rbuflen = 0;
1300                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1301                                reqcount);
1302                     return cc;
1303                 }
1304                 offset += cc;
1305             }
1306         }
1307         break;
1308     }
1309
1310 afp_write_done:
1311     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1312     if ( ad_hfileno( ofork->of_ad ) != -1 )
1313         ofork->of_flags |= AFPFORK_DIRTY;
1314
1315     offset = htonl( offset );
1316 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1317     bcopy(&offset, rbuf, sizeof(offset));
1318 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1319     memcpy(rbuf, &offset, sizeof(offset));
1320 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1321     *rbuflen = sizeof(offset);
1322     return( AFP_OK );
1323
1324 afp_write_err:
1325     if (obj->proto == AFPPROTO_DSI) {
1326         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1327         dsi_writeflush(obj->handle);
1328     }
1329
1330     *rbuflen = (err == AFP_OK) ? sizeof(offset) : 0;
1331     return err;
1332 }
1333
1334
1335 int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1336 AFPObj      *obj;
1337 char    *ibuf, *rbuf;
1338 int             ibuflen, *rbuflen;
1339 {
1340     struct ofork        *ofork;
1341     int                 buflen, ret;
1342     u_int16_t           ofrefnum, bitmap;
1343
1344     ibuf += 2;
1345     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1346     ibuf += sizeof( ofrefnum );
1347     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1348     bitmap = ntohs( bitmap );
1349     ibuf += sizeof( bitmap );
1350
1351     *rbuflen = 0;
1352     if (( ofork = of_find( ofrefnum )) == NULL ) {
1353         syslog( LOG_ERR, "afp_getforkparams: of_find: %s", strerror(errno) );
1354         return( AFPERR_PARAM );
1355     }
1356
1357     if (( ret = getforkparams( ofork, bitmap,
1358                                rbuf + sizeof( u_short ), &buflen, 0 )) != AFP_OK ) {
1359         return( ret );
1360     }
1361
1362     *rbuflen = buflen + sizeof( u_short );
1363     bitmap = htons( bitmap );
1364     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1365     return( AFP_OK );
1366 }
1367