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