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