]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
Fix fce merge conflict
[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 = dirlookup(vol, ofork->of_did);
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/%s'): ad_open: errno: %i (%s)",
437                     getcwdpath, s_path->m_name, errno, strerror(errno) );
438                 goto openfork_err;
439                 break;
440             }
441         }
442         else {
443             /* the ressource fork is open too */
444             ofork->of_flags |= AFPFORK_OPEN;
445         }
446     }
447
448     if ((adflags & ADFLAGS_HF) && (ad_get_HF_flags( ofork->of_ad) & O_CREAT)) {
449         if (ad_setname(ofork->of_ad, path)) {
450             ad_flush( ofork->of_ad );
451         }
452     }
453
454     if (( ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof( u_int16_t ),
455                               &buflen )) != AFP_OK ) {
456         ad_close( ofork->of_ad, adflags );
457         goto openfork_err;
458     }
459
460     *rbuflen = buflen + 2 * sizeof( u_int16_t );
461     bitmap = htons( bitmap );
462     memcpy(rbuf, &bitmap, sizeof( u_int16_t ));
463     rbuf += sizeof( u_int16_t );
464
465     /* check  WriteInhibit bit if we have a ressource fork
466      * the test is done here, after some Mac trafic capture 
467      */
468     if (ad_meta_fileno(ofork->of_ad) != -1) {   /* META */
469         ad_getattr(ofork->of_ad, &bshort);
470         if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
471             ad_close( ofork->of_ad, adflags );
472             of_dealloc( ofork );
473             ofrefnum = 0;
474             memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
475             return(AFPERR_OLOCK);
476         }
477     }
478
479     /*
480      * synchronization locks:
481      */
482
483     /* don't try to lock non-existent rforks. */
484     if ((eid == ADEID_DFORK) || (ad_meta_fileno(ofork->of_ad) != -1)) { /* META */
485
486         ret = fork_setmode(ofork->of_ad, eid, access, ofrefnum);
487         /* can we access the fork? */
488         if (ret < 0) {
489             ret = errno;
490             ad_close( ofork->of_ad, adflags );
491             of_dealloc( ofork );
492             switch (ret) {
493             case EAGAIN: /* return data anyway */
494             case EACCES:
495             case EINVAL:
496                 ofrefnum = 0;
497                 memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
498                 return( AFPERR_DENYCONF );
499                 break;
500             default:
501                 *rbuflen = 0;
502                 LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_lock: %s", s_path->m_name, strerror(ret) );
503                 return( AFPERR_PARAM );
504             }
505         }
506         if ((access & OPENACC_WR))
507             ofork->of_flags |= AFPFORK_ACCWR;
508     }
509     /* the file may be open read only without ressource fork */
510     if ((access & OPENACC_RD))
511         ofork->of_flags |= AFPFORK_ACCRD;
512
513     memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
514     return( AFP_OK );
515
516 openfork_err:
517     of_dealloc( ofork );
518     if (errno == EACCES)
519         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
520     return ret;
521 }
522
523 int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
524 {
525     struct ofork        *ofork;
526     off_t               size;
527     u_int16_t           ofrefnum, bitmap;
528     int                 err;
529     int                 is64;
530     int                 eid;
531     off_t               st_size;
532     
533     ibuf += 2;
534
535     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
536     ibuf += sizeof( ofrefnum );
537
538     memcpy(&bitmap, ibuf, sizeof(bitmap));
539     bitmap = ntohs(bitmap);
540     ibuf += sizeof( bitmap );
541
542     *rbuflen = 0;
543     if (NULL == ( ofork = of_find( ofrefnum )) ) {
544         LOG(log_error, logtype_afpd, "afp_setforkparams: of_find(%d) could not locate fork", ofrefnum );
545         return( AFPERR_PARAM );
546     }
547
548     if (ofork->of_vol->v_flags & AFPVOL_RO)
549         return AFPERR_VLOCK;
550
551     if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
552         return AFPERR_ACCESS;
553
554     if ( ofork->of_flags & AFPFORK_DATA) {
555         eid = ADEID_DFORK;
556     } else if (ofork->of_flags & AFPFORK_RSRC) {
557         eid = ADEID_RFORK;
558     } else
559         return AFPERR_PARAM;
560
561     if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) )) 
562                   && eid == ADEID_RFORK 
563          ) ||
564          ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) )) 
565                   && eid == ADEID_DFORK)) {
566         return AFPERR_BITMAP;
567     }
568     
569     is64 = 0;
570     if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
571         if (afp_version >= 30) {
572             is64 = 4;
573         }
574         else 
575            return AFPERR_BITMAP;
576     }
577
578     if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
579         return AFPERR_PARAM ;
580     
581     size = get_off_t(&ibuf, is64);
582
583     if (size < 0)
584         return AFPERR_PARAM; /* Some MacOS don't return an error they just don't change the size! */
585
586
587     if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
588         st_size = ad_size(ofork->of_ad, eid);
589         err = -2;
590         if (st_size > size && 
591               ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) 
592             goto afp_setfork_err;
593
594         err = ad_dtruncate( ofork->of_ad, size );
595         if (st_size > size)
596             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
597         if (err < 0)
598             goto afp_setfork_err;
599     } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
600         ad_refresh( ofork->of_ad );
601
602         st_size = ad_size(ofork->of_ad, eid);
603         err = -2;
604         if (st_size > size && 
605                ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
606             goto afp_setfork_err;
607         }
608         err = ad_rtruncate(ofork->of_ad, size);
609         if (st_size > size)
610             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
611         if (err < 0)
612             goto afp_setfork_err;
613
614         if (ad_flush( ofork->of_ad ) < 0) {
615             LOG(log_error, logtype_afpd, "afp_setforkparams(%s): ad_flush: %s", of_name(ofork), strerror(errno) );
616             return AFPERR_PARAM;
617         }
618     } else
619         return AFPERR_BITMAP;
620
621 #ifdef AFS
622     if ( flushfork( ofork ) < 0 ) {
623         LOG(log_error, logtype_afpd, "afp_setforkparams(%s): flushfork: %s", of_name(ofork), strerror(errno) );
624     }
625 #endif /* AFS */
626
627     return( AFP_OK );
628
629 afp_setfork_err:
630     if (err == -2)
631         return AFPERR_LOCK;
632     else {
633         switch (errno) {
634         case EROFS:
635             return AFPERR_VLOCK;
636         case EPERM:
637         case EACCES:
638             return AFPERR_ACCESS;
639         case EDQUOT:
640         case EFBIG:
641         case ENOSPC:
642             return AFPERR_DFULL;
643         default:
644             return AFPERR_PARAM;
645         }
646     }
647 }
648
649 /* for this to work correctly, we need to check for locks before each
650  * read and write. that's most easily handled by always doing an
651  * appropriate check before each ad_read/ad_write. other things
652  * that can change files like truncate are handled internally to those
653  * functions. 
654  */
655 #define ENDBIT(a)  ((a) & 0x80)
656 #define UNLOCKBIT(a) ((a) & 0x01)
657
658
659 /* ---------------------- */
660 static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
661 {
662     struct ofork        *ofork;
663     off_t               offset, length;
664     int                 eid;
665     u_int16_t           ofrefnum;
666     u_int8_t            flags;
667     int                 lockop;
668     
669     *rbuflen = 0;
670
671     /* figure out parameters */
672     ibuf++;
673     flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
674     ibuf++;
675     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
676     ibuf += sizeof(ofrefnum);
677
678     if (NULL == ( ofork = of_find( ofrefnum )) ) {
679         LOG(log_error, logtype_afpd, "afp_bytelock: of_find(%d) could not locate fork", ofrefnum );
680         return( AFPERR_PARAM );
681     }
682
683     if ( ofork->of_flags & AFPFORK_DATA) {
684         eid = ADEID_DFORK;
685     } else if (ofork->of_flags & AFPFORK_RSRC) {
686         eid = ADEID_RFORK;
687     } else
688         return AFPERR_PARAM;
689
690     offset = get_off_t(&ibuf, is64);
691     length = get_off_t(&ibuf, is64);
692
693     /* FIXME AD_FILELOCK test is surely wrong */
694     if (length == -1)
695         length = BYTELOCK_MAX;
696      else if (!length || is_neg(is64, length)) {
697         return AFPERR_PARAM;
698      } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
699         return AFPERR_LOCK;
700     }
701
702     if (ENDBIT(flags)) {
703         offset += ad_size(ofork->of_ad, eid);
704         /* FIXME what do we do if file size > 2 GB and 
705            it's not byte_lock_ext?
706         */
707     }
708     if (offset < 0)    /* error if we have a negative offset */
709         return AFPERR_PARAM;
710
711     /* if the file is a read-only file, we use read locks instead of
712      * write locks. that way, we can prevent anyone from initiating
713      * a write lock. */
714     lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
715     if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
716                 ofork->of_refnum) < 0) {
717         switch (errno) {
718         case EACCES:
719         case EAGAIN:
720             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
721             break;
722         case ENOLCK:
723             return AFPERR_NLOCK;
724             break;
725         case EINVAL:
726             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
727             break;
728         case EBADF:
729         default:
730             return AFPERR_PARAM;
731             break;
732         }
733     }
734     *rbuflen = set_off_t (offset, rbuf, is64);
735     return( AFP_OK );
736 }
737
738 /* --------------------------- */
739 int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
740 {
741    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
742 }
743
744 /* --------------------------- */
745 int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
746 {
747    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
748 }
749
750 #undef UNLOCKBIT
751
752 /* --------------------------- */
753 static int crlf(struct ofork *of)
754 {
755     struct extmap       *em;
756
757     if ( ad_meta_fileno( of->of_ad ) == -1 || !memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI),8)) { /* META */
758         /* no resource fork or no finderinfo, use our files extension mapping */
759         if (!( em = getextmap( of_name(of) )) || memcmp( "TEXT", em->em_type, sizeof( em->em_type ))) {
760             return 0;
761         } 
762         /* file type is TEXT */
763         return 1;
764
765     } else if ( !memcmp( "TEXT", ad_entry( of->of_ad, ADEID_FINDERI ), 4 )) {
766         return 1;
767     }
768     return 0;
769 }
770
771
772 static ssize_t read_file(struct ofork *ofork, int eid,
773                                     off_t offset, u_char nlmask,
774                                     u_char nlchar, char *rbuf,
775                                     size_t *rbuflen, const int xlate)
776 {
777     ssize_t cc;
778     int eof = 0;
779     char *p, *q;
780
781     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
782     if ( cc < 0 ) {
783         LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
784         *rbuflen = 0;
785         return( AFPERR_PARAM );
786     }
787     if ( (size_t)cc < *rbuflen ) {
788         eof = 1;
789     }
790
791     /*
792      * Do Newline check.
793      */
794     if ( nlmask != 0 ) {
795         for ( p = rbuf, q = p + cc; p < q; ) {
796             if (( *p++ & nlmask ) == nlchar ) {
797                 break;
798             }
799         }
800         if ( p != q ) {
801             cc = p - rbuf;
802             eof = 0;
803         }
804     }
805
806     /*
807      * If this file is of type TEXT, then swap \012 to \015.
808      */
809     if (xlate) {
810         for ( p = rbuf, q = p + cc; p < q; p++ ) {
811             if ( *p == '\012' ) {
812                 *p = '\015';
813             } else if ( *p == '\015' ) {
814                 *p = '\012';
815             }
816
817         }
818     }
819
820     *rbuflen = cc;
821     if ( eof ) {
822         return( AFPERR_EOF );
823     }
824     return AFP_OK;
825 }
826
827 /* -----------------------------
828  * with ddp, afp_read can return fewer bytes than in reqcount 
829  * so return EOF only if read actually past end of file not
830  * if offset +reqcount > size of file
831  * e.g.:
832  * getfork size ==> 10430
833  * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
834  * read fork offset 4264 size 6128 ==> 4264 (without EOF)
835  * read fork offset 9248 size 1508 ==> 1182 (EOF)
836  * 10752 is a bug in Mac 7.5.x finder 
837  *
838  * with dsi, should we check that reqcount < server quantum? 
839 */
840 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
841 {
842     struct ofork        *ofork;
843     off_t               offset, saveoff, reqcount, savereqcount;
844     ssize_t             cc, err;
845     int                 eid, xlate = 0;
846     u_int16_t           ofrefnum;
847     u_char              nlmask, nlchar;
848     
849     ibuf += 2;
850     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
851     ibuf += sizeof( u_short );
852
853     if (NULL == ( ofork = of_find( ofrefnum )) ) {
854         LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
855         err = AFPERR_PARAM;
856         goto afp_read_err;
857     }
858
859     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
860         err = AFPERR_ACCESS;
861         goto afp_read_err;
862     }
863     offset   = get_off_t(&ibuf, is64);
864     reqcount = get_off_t(&ibuf, is64);
865
866     if (is64) {
867         nlmask = nlchar = 0;
868     }
869     else {
870         nlmask = *ibuf++;
871         nlchar = *ibuf++;
872     }
873     /* if we wanted to be picky, we could add in the following
874      * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
875      */
876     if (reqcount < 0 || offset < 0) {
877         err = AFPERR_PARAM;
878         goto afp_read_err;
879     }
880
881     if ( ofork->of_flags & AFPFORK_DATA) {
882         eid = ADEID_DFORK;
883         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
884     } else if (ofork->of_flags & AFPFORK_RSRC) {
885         eid = ADEID_RFORK;
886     } else { /* fork wasn't opened. this should never really happen. */
887         err = AFPERR_ACCESS;
888         goto afp_read_err;
889     }
890
891     /* zero request count */
892     err = AFP_OK;
893     if (!reqcount) {
894         goto afp_read_err;
895     }
896
897     savereqcount = reqcount;
898     saveoff = offset;
899     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
900         err = AFPERR_LOCK;
901         goto afp_read_err;
902     }
903
904 #define min(a,b)        ((a)<(b)?(a):(b))
905     *rbuflen = min( reqcount, *rbuflen );
906     err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen, xlate);
907     if (err < 0)
908         goto afp_read_done;
909
910     /* dsi can stream requests. we can only do this if we're not checking
911      * for an end-of-line character. oh well. */
912     if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
913         DSI    *dsi = obj->handle;
914         off_t  size;
915
916         /* reqcount isn't always truthful. we need to deal with that. */
917         size = ad_size(ofork->of_ad, eid);
918
919         /* subtract off the offset */
920         size -= offset;
921         if (reqcount > size) {
922            reqcount = size;
923            err = AFPERR_EOF;
924         }
925
926         offset += *rbuflen;
927
928         /* dsi_readinit() returns size of next read buffer. by this point,
929          * we know that we're sending some data. if we fail, something
930          * horrible happened. */
931         if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
932             goto afp_read_exit;
933         *rbuflen = cc;
934         /* due to the nature of afp packets, we have to exit if we get
935            an error. we can't do this with translation on. */
936 #ifdef WITH_SENDFILE 
937         if (!(xlate || Debug(obj) )) {
938             int fd;
939                         
940             fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
941             if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) {
942                 if (errno == EINVAL || errno == ENOSYS)
943                     goto afp_read_loop;
944                 else {
945                     LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
946                     goto afp_read_exit;
947                 }
948             }
949
950             dsi_readdone(dsi);
951             goto afp_read_done;
952         }
953
954 afp_read_loop:
955 #endif 
956
957         /* fill up our buffer. */
958         while (*rbuflen > 0) {
959             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen, xlate);
960             if (cc < 0)
961                 goto afp_read_exit;
962
963             offset += *rbuflen;
964 #ifdef DEBUG1
965             if (obj->options.flags & OPTION_DEBUG) {
966                 printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
967                 bprint(rbuf, *rbuflen);
968             }
969 #endif
970             /* dsi_read() also returns buffer size of next allocation */
971             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
972             if (cc < 0)
973                 goto afp_read_exit;
974             *rbuflen = cc;
975         }
976         dsi_readdone(dsi);
977         goto afp_read_done;
978
979 afp_read_exit:
980         LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
981         dsi_readdone(dsi);
982         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
983         obj->exit(EXITERR_CLNT);
984     }
985
986 afp_read_done:
987     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
988     return err;
989
990 afp_read_err:
991     *rbuflen = 0;
992     return err;
993 }
994
995 /* ---------------------- */
996 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
997 {
998     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
999 }
1000
1001 /* ---------------------- */
1002 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1003 {
1004     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1005 }
1006
1007 /* ---------------------- */
1008 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1009 {
1010     struct vol *vol;
1011     u_int16_t vid;
1012
1013     *rbuflen = 0;
1014     ibuf += 2;
1015
1016     memcpy(&vid, ibuf, sizeof(vid));
1017     if (NULL == ( vol = getvolbyvid( vid )) ) {
1018         return( AFPERR_PARAM );
1019     }
1020
1021     of_flush(vol);
1022     return( AFP_OK );
1023 }
1024
1025 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1026 {
1027     struct ofork        *ofork;
1028     u_int16_t           ofrefnum;
1029
1030     *rbuflen = 0;
1031     ibuf += 2;
1032     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1033
1034     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1035         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
1036         return( AFPERR_PARAM );
1037     }
1038
1039     if ( flushfork( ofork ) < 0 ) {
1040         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
1041     }
1042
1043     return( AFP_OK );
1044 }
1045
1046 /*
1047   FIXME
1048   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
1049   fsync(2) on OSX is implemented differently than on other platforms.
1050   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
1051  */
1052 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1053 {
1054     struct ofork        *ofork;
1055     u_int16_t           ofrefnum;
1056
1057     *rbuflen = 0;
1058     ibuf += 2;
1059
1060     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
1061     ibuf += sizeof( ofrefnum );
1062
1063     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1064         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
1065         return( AFPERR_PARAM );
1066     }
1067
1068     if ( flushfork( ofork ) < 0 ) {
1069         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1070         return AFPERR_MISC;
1071     }
1072
1073     return( AFP_OK );
1074 }
1075
1076 /* this is very similar to closefork */
1077 int flushfork(struct ofork *ofork)
1078 {
1079     struct timeval tv;
1080
1081     int err = 0, doflush = 0;
1082
1083     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1084             fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1085         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1086             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1087         err = -1;
1088     }
1089
1090     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1091          (ofork->of_flags & AFPFORK_RSRC)) {
1092
1093         /* read in the rfork length */
1094         ad_refresh(ofork->of_ad);
1095
1096         /* set the date if we're dirty */
1097         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1098             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1099             ofork->of_flags &= ~AFPFORK_DIRTY;
1100             doflush++;
1101         }
1102
1103         /* flush the header */
1104         if (doflush && ad_flush(ofork->of_ad) < 0)
1105                 err = -1;
1106
1107         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1108             err = -1;
1109
1110         if (err < 0)
1111             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1112                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1113     }
1114
1115     return( err );
1116 }
1117
1118 int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1119 {
1120     struct ofork        *ofork;
1121     u_int16_t           ofrefnum;
1122
1123     *rbuflen = 0;
1124     ibuf += 2;
1125     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1126
1127     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1128         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1129         return( AFPERR_PARAM );
1130     }
1131     if ( of_closefork( ofork ) < 0 ) {
1132         LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1133         return( AFPERR_PARAM );
1134     }
1135
1136     return( AFP_OK );
1137 }
1138
1139
1140 static ssize_t write_file(struct ofork *ofork, int eid,
1141                                      off_t offset, char *rbuf,
1142                                      size_t rbuflen, const int xlate)
1143 {
1144     char *p, *q;
1145     ssize_t cc;
1146
1147     /*
1148      * If this file is of type TEXT, swap \015 to \012.
1149      */
1150     if (xlate) {
1151         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1152             if ( *p == '\015' ) {
1153                 *p = '\012';
1154             } else if ( *p == '\012' ) {
1155                 *p = '\015';
1156             }
1157         }
1158     }
1159
1160     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1161                         rbuf, rbuflen)) < 0 ) {
1162         switch ( errno ) {
1163         case EDQUOT :
1164         case EFBIG :
1165         case ENOSPC :
1166             return( AFPERR_DFULL );
1167         case EACCES:
1168             return AFPERR_ACCESS;
1169         default :
1170             LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1171             return( AFPERR_PARAM );
1172         }
1173     }
1174
1175     return cc;
1176 }
1177
1178
1179 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1180  * the client may have sent us a bunch of data that's not reflected 
1181  * in reqcount et al. */
1182 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1183 {
1184     struct ofork        *ofork;
1185     off_t               offset, saveoff, reqcount;
1186     int                 endflag, eid, xlate = 0, err = AFP_OK;
1187     u_int16_t           ofrefnum;
1188     ssize_t             cc;
1189
1190     /* figure out parameters */
1191     ibuf++;
1192     endflag = ENDBIT(*ibuf);
1193     ibuf++;
1194     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1195     ibuf += sizeof( ofrefnum );
1196
1197     offset   = get_off_t(&ibuf, is64);
1198     reqcount = get_off_t(&ibuf, is64);
1199
1200     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1201         LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1202         err = AFPERR_PARAM;
1203         goto afp_write_err;
1204     }
1205
1206     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1207         err = AFPERR_ACCESS;
1208         goto afp_write_err;
1209     }
1210
1211 #ifdef AFS
1212     writtenfork = ofork;
1213 #endif /* AFS */
1214
1215     if ( ofork->of_flags & AFPFORK_DATA) {
1216         eid = ADEID_DFORK;
1217         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1218     } else if (ofork->of_flags & AFPFORK_RSRC) {
1219         eid = ADEID_RFORK;
1220     } else {
1221         err = AFPERR_ACCESS; /* should never happen */
1222         goto afp_write_err;
1223     }
1224
1225     if (endflag)
1226         offset += ad_size(ofork->of_ad, eid);
1227
1228     /* handle bogus parameters */
1229     if (reqcount < 0 || offset < 0) {
1230         err = AFPERR_PARAM;
1231         goto afp_write_err;
1232     }
1233
1234     /* offset can overflow on 64-bit capable filesystems.
1235      * report disk full if that's going to happen. */
1236      if (sum_neg(is64, offset, reqcount)) {
1237         err = AFPERR_DFULL;
1238         goto afp_write_err;
1239     }
1240
1241     if (!reqcount) { /* handle request counts of 0 */
1242         err = AFP_OK;
1243         *rbuflen = set_off_t (offset, rbuf, is64);
1244         goto afp_write_err;
1245     }
1246
1247     saveoff = offset;
1248     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1249                    reqcount, ofork->of_refnum) < 0) {
1250         err = AFPERR_LOCK;
1251         goto afp_write_err;
1252     }
1253
1254     /* this is yucky, but dsi can stream i/o and asp can't */
1255     switch (obj->proto) {
1256 #ifndef NO_DDP
1257     case AFPPROTO_ASP:
1258         if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1259             *rbuflen = 0;
1260             LOG(log_error, logtype_afpd, "afp_write: asp_wrtcont: %s", strerror(errno) );
1261             return( AFPERR_PARAM );
1262         }
1263
1264 #ifdef DEBUG1
1265         if (obj->options.flags & OPTION_DEBUG) {
1266             printf("(write) len: %d\n", *rbuflen);
1267             bprint(rbuf, *rbuflen);
1268         }
1269 #endif
1270         if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1271                              xlate)) < 0) {
1272             *rbuflen = 0;
1273             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1274             return cc;
1275         }
1276         offset += cc;
1277         break;
1278 #endif /* no afp/asp */
1279
1280     case AFPPROTO_DSI:
1281         {
1282             DSI *dsi = obj->handle;
1283
1284             /* find out what we have already and write it out. */
1285             cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1286             if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1287                 dsi_writeflush(dsi);
1288                 *rbuflen = 0;
1289                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1290                 return cc;
1291             }
1292             offset += cc;
1293
1294 #if 0 /*def HAVE_SENDFILE_WRITE*/
1295             if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1296                 if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1297                                        offset, dsi->datasize)) < 0) {
1298                     switch (errno) {
1299                     case EDQUOT :
1300                     case EFBIG :
1301                     case ENOSPC :
1302                         cc = AFPERR_DFULL;
1303                         break;
1304                     default :
1305                         LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1306                         goto afp_write_loop;
1307                     }
1308                     dsi_writeflush(dsi);
1309                     *rbuflen = 0;
1310                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1311                                reqcount,  ofork->of_refnum);
1312                     return cc;
1313                 }
1314
1315                 offset += cc;
1316                 goto afp_write_done;
1317             }
1318 #endif /* 0, was HAVE_SENDFILE_WRITE */
1319
1320             /* loop until everything gets written. currently
1321                     * dsi_write handles the end case by itself. */
1322             while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1323                 if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1324                     dsi_writeflush(dsi);
1325                     *rbuflen = 0;
1326                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1327                                reqcount,  ofork->of_refnum);
1328                     return cc;
1329                 }
1330                 offset += cc;
1331             }
1332         }
1333         break;
1334     }
1335
1336     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1337     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1338         ofork->of_flags |= AFPFORK_DIRTY;
1339
1340     /* we have modified any fork, remember until close_fork */
1341     ofork->of_flags |= AFPFORK_MODIFIED;
1342
1343     *rbuflen = set_off_t (offset, rbuf, is64);
1344     return( AFP_OK );
1345
1346 afp_write_err:
1347     if (obj->proto == AFPPROTO_DSI) {
1348         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1349         dsi_writeflush(obj->handle);
1350     }
1351     if (err != AFP_OK) {
1352         *rbuflen = 0;
1353     }
1354     return err;
1355 }
1356
1357 /* ---------------------------- */
1358 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1359 {
1360     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1361 }
1362
1363 /* ---------------------------- 
1364  * FIXME need to deal with SIGXFSZ signal
1365 */
1366 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1367 {
1368     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1369 }
1370
1371 /* ---------------------------- */
1372 int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1373 {
1374     struct ofork        *ofork;
1375     int             ret;
1376     u_int16_t           ofrefnum, bitmap;
1377     size_t          buflen;
1378     ibuf += 2;
1379     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1380     ibuf += sizeof( ofrefnum );
1381     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1382     bitmap = ntohs( bitmap );
1383     ibuf += sizeof( bitmap );
1384
1385     *rbuflen = 0;
1386     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1387         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1388         return( AFPERR_PARAM );
1389     }
1390
1391     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) { /* META */
1392         if ( ad_refresh( ofork->of_ad ) < 0 ) {
1393             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1394             return( AFPERR_PARAM );
1395         }
1396     }
1397
1398     if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
1399                                rbuf + sizeof( u_short ), &buflen ))) {
1400         return( ret );
1401     }
1402
1403     *rbuflen = buflen + sizeof( u_short );
1404     bitmap = htons( bitmap );
1405     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1406     return( AFP_OK );
1407 }
1408