]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
MFH: 1.24
[netatalk.git] / etc / afpd / fork.c
1 /*
2  * $Id: fork.c,v 1.11.2.5 2002-03-11 17:54:01 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 <syslog.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             syslog( LOG_ERR, "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                     syslog(LOG_ERR, "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                 syslog( LOG_ERR, "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                 syslog( LOG_ERR, "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                 syslog( LOG_ERR, "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         syslog( LOG_ERR, "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             syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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     }
880     /* dsi can stream requests. we can only do this if we're not checking
881      * for an end-of-line character. oh well. */
882     if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
883         DSI *dsi = obj->handle;
884
885         if (obj->options.flags & OPTION_DEBUG) {
886             printf( "(read) reply: %d/%d, %d\n", *rbuflen,
887                     reqcount, dsi->clientID);
888             bprint(rbuf, *rbuflen);
889         }
890
891         offset += *rbuflen;
892
893         /* dsi_readinit() returns size of next read buffer. by this point,
894          * we know that we're sending some data. if we fail, something
895          * horrible happened. */
896         if ((*rbuflen = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
897             goto afp_read_exit;
898
899         /* due to the nature of afp packets, we have to exit if we get
900            an error. we can't do this with translation on. */
901 #ifdef HAVE_SENDFILE_READ
902         if (!(xlate || (obj->options.flags & OPTION_DEBUG))) {
903             if (ad_readfile(ofork->of_ad, eid, dsi->socket, offset,
904                             dsi->datasize) < 0) {
905                 if (errno == EINVAL)
906                     goto afp_read_loop;
907                 else {
908                     syslog(LOG_ERR, "afp_read: ad_readfile: %s", strerror(errno));
909                     goto afp_read_exit;
910                 }
911             }
912
913             dsi_readdone(dsi);
914             goto afp_read_done;
915         }
916
917 afp_read_loop:
918 #endif /* HAVE_SENDFILE_READ */
919
920         /* fill up our buffer. */
921         while (*rbuflen > 0) {
922             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,
923                            rbuflen, xlate);
924             if (cc < 0)
925                 goto afp_read_exit;
926
927             offset += *rbuflen;
928             if (obj->options.flags & OPTION_DEBUG) {
929                 printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
930                 bprint(rbuf, *rbuflen);
931             }
932
933             /* dsi_read() also returns buffer size of next allocation */
934             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
935             if (cc < 0)
936                 goto afp_read_exit;
937             *rbuflen = cc;
938         }
939         dsi_readdone(dsi);
940         goto afp_read_done;
941
942 afp_read_exit:
943         syslog(LOG_ERR, "afp_read: %s", strerror(errno));
944         dsi_readdone(dsi);
945         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount);
946         obj->exit(1);
947     }
948
949 afp_read_done:
950     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount);
951     return err;
952
953 afp_read_err:
954     *rbuflen = 0;
955     return err;
956 }
957
958 int afp_flush(obj, ibuf, ibuflen, rbuf, rbuflen )
959 AFPObj      *obj;
960 char    *ibuf, *rbuf;
961 int             ibuflen, *rbuflen;
962 {
963     struct vol *vol;
964     u_int16_t vid;
965
966     *rbuflen = 0;
967     ibuf += 2;
968
969     memcpy(&vid, ibuf, sizeof(vid));
970     if (( vol = getvolbyvid( vid )) == NULL ) {
971         return( AFPERR_PARAM );
972     }
973
974     of_flush(vol);
975     return( AFP_OK );
976 }
977
978 int afp_flushfork(obj, ibuf, ibuflen, rbuf, rbuflen )
979 AFPObj      *obj;
980 char    *ibuf, *rbuf;
981 int             ibuflen, *rbuflen;
982 {
983     struct ofork        *ofork;
984     u_int16_t           ofrefnum;
985
986     *rbuflen = 0;
987     ibuf += 2;
988     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
989
990     if (( ofork = of_find( ofrefnum )) == NULL ) {
991         syslog( LOG_ERR, "afp_flushfork: of_find: %s", strerror(errno) );
992         return( AFPERR_PARAM );
993     }
994
995     if ( flushfork( ofork ) < 0 ) {
996         syslog( LOG_ERR, "afp_flushfork: %s", strerror(errno) );
997     }
998
999     return( AFP_OK );
1000 }
1001
1002 /* this is very similar to closefork */
1003 int flushfork( ofork )
1004 struct ofork    *ofork;
1005 {
1006     struct timeval tv;
1007     int len, err = 0, doflush = 0;
1008
1009     if ( ad_dfileno( ofork->of_ad ) != -1 &&
1010             fsync( ad_dfileno( ofork->of_ad )) < 0 ) {
1011         syslog( LOG_ERR, "flushfork: dfile(%d) %s",
1012                 ad_dfileno(ofork->of_ad), strerror(errno) );
1013         err = -1;
1014     }
1015
1016     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1017
1018         /* read in the rfork length */
1019         len = ad_getentrylen(ofork->of_ad, ADEID_RFORK);
1020         ad_refresh(ofork->of_ad);
1021
1022         /* set the date if we're dirty */
1023         if ((ofork->of_flags & AFPFORK_DIRTY) &&
1024                 (gettimeofday(&tv, NULL) == 0)) {
1025             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1026             ofork->of_flags &= ~AFPFORK_DIRTY;
1027             doflush++;
1028         }
1029
1030         /* if we're actually flushing this fork, make sure to set the
1031          * length. otherwise, just use the stored length */
1032         if ((ofork->of_flags & AFPFORK_RSRC) &&
1033                 (len != ad_getentrylen(ofork->of_ad, ADEID_RFORK))) {
1034             ad_setentrylen(ofork->of_ad, ADEID_RFORK, len);
1035             doflush++;
1036         }
1037
1038
1039         /* flush the header (if it is a resource fork) */
1040         if (ofork->of_flags & AFPFORK_RSRC)
1041             if (doflush && (ad_flush(ofork->of_ad, ADFLAGS_HF) < 0))
1042                 err = -1;
1043
1044         if (fsync( ad_hfileno( ofork->of_ad )) < 0)
1045             err = -1;
1046
1047         if (err < 0)
1048             syslog( LOG_ERR, "flushfork: hfile(%d) %s",
1049                     ad_hfileno(ofork->of_ad), strerror(errno) );
1050     }
1051
1052     return( err );
1053 }
1054
1055 int afp_closefork(obj, ibuf, ibuflen, rbuf, rbuflen )
1056 AFPObj      *obj;
1057 char    *ibuf, *rbuf;
1058 int             ibuflen, *rbuflen;
1059 {
1060     struct ofork        *ofork;
1061     struct timeval      tv;
1062     int                 adflags, aint, doflush = 0;
1063     u_int16_t           ofrefnum;
1064
1065     *rbuflen = 0;
1066     ibuf += 2;
1067     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1068
1069     if (( ofork = of_find( ofrefnum )) == NULL ) {
1070         syslog( LOG_ERR, "afp_closefork: of_find: %s", strerror(errno) );
1071         return( AFPERR_PARAM );
1072     }
1073
1074     adflags = 0;
1075     if ((ofork->of_flags & AFPFORK_DATA) &&
1076             (ad_dfileno( ofork->of_ad ) != -1)) {
1077         adflags |= ADFLAGS_DF;
1078     }
1079
1080     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1081         adflags |= ADFLAGS_HF;
1082
1083         aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
1084         ad_refresh( ofork->of_ad );
1085         if ((ofork->of_flags & AFPFORK_DIRTY) &&
1086                 (gettimeofday(&tv, NULL) == 0)) {
1087             ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,
1088                        tv.tv_sec);
1089             doflush++;
1090         }
1091
1092         /*
1093          * Only set the rfork's length if we're closing the rfork.
1094          */
1095         if ((ofork->of_flags & AFPFORK_RSRC) && aint !=
1096                 ad_getentrylen( ofork->of_ad, ADEID_RFORK )) {
1097             ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
1098             doflush++;
1099         }
1100         if ( doflush ) {
1101             ad_flush( ofork->of_ad, adflags );
1102         }
1103     }
1104
1105     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
1106         syslog( LOG_ERR, "afp_closefork: ad_close: %s", strerror(errno) );
1107         return( AFPERR_PARAM );
1108     }
1109
1110     of_dealloc( ofork );
1111     return( AFP_OK );
1112 }
1113
1114
1115 static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
1116                                      off_t offset, char *rbuf,
1117                                      size_t rbuflen, const int xlate)
1118 {
1119     char *p, *q;
1120     ssize_t cc;
1121
1122     /*
1123      * If this file is of type TEXT, swap \015 to \012.
1124      */
1125     if (xlate) {
1126         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1127             if ( *p == '\015' ) {
1128                 *p = '\012';
1129             } else if ( *p == '\012' ) {
1130                 *p = '\015';
1131             }
1132         }
1133     }
1134
1135     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1136                         rbuf, rbuflen)) < 0 ) {
1137         switch ( errno ) {
1138         case EDQUOT :
1139         case EFBIG :
1140         case ENOSPC :
1141             return( AFPERR_DFULL );
1142         default :
1143             syslog( LOG_ERR, "afp_write: ad_write: %s", strerror(errno) );
1144             return( AFPERR_PARAM );
1145         }
1146     }
1147
1148     return cc;
1149 }
1150
1151 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1152  * the client may have sent us a bunch of data that's not reflected 
1153  * in reqcount et al. */
1154 int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
1155 AFPObj              *obj;
1156 char                *ibuf, *rbuf;
1157 int                 ibuflen, *rbuflen;
1158 {
1159     struct ofork        *ofork;
1160     int32_t             offset, saveoff, reqcount;
1161     int                 endflag, eid, xlate = 0, err = AFP_OK;
1162     u_int16_t           ofrefnum;
1163     ssize_t             cc;
1164
1165     /* figure out parameters */
1166     ibuf++;
1167     endflag = ENDBIT(*ibuf);
1168     ibuf++;
1169     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1170     ibuf += sizeof( ofrefnum );
1171     memcpy(&offset, ibuf, sizeof( offset ));
1172     offset = ntohl( offset );
1173     ibuf += sizeof( offset );
1174     memcpy(&reqcount, ibuf, sizeof( reqcount ));
1175     reqcount = ntohl( reqcount );
1176     ibuf += sizeof( reqcount );
1177
1178     if (( ofork = of_find( ofrefnum )) == NULL ) {
1179         syslog( LOG_ERR, "afp_write: of_find: %s", strerror(errno) );
1180         err = AFPERR_PARAM;
1181         goto afp_write_err;
1182     }
1183
1184     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1185         err = AFPERR_ACCESS;
1186         goto afp_write_err;
1187     }
1188
1189 #ifdef AFS
1190     writtenfork = ofork;
1191 #endif /* AFS */
1192
1193     if ( ofork->of_flags & AFPFORK_DATA) {
1194         eid = ADEID_DFORK;
1195         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1196     } else if (ofork->of_flags & AFPFORK_RSRC) {
1197         eid = ADEID_RFORK;
1198     } else {
1199         err = AFPERR_ACCESS; /* should never happen */
1200         goto afp_write_err;
1201     }
1202
1203     if (endflag)
1204         offset += ad_size(ofork->of_ad, eid);
1205
1206     /* handle bogus parameters */
1207     if (reqcount < 0 || offset < 0) {
1208         err = AFPERR_PARAM;
1209         goto afp_write_err;
1210     }
1211
1212     /* offset can overflow on 64-bit capable filesystems.
1213      * report disk full if that's going to happen. */
1214     if (offset + reqcount < 0) {
1215         err = AFPERR_DFULL;
1216         goto afp_write_err;
1217     }
1218
1219     if (!reqcount) { /* handle request counts of 0 */
1220         err = AFP_OK;
1221         offset = htonl(offset);
1222         memcpy(rbuf, &offset, sizeof(offset));
1223         goto afp_write_err;
1224     }
1225
1226     saveoff = offset;
1227     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1228                    reqcount) < 0) {
1229         err = AFPERR_LOCK;
1230         goto afp_write_err;
1231     }
1232
1233     /* this is yucky, but dsi can stream i/o and asp can't */
1234     switch (obj->proto) {
1235 #ifndef NO_DDP
1236     case AFPPROTO_ASP:
1237         if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1238             *rbuflen = 0;
1239             syslog( LOG_ERR, "afp_write: asp_wrtcont: %s", strerror(errno) );
1240             return( AFPERR_PARAM );
1241         }
1242
1243         if (obj->options.flags & OPTION_DEBUG) {
1244             printf("(write) len: %d\n", *rbuflen);
1245             bprint(rbuf, *rbuflen);
1246         }
1247
1248         if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1249                              xlate)) < 0) {
1250             *rbuflen = 0;
1251             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1252             return cc;
1253         }
1254         offset += cc;
1255         break;
1256 #endif /* no afp/asp */
1257
1258     case AFPPROTO_DSI:
1259         {
1260             DSI *dsi = obj->handle;
1261
1262             /* find out what we have already and write it out. */
1263             cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1264             if (!cc ||
1265                     (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1266                 dsi_writeflush(dsi);
1267                 *rbuflen = 0;
1268                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1269                 return cc;
1270             }
1271             offset += cc;
1272
1273 #if 0 /*def HAVE_SENDFILE_WRITE*/
1274             if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1275                 if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1276                                        offset, dsi->datasize)) < 0) {
1277                     switch (errno) {
1278                     case EDQUOT :
1279                     case EFBIG :
1280                     case ENOSPC :
1281                         cc = AFPERR_DFULL;
1282                         break;
1283                     default :
1284                         syslog( LOG_ERR, "afp_write: ad_writefile: %s", strerror(errno) );
1285                         goto afp_write_loop;
1286                     }
1287                     dsi_writeflush(dsi);
1288                     *rbuflen = 0;
1289                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1290                                reqcount);
1291                     return cc;
1292                 }
1293
1294                 offset += cc;
1295                 goto afp_write_done;
1296             }
1297 #endif /* 0, was HAVE_SENDFILE_WRITE */
1298
1299             /* loop until everything gets written. currently
1300                     * dsi_write handles the end case by itself. */
1301 afp_write_loop:
1302             while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1303                 if ( obj->options.flags & OPTION_DEBUG ) {
1304                     printf("(write) command cont'd: %d\n", cc);
1305                     bprint(rbuf, cc);
1306                 }
1307
1308                 if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1309                     dsi_writeflush(dsi);
1310                     *rbuflen = 0;
1311                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1312                                reqcount);
1313                     return cc;
1314                 }
1315                 offset += cc;
1316             }
1317         }
1318         break;
1319     }
1320
1321 afp_write_done:
1322     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
1323     if ( ad_hfileno( ofork->of_ad ) != -1 )
1324         ofork->of_flags |= AFPFORK_DIRTY;
1325
1326     offset = htonl( offset );
1327 #if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
1328     bcopy(&offset, rbuf, sizeof(offset));
1329 #else /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1330     memcpy(rbuf, &offset, sizeof(offset));
1331 #endif /* __GNUC__ && HAVE_GCC_MEMCPY_BUG */
1332     *rbuflen = sizeof(offset);
1333     return( AFP_OK );
1334
1335 afp_write_err:
1336     if (obj->proto == AFPPROTO_DSI) {
1337         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1338         dsi_writeflush(obj->handle);
1339     }
1340
1341     *rbuflen = (err == AFP_OK) ? sizeof(offset) : 0;
1342     return err;
1343 }
1344
1345
1346 int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1347 AFPObj      *obj;
1348 char    *ibuf, *rbuf;
1349 int             ibuflen, *rbuflen;
1350 {
1351     struct ofork        *ofork;
1352     int                 buflen, ret;
1353     u_int16_t           ofrefnum, bitmap;
1354
1355     ibuf += 2;
1356     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1357     ibuf += sizeof( ofrefnum );
1358     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1359     bitmap = ntohs( bitmap );
1360     ibuf += sizeof( bitmap );
1361
1362     *rbuflen = 0;
1363     if (( ofork = of_find( ofrefnum )) == NULL ) {
1364         syslog( LOG_ERR, "afp_getforkparams: of_find: %s", strerror(errno) );
1365         return( AFPERR_PARAM );
1366     }
1367
1368     if (( ret = getforkparams( ofork, bitmap,
1369                                rbuf + sizeof( u_short ), &buflen, 0 )) != AFP_OK ) {
1370         return( ret );
1371     }
1372
1373     *rbuflen = buflen + sizeof( u_short );
1374     bitmap = htons( bitmap );
1375     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1376     return( AFP_OK );
1377 }
1378