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