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