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