]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
desktop.c:
[netatalk.git] / etc / afpd / fork.c
1 /*
2  * $Id: fork.c,v 1.49 2003-02-16 12:35:04 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     path.u_name = mtoupath(vol, ofork->of_name);
90     path.m_name = ofork->of_name;
91     st = &path.st;
92     if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) | 
93                     (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) | 
94                     (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) {
95         if ( ad_dfileno( ofork->of_ad ) == -1 ) {
96             if (movecwd(vol, dir) < 0)
97                 return( AFPERR_NOOBJ );
98             if ( stat( path.u_name, st ) < 0 )
99                 return( AFPERR_NOOBJ );
100         } else {
101             if ( fstat( ad_dfileno( ofork->of_ad ), st ) < 0 ) {
102                 return( AFPERR_BITMAP );
103             }
104         }
105     }
106     return getmetadata(vol, bitmap, &path, dir, buf, buflen, adp, attrbits );    
107 }
108
109 /* ---------------------------- */
110 static off_t get_off_t(ibuf, is64)
111 char    **ibuf;
112 int     is64;
113 {
114     u_int32_t             temp;
115     off_t                 ret;
116
117     ret = 0;
118     memcpy(&temp, *ibuf, sizeof( temp ));
119     ret = ntohl(temp); /* ntohl is unsigned */
120     *ibuf += sizeof(temp);
121
122     if (is64) {
123         memcpy(&temp, *ibuf, sizeof( temp ));
124         *ibuf += sizeof(temp);
125         ret = ntohl(temp)| (ret << 32);
126     }
127     else {
128         ret = (int)ret; /* sign extend */
129     }
130     return ret;
131 }
132
133 /* ---------------------- */
134 static int set_off_t(offset, rbuf, is64)
135 off_t   offset;
136 char    *rbuf;
137 int     is64;
138 {
139     u_int32_t  temp;
140     int        ret;
141
142     ret = 0;
143     if (is64) {
144         temp = htonl(offset >> 32);
145         memcpy(rbuf, &temp, sizeof( temp ));
146         rbuf += sizeof(temp);
147         ret = sizeof( temp );
148         offset &= 0xffffffff;
149     }
150     temp = htonl(offset);
151     memcpy(rbuf, &temp, sizeof( temp ));
152     ret += sizeof( temp );
153
154     return ret;
155 }
156
157 /* ------------------------ 
158 */
159 static int is_neg(int is64, off_t val)
160 {
161     if (val < 0 || (sizeof(off_t) == 8 && !is64 && (val & 0x80000000U)))
162         return 1;
163     return 0;
164 }
165
166 static __inline__ int sum_neg(int is64, off_t offset, off_t reqcount) 
167 {
168     if (is_neg(is64, offset +reqcount) ) 
169         return 1;
170     return 0;
171 }
172
173 /* -------------------------
174 */
175 static int setforkmode(struct adouble *adp, int eid, int ofrefnum, int what)
176 {
177     return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, what, 1, ofrefnum);
178 }
179
180 /* -------------------------
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
195     if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) {
196         return setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_NONE);
197     }
198
199     if ((access & (OPENACC_RD | OPENACC_DRD))) {
200         if ((readset = getforkmode(adp, eid, AD_FILELOCK_OPEN_RD)) <0)
201             return readset;
202         if ((denyreadset = getforkmode(adp, eid, AD_FILELOCK_DENY_RD)) <0)
203             return denyreadset;
204
205         if ((access & OPENACC_RD) && denyreadset) {
206             errno = EACCES;
207             return -1;
208         }
209         if ((access & OPENACC_DRD) && readset) {
210             errno = EACCES;
211             return -1;
212         }   
213         /* boolean logic is not enough, because getforkmode is not always telling the
214          * true 
215          */
216         if ((access & OPENACC_RD)) {
217             ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_RD);
218             if (ret)
219                 return ret;
220         }
221         if ((access & OPENACC_DRD)) {
222             ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_DENY_RD);
223             if (ret)
224                 return ret;
225         }
226     }
227     /* ------------same for writing -------------- */
228     if ((access & (OPENACC_WR | OPENACC_DWR))) {
229         if ((writeset = getforkmode(adp, eid, AD_FILELOCK_OPEN_WR)) <0)
230             return writeset;
231         if ((denywriteset = getforkmode(adp, eid, AD_FILELOCK_DENY_WR)) <0)
232             return denywriteset;
233
234         if ((access & OPENACC_WR) && denywriteset) {
235             errno = EACCES;
236             return -1;
237         }
238         if ((access & OPENACC_DWR) && writeset) {
239             errno = EACCES;
240             return -1;
241         }   
242         if ((access & OPENACC_WR)) {
243             ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_WR);
244             if (ret)
245                 return ret;
246         }
247         if ((access & OPENACC_DWR)) {
248             ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_DENY_WR);
249             if (ret)
250                 return ret;
251         }
252     }
253
254     if ( access == (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD)) {
255         return ad_excl_lock(adp, eid);
256     }
257
258     return 0;
259 }
260
261 /* ----------------------- */
262 int afp_openfork(obj, ibuf, ibuflen, rbuf, rbuflen )
263 AFPObj      *obj;
264 char    *ibuf, *rbuf;
265 int             ibuflen, *rbuflen;
266 {
267     struct vol          *vol;
268     struct dir          *dir;
269     struct ofork        *ofork, *opened;
270     struct adouble      *adsame = NULL;
271     int                 buflen, ret, adflags, eid;
272     u_int32_t           did;
273     u_int16_t           vid, bitmap, access, ofrefnum, attrbits = 0;
274     char                fork, *path, *upath;
275     struct stat         *st;
276     u_int16_t           bshort;
277     struct path         *s_path;
278     
279     ibuf++;
280     fork = *ibuf++;
281     memcpy(&vid, ibuf, sizeof( vid ));
282     ibuf += sizeof(vid);
283
284     *rbuflen = 0;
285     if (NULL == ( vol = getvolbyvid( vid ))) {
286         return( AFPERR_PARAM );
287     }
288
289     memcpy(&did, ibuf, sizeof( did ));
290     ibuf += sizeof( int );
291
292     if (NULL == ( dir = dirlookup( vol, did ))) {
293         return afp_errno;    
294     }
295
296     memcpy(&bitmap, ibuf, sizeof( bitmap ));
297     bitmap = ntohs( bitmap );
298     ibuf += sizeof( bitmap );
299     memcpy(&access, ibuf, sizeof( access ));
300     access = ntohs( access );
301     ibuf += sizeof( access );
302
303     if ((vol->v_flags & AFPVOL_RO) && (access & OPENACC_WR)) {
304         return AFPERR_VLOCK;
305     }
306
307     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
308         return afp_errno;    
309     }
310
311     if (*s_path->m_name == '\0') {
312        /* it's a dir ! */
313        return  AFPERR_BADTYPE;
314     }
315
316     /* stat() data fork st is set because it's not a dir */
317     switch ( s_path->st_errno ) {
318     case 0:
319         break;
320     case ENOENT:
321         return AFPERR_NOOBJ;
322     case EACCES:
323         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
324     default:
325         LOG(log_error, logtype_afpd, "afp_openfork: ad_open: %s", strerror(errno) );
326         return AFPERR_PARAM;
327     }
328     /* FIXME should we check it first ? */
329     upath = s_path->u_name;
330     if (check_access(upath, access ) < 0) {
331         return AFPERR_ACCESS;
332     }
333
334     st   = &s_path->st;
335     /* XXX: this probably isn't the best way to do this. the already
336        open bits should really be set if the fork is opened by any
337        program, not just this one. however, that's problematic to do
338        if we can't write lock files somewhere. opened is also passed to 
339        ad_open so that we can keep file locks together.
340        FIXME: add the fork we are opening? 
341     */
342     if ((opened = of_findname(s_path))) {
343         attrbits = ((opened->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
344         attrbits |= ((opened->of_ad->ad_hf.adf_refcount > opened->of_ad->ad_df.adf_refcount)? ATTRBIT_ROPEN : 0);
345                    
346         adsame = opened->of_ad;
347     }
348
349     if ( fork == OPENFORK_DATA ) {
350         eid = ADEID_DFORK;
351         adflags = ADFLAGS_DF|ADFLAGS_HF;
352     } else {
353         eid = ADEID_RFORK;
354         adflags = ADFLAGS_HF;
355     }
356
357     path = s_path->m_name;
358     if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid,
359                            adsame, st)) == NULL ) {
360         return( AFPERR_NFILE );
361     }
362
363     ret = AFPERR_NOOBJ;
364     if (access & OPENACC_WR) {
365         /* try opening in read-write mode */
366         if (ad_open(upath, adflags, O_RDWR, 0, ofork->of_ad) < 0) {
367             switch ( errno ) {
368             case EROFS:
369                 ret = AFPERR_VLOCK;
370             case EACCES:
371                 goto openfork_err;
372                 break;
373             case ENOENT:
374                 if (fork == OPENFORK_DATA) {
375                     /* try to open only the data fork */
376                     if (ad_open(upath, ADFLAGS_DF, O_RDWR, 0, ofork->of_ad) < 0) {
377                         goto openfork_err;
378                     }
379                     adflags = ADFLAGS_DF;
380                 }
381                 else {
382                     /* here's the deal. we only try to create the resource
383                     * fork if the user wants to open it for write acess. */
384                     if (ad_open(upath, adflags, O_RDWR | O_CREAT, 0666, ofork->of_ad) < 0)
385                         goto openfork_err;
386                     ofork->of_flags |= AFPFORK_OPEN;
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         else {
406             /* the ressource fork is open too */
407             ofork->of_flags |= AFPFORK_OPEN;
408         }
409     } else {
410         /* try opening in read-only mode */
411         ret = AFPERR_NOOBJ;
412         if (ad_open(upath, adflags, O_RDONLY, 0, ofork->of_ad) < 0) {
413             switch ( errno ) {
414             case EROFS:
415                 ret = AFPERR_VLOCK;
416             case EACCES:
417                 goto openfork_err;
418                 break;
419             case ENOENT:
420                 /* see if client asked for a read only data fork */
421                 if (fork == OPENFORK_DATA) {
422                     if (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0, ofork->of_ad) < 0) {
423                         goto openfork_err;
424                     }
425                     adflags = ADFLAGS_DF;
426                 }
427                 /* else we don't set AFPFORK_OPEN because there's no ressource fork file 
428                  * We need to check AFPFORK_OPEN in afp_closefork(). eg fork open read-only
429                  * then create in open read-write.
430                 */
431                 break;
432             case EMFILE :
433             case ENFILE :
434                 ret = AFPERR_NFILE;
435                 goto openfork_err;
436                 break;
437             case EISDIR :
438                 ret = AFPERR_BADTYPE;
439                 goto openfork_err;
440                 break;
441             default:
442                 LOG(log_error, logtype_afpd, "afp_openfork: ad_open: %s", strerror(errno) );
443                 goto openfork_err;
444                 break;
445             }
446         }
447         else {
448             /* the ressource fork is open too */
449             ofork->of_flags |= AFPFORK_OPEN;
450         }
451     }
452
453     if ((adflags & ADFLAGS_HF) && (ad_get_HF_flags( ofork->of_ad) & 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(ret) );
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 (NULL == ( em = getextmap( of->of_name )) ||
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_DATA) && (ad_dfileno( ofork->of_ad ) != -1)) {
1159             adflags |= ADFLAGS_DF;
1160     }
1161     if ( (ofork->of_flags & AFPFORK_OPEN) && ad_hfileno( ofork->of_ad ) != -1 ) {
1162         adflags |= ADFLAGS_HF;
1163         /*
1164          * Only set the rfork's length if we're closing the rfork.
1165          */
1166         if ((ofork->of_flags & AFPFORK_RSRC)) {
1167             ad_refresh( ofork->of_ad );
1168             if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1169                 ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
1170                 doflush++;
1171             }
1172             if ( doflush ) {
1173                  ad_flush( ofork->of_ad, adflags );
1174             }
1175         }
1176     }
1177
1178     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
1179         LOG(log_error, logtype_afpd, "afp_closefork: ad_close: %s", strerror(errno) );
1180         return( AFPERR_PARAM );
1181     }
1182
1183     of_dealloc( ofork );
1184     return( AFP_OK );
1185 }
1186
1187
1188 static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
1189                                      off_t offset, char *rbuf,
1190                                      size_t rbuflen, const int xlate)
1191 {
1192     char *p, *q;
1193     ssize_t cc;
1194
1195     /*
1196      * If this file is of type TEXT, swap \015 to \012.
1197      */
1198     if (xlate) {
1199         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1200             if ( *p == '\015' ) {
1201                 *p = '\012';
1202             } else if ( *p == '\012' ) {
1203                 *p = '\015';
1204             }
1205         }
1206     }
1207
1208     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1209                         rbuf, rbuflen)) < 0 ) {
1210         switch ( errno ) {
1211         case EDQUOT :
1212         case EFBIG :
1213         case ENOSPC :
1214             return( AFPERR_DFULL );
1215         default :
1216             LOG(log_error, logtype_afpd, "afp_write: ad_write: %s", strerror(errno) );
1217             return( AFPERR_PARAM );
1218         }
1219     }
1220
1221     return cc;
1222 }
1223
1224
1225 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1226  * the client may have sent us a bunch of data that's not reflected 
1227  * in reqcount et al. */
1228 static int write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, is64)
1229 AFPObj              *obj;
1230 char                *ibuf, *rbuf;
1231 int                 ibuflen, *rbuflen;
1232 int                 is64;
1233 {
1234     struct ofork        *ofork;
1235     off_t               offset, saveoff, reqcount;
1236     int                 endflag, eid, xlate = 0, err = AFP_OK;
1237     u_int16_t           ofrefnum;
1238     ssize_t             cc;
1239
1240     /* figure out parameters */
1241     ibuf++;
1242     endflag = ENDBIT(*ibuf);
1243     ibuf++;
1244     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1245     ibuf += sizeof( ofrefnum );
1246
1247     offset   = get_off_t(&ibuf, is64);
1248     reqcount = get_off_t(&ibuf, is64);
1249
1250     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1251         LOG(log_error, logtype_afpd, "afp_write: of_find");
1252         err = AFPERR_PARAM;
1253         goto afp_write_err;
1254     }
1255
1256     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1257         err = AFPERR_ACCESS;
1258         goto afp_write_err;
1259     }
1260
1261 #ifdef AFS
1262     writtenfork = ofork;
1263 #endif /* AFS */
1264
1265     if ( ofork->of_flags & AFPFORK_DATA) {
1266         eid = ADEID_DFORK;
1267         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1268     } else if (ofork->of_flags & AFPFORK_RSRC) {
1269         eid = ADEID_RFORK;
1270     } else {
1271         err = AFPERR_ACCESS; /* should never happen */
1272         goto afp_write_err;
1273     }
1274
1275     if (endflag)
1276         offset += ad_size(ofork->of_ad, eid);
1277
1278     /* handle bogus parameters */
1279     if (reqcount < 0 || offset < 0) {
1280         err = AFPERR_PARAM;
1281         goto afp_write_err;
1282     }
1283
1284     /* offset can overflow on 64-bit capable filesystems.
1285      * report disk full if that's going to happen. */
1286      if (sum_neg(is64, offset, reqcount)) {
1287         err = AFPERR_DFULL;
1288         goto afp_write_err;
1289     }
1290
1291     if (!reqcount) { /* handle request counts of 0 */
1292         err = AFP_OK;
1293         *rbuflen = set_off_t (offset, rbuf, is64);
1294         goto afp_write_err;
1295     }
1296
1297     saveoff = offset;
1298     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1299                    reqcount, ofork->of_refnum) < 0) {
1300         err = AFPERR_LOCK;
1301         goto afp_write_err;
1302     }
1303
1304     /* this is yucky, but dsi can stream i/o and asp can't */
1305     switch (obj->proto) {
1306 #ifndef NO_DDP
1307     case AFPPROTO_ASP:
1308         if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1309             *rbuflen = 0;
1310             LOG(log_error, logtype_afpd, "afp_write: asp_wrtcont: %s", strerror(errno) );
1311             return( AFPERR_PARAM );
1312         }
1313
1314         if (obj->options.flags & OPTION_DEBUG) {
1315             printf("(write) len: %d\n", *rbuflen);
1316             bprint(rbuf, *rbuflen);
1317         }
1318
1319         if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1320                              xlate)) < 0) {
1321             *rbuflen = 0;
1322             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1323             return cc;
1324         }
1325         offset += cc;
1326         break;
1327 #endif /* no afp/asp */
1328
1329     case AFPPROTO_DSI:
1330         {
1331             DSI *dsi = obj->handle;
1332
1333             /* find out what we have already and write it out. */
1334             cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1335             if (!cc ||
1336                     (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1337                 dsi_writeflush(dsi);
1338                 *rbuflen = 0;
1339                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1340                 return cc;
1341             }
1342             offset += cc;
1343
1344 #if 0 /*def HAVE_SENDFILE_WRITE*/
1345             if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1346                 if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1347                                        offset, dsi->datasize)) < 0) {
1348                     switch (errno) {
1349                     case EDQUOT :
1350                     case EFBIG :
1351                     case ENOSPC :
1352                         cc = AFPERR_DFULL;
1353                         break;
1354                     default :
1355                         LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1356                         goto afp_write_loop;
1357                     }
1358                     dsi_writeflush(dsi);
1359                     *rbuflen = 0;
1360                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1361                                reqcount,  ofork->of_refnum);
1362                     return cc;
1363                 }
1364
1365                 offset += cc;
1366                 goto afp_write_done;
1367             }
1368 #endif /* 0, was HAVE_SENDFILE_WRITE */
1369
1370             /* loop until everything gets written. currently
1371                     * dsi_write handles the end case by itself. */
1372             while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1373                 if ( obj->options.flags & OPTION_DEBUG ) {
1374                     printf("(write) command cont'd: %d\n", cc);
1375                     bprint(rbuf, cc);
1376                 }
1377
1378                 if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1379                     dsi_writeflush(dsi);
1380                     *rbuflen = 0;
1381                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1382                                reqcount,  ofork->of_refnum);
1383                     return cc;
1384                 }
1385                 offset += cc;
1386             }
1387         }
1388         break;
1389     }
1390
1391     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1392     if ( ad_hfileno( ofork->of_ad ) != -1 )
1393         ofork->of_flags |= AFPFORK_DIRTY;
1394
1395     *rbuflen = set_off_t (offset, rbuf, is64);
1396     return( AFP_OK );
1397
1398 afp_write_err:
1399     if (obj->proto == AFPPROTO_DSI) {
1400         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1401         dsi_writeflush(obj->handle);
1402     }
1403
1404     *rbuflen = (err == AFP_OK) ? sizeof(offset) : 0;
1405     return err;
1406 }
1407
1408 /* ---------------------------- */
1409 int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
1410 AFPObj              *obj;
1411 char                *ibuf, *rbuf;
1412 int                 ibuflen, *rbuflen;
1413 {
1414     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1415 }
1416
1417 /* ---------------------------- 
1418  * FIXME need to deal with SIGXFSZ signal
1419 */
1420 int afp_write_ext(obj, ibuf, ibuflen, rbuf, rbuflen)
1421 AFPObj              *obj;
1422 char                *ibuf, *rbuf;
1423 int                 ibuflen, *rbuflen;
1424 {
1425     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1426 }
1427
1428 /* ---------------------------- */
1429 int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1430 AFPObj      *obj;
1431 char    *ibuf, *rbuf;
1432 int             ibuflen, *rbuflen;
1433 {
1434     struct ofork        *ofork;
1435     int                 buflen, ret;
1436     u_int16_t           ofrefnum, bitmap;
1437     u_int16_t           attrbits = 0;
1438
1439     ibuf += 2;
1440     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1441     ibuf += sizeof( ofrefnum );
1442     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1443     bitmap = ntohs( bitmap );
1444     ibuf += sizeof( bitmap );
1445
1446     *rbuflen = 0;
1447     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1448         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find");
1449         return( AFPERR_PARAM );
1450     }
1451     attrbits = ((ofork->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1452     attrbits |= ((ofork->of_ad->ad_hf.adf_refcount > ofork->of_ad->ad_df.adf_refcount) ? ATTRBIT_ROPEN : 0);
1453
1454     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1455         if ( ad_refresh( ofork->of_ad ) < 0 ) {
1456             LOG(log_error, logtype_afpd, "getforkparams: ad_refresh: %s", strerror(errno) );
1457             return( AFPERR_PARAM );
1458         }
1459     }
1460
1461     if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
1462                                rbuf + sizeof( u_short ), &buflen, attrbits ))) {
1463         return( ret );
1464     }
1465
1466     *rbuflen = buflen + sizeof( u_short );
1467     bitmap = htons( bitmap );
1468     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1469     return( AFP_OK );
1470 }
1471