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