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