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