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