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