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