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