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