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