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