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