]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
c066e67f0bb4c92aab5fd501b6eb940c5e27f495
[netatalk.git] / etc / afpd / fork.c
1 /*
2  * $Id: fork.c,v 1.51.2.2.2.7 2004-03-11 02:02:01 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_getentryoff(ofork->of_ad, ADEID_NAME)) {
463             ad_setentrylen( ofork->of_ad, ADEID_NAME, strlen( path ));
464             memcpy(ad_entry( ofork->of_ad, ADEID_NAME ), path,
465                ad_getentrylen( ofork->of_ad, ADEID_NAME ));
466             ad_flush( ofork->of_ad, adflags );
467         }
468     }
469
470     if (( ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof( u_int16_t ),
471                               &buflen, attrbits )) != AFP_OK ) {
472         ad_close( ofork->of_ad, adflags );
473         goto openfork_err;
474     }
475
476     *rbuflen = buflen + 2 * sizeof( u_int16_t );
477     bitmap = htons( bitmap );
478     memcpy(rbuf, &bitmap, sizeof( u_int16_t ));
479     rbuf += sizeof( u_int16_t );
480
481     /* check  WriteInhibit bit if we have a ressource fork
482      * the test is done here, after some Mac trafic capture 
483      */
484     if (ad_hfileno(ofork->of_ad) != -1) {
485         ad_getattr(ofork->of_ad, &bshort);
486         if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
487             ad_close( ofork->of_ad, adflags );
488             of_dealloc( ofork );
489             ofrefnum = 0;
490             memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
491             return(AFPERR_OLOCK);
492         }
493     }
494
495     /*
496      * synchronization locks:
497      */
498
499     /* don't try to lock non-existent rforks. */
500     if ((eid == ADEID_DFORK) || (ad_hfileno(ofork->of_ad) != -1)) {
501
502         ret = fork_setmode(ofork->of_ad, eid, access, ofrefnum);
503         /* can we access the fork? */
504         if (ret < 0) {
505             ret = errno;
506             ad_close( ofork->of_ad, adflags );
507             of_dealloc( ofork );
508             switch (ret) {
509             case EAGAIN: /* return data anyway */
510             case EACCES:
511             case EINVAL:
512                 ofrefnum = 0;
513                 memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
514                 return( AFPERR_DENYCONF );
515                 break;
516             default:
517                 *rbuflen = 0;
518                 LOG(log_error, logtype_afpd, "afp_openfork: ad_lock: %s", strerror(ret) );
519                 return( AFPERR_PARAM );
520             }
521         }
522         if ((access & OPENACC_WR))
523             ofork->of_flags |= AFPFORK_ACCWR;
524     }
525     /* the file may be open read only without ressource fork */
526     if ((access & OPENACC_RD))
527         ofork->of_flags |= AFPFORK_ACCRD;
528
529     memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
530     return( AFP_OK );
531
532 openfork_err:
533     of_dealloc( ofork );
534     if (errno == EACCES)
535         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
536     return ret;
537 }
538
539 int afp_setforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
540 AFPObj      *obj;
541 char    *ibuf, *rbuf;
542 int             ibuflen, *rbuflen;
543 {
544     struct ofork        *ofork;
545     off_t               size;
546     u_int16_t           ofrefnum, bitmap;
547     int                 err;
548     int                 is64;
549     int                 eid;
550     off_t               st_size;
551     
552     ibuf += 2;
553
554     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
555     ibuf += sizeof( ofrefnum );
556
557     memcpy(&bitmap, ibuf, sizeof(bitmap));
558     bitmap = ntohs(bitmap);
559     ibuf += sizeof( bitmap );
560
561     *rbuflen = 0;
562     if (NULL == ( ofork = of_find( ofrefnum )) ) {
563         LOG(log_error, logtype_afpd, "afp_setforkparams: of_find could not locate open fork refnum: %u", ofrefnum );
564         return( AFPERR_PARAM );
565     }
566
567     if (ofork->of_vol->v_flags & AFPVOL_RO)
568         return AFPERR_VLOCK;
569
570     if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
571         return AFPERR_ACCESS;
572
573     if ( ofork->of_flags & AFPFORK_DATA) {
574         eid = ADEID_DFORK;
575     } else if (ofork->of_flags & AFPFORK_RSRC) {
576         eid = ADEID_RFORK;
577     } else
578         return AFPERR_PARAM;
579
580     if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) )) 
581                   && eid == ADEID_RFORK 
582          ) ||
583          ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) )) 
584                   && eid == ADEID_DFORK)) {
585         return AFPERR_BITMAP;
586     }
587     
588     is64 = 0;
589     if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
590         if (afp_version >= 30) {
591             is64 = 4;
592         }
593         else 
594            return AFPERR_BITMAP;
595     }
596
597     if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
598         return AFPERR_PARAM ;
599     
600     size = get_off_t(&ibuf, is64);
601
602     if (size < 0)
603         return AFPERR_PARAM; /* Some MacOS don't return an error they just don't change the size! */
604
605
606     if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
607         st_size = ad_size(ofork->of_ad, eid);
608         err = -2;
609         if (st_size > size && 
610               ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) 
611             goto afp_setfork_err;
612
613         err = ad_dtruncate( ofork->of_ad, size );
614         if (st_size > size)
615             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
616         if (err < 0)
617             goto afp_setfork_err;
618     } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
619         ad_refresh( ofork->of_ad );
620
621         st_size = ad_size(ofork->of_ad, eid);
622         err = -2;
623         if (st_size > size && 
624                ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
625             goto afp_setfork_err;
626         }
627         err = ad_rtruncate(ofork->of_ad, size);
628         if (st_size > size)
629             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
630         if (err < 0)
631             goto afp_setfork_err;
632
633         if (ad_flush( ofork->of_ad, ADFLAGS_HF ) < 0) {
634             LOG(log_error, logtype_afpd, "afp_setforkparams: ad_flush: %s",strerror(errno) );
635             return AFPERR_PARAM;
636         }
637     } else
638         return AFPERR_BITMAP;
639
640 #ifdef AFS
641     if ( flushfork( ofork ) < 0 ) {
642         LOG(log_error, logtype_afpd, "afp_setforkparams: flushfork: %s", strerror(errno) );
643     }
644 #endif /* AFS */
645
646     return( AFP_OK );
647
648 afp_setfork_err:
649     if (err == -2)
650         return AFPERR_LOCK;
651     else {
652         switch (errno) {
653         case EROFS:
654             return AFPERR_VLOCK;
655         case EPERM:
656         case EACCES:
657             return AFPERR_ACCESS;
658         case EDQUOT:
659         case EFBIG:
660         case ENOSPC:
661             return AFPERR_DFULL;
662         default:
663             return AFPERR_PARAM;
664         }
665     }
666 }
667
668 /* for this to work correctly, we need to check for locks before each
669  * read and write. that's most easily handled by always doing an
670  * appropriate check before each ad_read/ad_write. other things
671  * that can change files like truncate are handled internally to those
672  * functions. 
673  */
674 #define ENDBIT(a)  ((a) & 0x80)
675 #define UNLOCKBIT(a) ((a) & 0x01)
676
677
678 /* ---------------------- */
679 static int byte_lock(obj, ibuf, ibuflen, rbuf, rbuflen, is64 )
680 AFPObj  *obj;
681 char    *ibuf, *rbuf;
682 int     ibuflen, *rbuflen;
683 int     is64;
684 {
685     struct ofork        *ofork;
686     off_t               offset, length;
687     int                 eid;
688     u_int16_t           ofrefnum;
689     u_int8_t            flags;
690     int                 lockop;
691     
692     *rbuflen = 0;
693
694     /* figure out parameters */
695     ibuf++;
696     flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
697     ibuf++;
698     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
699     ibuf += sizeof(ofrefnum);
700
701     if (NULL == ( ofork = of_find( ofrefnum )) ) {
702         LOG(log_error, logtype_afpd, "afp_bytelock: of_find");
703         return( AFPERR_PARAM );
704     }
705
706     if ( ofork->of_flags & AFPFORK_DATA) {
707         eid = ADEID_DFORK;
708     } else if (ofork->of_flags & AFPFORK_RSRC) {
709         eid = ADEID_RFORK;
710     } else
711         return AFPERR_PARAM;
712
713     offset = get_off_t(&ibuf, is64);
714     length = get_off_t(&ibuf, is64);
715
716     /* FIXME AD_FILELOCK test is surely wrong */
717     if (length == -1)
718         length = BYTELOCK_MAX;
719      else if (!length || is_neg(is64, length)) {
720         return AFPERR_PARAM;
721      } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_hfileno(ofork->of_ad))) {
722         return AFPERR_LOCK;
723     }
724
725     if (ENDBIT(flags)) {
726         offset += ad_size(ofork->of_ad, eid);
727         /* FIXME what do we do if file size > 2 GB and 
728            it's not byte_lock_ext?
729         */
730     }
731     if (offset < 0)    /* error if we have a negative offset */
732         return AFPERR_PARAM;
733
734     /* if the file is a read-only file, we use read locks instead of
735      * write locks. that way, we can prevent anyone from initiating
736      * a write lock. */
737     lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
738     if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
739                 ofork->of_refnum) < 0) {
740         switch (errno) {
741         case EACCES:
742         case EAGAIN:
743             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
744             break;
745         case ENOLCK:
746             return AFPERR_NLOCK;
747             break;
748         case EINVAL:
749             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
750             break;
751         case EBADF:
752         default:
753             return AFPERR_PARAM;
754             break;
755         }
756     }
757     *rbuflen = set_off_t (offset, rbuf, is64);
758     return( AFP_OK );
759 }
760
761 /* --------------------------- */
762 int afp_bytelock(obj, ibuf, ibuflen, rbuf, rbuflen )
763 AFPObj  *obj;
764 char    *ibuf, *rbuf;
765 int     ibuflen, *rbuflen;
766 {
767    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
768 }
769
770 /* --------------------------- */
771 int afp_bytelock_ext(obj, ibuf, ibuflen, rbuf, rbuflen )
772 AFPObj  *obj;
773 char    *ibuf, *rbuf;
774 int     ibuflen, *rbuflen;
775 {
776    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
777 }
778
779 #undef UNLOCKBIT
780
781 /* --------------------------- */
782 static __inline__ int crlf( of )
783 struct ofork    *of;
784 {
785     struct extmap       *em;
786
787     if ( ad_hfileno( of->of_ad ) == -1 ||
788             memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI ),
789                     8) == 0 ) {
790         if (NULL == ( em = getextmap( of->of_name )) ||
791                 memcmp( "TEXT", em->em_type, sizeof( em->em_type )) == 0 ) {
792             return( 1 );
793         } else {
794             return( 0 );
795         }
796     } else {
797         if ( memcmp( ufinderi,
798                      ad_entry( of->of_ad, ADEID_FINDERI ), 4 ) == 0 ) {
799             return( 1 );
800         } else {
801             return( 0 );
802         }
803     }
804 }
805
806
807 static __inline__ ssize_t read_file(struct ofork *ofork, int eid,
808                                     off_t offset, u_char nlmask,
809                                     u_char nlchar, char *rbuf,
810                                     int *rbuflen, const int xlate)
811 {
812     ssize_t cc;
813     int eof = 0;
814     char *p, *q;
815
816     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
817     if ( cc < 0 ) {
818         LOG(log_error, logtype_afpd, "afp_read: ad_read: %s", strerror(errno) );
819         *rbuflen = 0;
820         return( AFPERR_PARAM );
821     }
822     if ( cc < *rbuflen ) {
823         eof = 1;
824     }
825
826     /*
827      * Do Newline check.
828      */
829     if ( nlmask != 0 ) {
830         for ( p = rbuf, q = p + cc; p < q; ) {
831             if (( *p++ & nlmask ) == nlchar ) {
832                 break;
833             }
834         }
835         if ( p != q ) {
836             cc = p - rbuf;
837             eof = 0;
838         }
839     }
840
841     /*
842      * If this file is of type TEXT, then swap \012 to \015.
843      */
844     if (xlate) {
845         for ( p = rbuf, q = p + cc; p < q; p++ ) {
846             if ( *p == '\012' ) {
847                 *p = '\015';
848             } else if ( *p == '\015' ) {
849                 *p = '\012';
850             }
851
852         }
853     }
854
855     *rbuflen = cc;
856     if ( eof ) {
857         return( AFPERR_EOF );
858     }
859     return AFP_OK;
860 }
861
862 /* -----------------------------
863  * with ddp, afp_read can return fewer bytes than in reqcount 
864  * so return EOF only if read actually past end of file not
865  * if offset +reqcount > size of file
866  * e.g.:
867  * getfork size ==> 10430
868  * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
869  * read fork offset 4264 size 6128 ==> 4264 (without EOF)
870  * read fork offset 9248 size 1508 ==> 1182 (EOF)
871  * 10752 is a bug in Mac 7.5.x finder 
872  *
873  * with dsi, should we check that reqcount < server quantum? 
874 */
875 static int read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, is64)
876 AFPObj      *obj;
877 char    *ibuf, *rbuf;
878 int             ibuflen, *rbuflen;
879 int is64;
880 {
881     struct ofork        *ofork;
882     off_t               size;
883     off_t               offset, saveoff, reqcount, savereqcount;
884     int                 cc, err, eid, xlate = 0;
885     u_int16_t           ofrefnum;
886     u_char              nlmask, nlchar;
887     int                 non_blocking = 0;
888     
889     ibuf += 2;
890     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
891     ibuf += sizeof( u_short );
892
893     if (NULL == ( ofork = of_find( ofrefnum )) ) {
894         LOG(log_error, logtype_afpd, "afp_read: of_find");
895         err = AFPERR_PARAM;
896         goto afp_read_err;
897     }
898
899     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
900         err = AFPERR_ACCESS;
901         goto afp_read_err;
902     }
903     offset   = get_off_t(&ibuf, is64);
904     reqcount = get_off_t(&ibuf, is64);
905
906     if (is64) {
907         nlmask = nlchar = 0;
908     }
909     else {
910         nlmask = *ibuf++;
911         nlchar = *ibuf++;
912     }
913     /* if we wanted to be picky, we could add in the following
914      * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
915      */
916     if (reqcount < 0 || offset < 0) {
917         err = AFPERR_PARAM;
918         goto afp_read_err;
919     }
920
921     if ( ofork->of_flags & AFPFORK_DATA) {
922         eid = ADEID_DFORK;
923         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
924     } else if (ofork->of_flags & AFPFORK_RSRC) {
925         eid = ADEID_RFORK;
926     } else { /* fork wasn't opened. this should never really happen. */
927         err = AFPERR_ACCESS;
928         goto afp_read_err;
929     }
930
931     /* zero request count */
932     err = AFP_OK;
933     if (!reqcount) {
934         goto afp_read_err;
935     }
936
937     /* reqcount isn't always truthful. we need to deal with that. */
938     size = ad_size(ofork->of_ad, eid);
939
940     if (offset >= size) {
941         err = AFPERR_EOF;
942         goto afp_read_err;
943     }
944
945     savereqcount = reqcount;
946     saveoff = offset;
947     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
948         err = AFPERR_LOCK;
949         goto afp_read_err;
950     }
951
952 #define min(a,b)        ((a)<(b)?(a):(b))
953     *rbuflen = min( reqcount, *rbuflen );
954     err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen, xlate);
955     if (err < 0)
956         goto afp_read_done;
957
958     /* dsi can stream requests. we can only do this if we're not checking
959      * for an end-of-line character. oh well. */
960     if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
961         DSI *dsi = obj->handle;
962
963 #ifdef DEBUG1
964         if (obj->options.flags & OPTION_DEBUG) {
965             printf( "(read) reply: %d/%d, %d\n", *rbuflen,(int) reqcount, dsi->clientID);
966             bprint(rbuf, *rbuflen);
967         }
968 #endif        
969         /* subtract off the offset */
970         size -= offset;
971         if (reqcount > size) {
972            reqcount = size;
973            err = AFPERR_EOF;
974         }
975
976         offset += *rbuflen;
977
978         /* dsi_readinit() returns size of next read buffer. by this point,
979          * we know that we're sending some data. if we fail, something
980          * horrible happened. */
981         if ((*rbuflen = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
982             goto afp_read_exit;
983
984         /* due to the nature of afp packets, we have to exit if we get
985            an error. we can't do this with translation on. */
986 #if 0 /* idef WITH_SENDFILE */
987         /* FIXME with OS X deadlock partial workaround we can't use sendfile */
988         if (!(xlate || Debug(obj) )) {
989             if (ad_readfile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize) < 0) {
990                 if (errno == EINVAL || errno == ENOSYS)
991                     goto afp_read_loop;
992                 else {
993                     LOG(log_error, logtype_afpd, "afp_read: ad_readfile: %s", strerror(errno));
994                     goto afp_read_exit;
995                 }
996             }
997
998             dsi_readdone(dsi);
999             goto afp_read_done;
1000         }
1001
1002 afp_read_loop:
1003 #endif 
1004
1005         /* fill up our buffer. */
1006         if (*rbuflen) {
1007             /* set to non blocking mode */
1008             non_blocking = 1;
1009             dsi_block(dsi, 1);
1010         }
1011         /* fill up our buffer. */
1012         while (*rbuflen > 0) {
1013             cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen, xlate);
1014             if (cc < 0)
1015                 goto afp_read_exit;
1016
1017             offset += *rbuflen;
1018 #ifdef DEBUG1
1019             if (obj->options.flags & OPTION_DEBUG) {
1020                 printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
1021                 bprint(rbuf, *rbuflen);
1022             }
1023 #endif
1024             /* dsi_read() also returns buffer size of next allocation */
1025             cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
1026             if (cc < 0)
1027                 goto afp_read_exit;
1028             *rbuflen = cc;
1029         }
1030         dsi_readdone(dsi);
1031         goto afp_read_done;
1032
1033 afp_read_exit:
1034         LOG(log_error, logtype_afpd, "afp_read: %s", strerror(errno));
1035         dsi_readdone(dsi);
1036         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
1037         obj->exit(1);
1038     }
1039
1040 afp_read_done:
1041     if (non_blocking) {
1042         DSI *dsi = obj->handle;
1043         /* set back to blocking mode */
1044         dsi_block(dsi, 0);
1045     }
1046     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
1047     return err;
1048
1049 afp_read_err:
1050     *rbuflen = 0;
1051     return err;
1052 }
1053
1054 /* ---------------------- */
1055 int afp_read(obj, ibuf, ibuflen, rbuf, rbuflen)
1056 AFPObj  *obj;
1057 char    *ibuf, *rbuf;
1058 int     ibuflen, *rbuflen;
1059 {
1060     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1061 }
1062
1063 /* ---------------------- */
1064 int afp_read_ext(obj, ibuf, ibuflen, rbuf, rbuflen)
1065 AFPObj  *obj;
1066 char    *ibuf, *rbuf;
1067 int     ibuflen, *rbuflen;
1068 {
1069     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1070 }
1071
1072 /* ---------------------- */
1073 int afp_flush(obj, ibuf, ibuflen, rbuf, rbuflen )
1074 AFPObj      *obj;
1075 char    *ibuf, *rbuf;
1076 int             ibuflen, *rbuflen;
1077 {
1078     struct vol *vol;
1079     u_int16_t vid;
1080
1081     *rbuflen = 0;
1082     ibuf += 2;
1083
1084     memcpy(&vid, ibuf, sizeof(vid));
1085     if (NULL == ( vol = getvolbyvid( vid )) ) {
1086         return( AFPERR_PARAM );
1087     }
1088
1089     of_flush(vol);
1090     return( AFP_OK );
1091 }
1092
1093 int afp_flushfork(obj, ibuf, ibuflen, rbuf, rbuflen )
1094 AFPObj      *obj;
1095 char    *ibuf, *rbuf;
1096 int             ibuflen, *rbuflen;
1097 {
1098     struct ofork        *ofork;
1099     u_int16_t           ofrefnum;
1100
1101     *rbuflen = 0;
1102     ibuf += 2;
1103     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1104
1105     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1106         LOG(log_error, logtype_afpd, "afp_flushfork: of_find");
1107         return( AFPERR_PARAM );
1108     }
1109
1110     if ( flushfork( ofork ) < 0 ) {
1111         LOG(log_error, logtype_afpd, "afp_flushfork: %s", strerror(errno) );
1112     }
1113
1114     return( AFP_OK );
1115 }
1116
1117 /* this is very similar to closefork */
1118 int flushfork( ofork )
1119 struct ofork    *ofork;
1120 {
1121     struct timeval tv;
1122
1123     int err = 0, doflush = 0;
1124
1125     if ( ad_dfileno( ofork->of_ad ) != -1 &&
1126             fsync( ad_dfileno( ofork->of_ad )) < 0 ) {
1127         LOG(log_error, logtype_afpd, "flushfork: dfile(%d) %s",
1128             ad_dfileno(ofork->of_ad), strerror(errno) );
1129         err = -1;
1130     }
1131
1132     if ( ad_hfileno( ofork->of_ad ) != -1 && 
1133            (ofork->of_flags & AFPFORK_RSRC)) {
1134
1135         /* read in the rfork length */
1136         ad_refresh(ofork->of_ad);
1137
1138         /* set the date if we're dirty */
1139         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1140             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1141             ofork->of_flags &= ~AFPFORK_DIRTY;
1142             doflush++;
1143         }
1144
1145         /* flush the header */
1146         if (doflush && ad_flush(ofork->of_ad, ADFLAGS_HF) < 0)
1147                 err = -1;
1148
1149         if (fsync( ad_hfileno( ofork->of_ad )) < 0)
1150             err = -1;
1151
1152         if (err < 0)
1153             LOG(log_error, logtype_afpd, "flushfork: hfile(%d) %s",
1154                 ad_hfileno(ofork->of_ad), strerror(errno) );
1155     }
1156
1157     return( err );
1158 }
1159
1160 int afp_closefork(obj, ibuf, ibuflen, rbuf, rbuflen )
1161 AFPObj      *obj;
1162 char    *ibuf, *rbuf;
1163 int             ibuflen, *rbuflen;
1164 {
1165     struct ofork        *ofork;
1166     struct timeval      tv;
1167     int                 adflags, doflush = 0;
1168     u_int16_t           ofrefnum;
1169
1170     *rbuflen = 0;
1171     ibuf += 2;
1172     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1173
1174     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1175         LOG(log_error, logtype_afpd, "afp_closefork: of_find");
1176         return( AFPERR_PARAM );
1177     }
1178
1179     adflags = 0;
1180     if ((ofork->of_flags & AFPFORK_DATA) && (ad_dfileno( ofork->of_ad ) != -1)) {
1181             adflags |= ADFLAGS_DF;
1182     }
1183     if ( (ofork->of_flags & AFPFORK_OPEN) && ad_hfileno( ofork->of_ad ) != -1 ) {
1184         adflags |= ADFLAGS_HF;
1185         /*
1186          * Only set the rfork's length if we're closing the rfork.
1187          */
1188         if ((ofork->of_flags & AFPFORK_RSRC)) {
1189             ad_refresh( ofork->of_ad );
1190             if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1191                 ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
1192                 doflush++;
1193             }
1194             if ( doflush ) {
1195                  ad_flush( ofork->of_ad, adflags );
1196             }
1197         }
1198     }
1199
1200     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
1201         LOG(log_error, logtype_afpd, "afp_closefork: ad_close: %s", strerror(errno) );
1202         return( AFPERR_PARAM );
1203     }
1204
1205     of_dealloc( ofork );
1206     return( AFP_OK );
1207 }
1208
1209
1210 static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
1211                                      off_t offset, char *rbuf,
1212                                      size_t rbuflen, const int xlate)
1213 {
1214     char *p, *q;
1215     ssize_t cc;
1216
1217     /*
1218      * If this file is of type TEXT, swap \015 to \012.
1219      */
1220     if (xlate) {
1221         for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
1222             if ( *p == '\015' ) {
1223                 *p = '\012';
1224             } else if ( *p == '\012' ) {
1225                 *p = '\015';
1226             }
1227         }
1228     }
1229
1230     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1231                         rbuf, rbuflen)) < 0 ) {
1232         switch ( errno ) {
1233         case EDQUOT :
1234         case EFBIG :
1235         case ENOSPC :
1236             return( AFPERR_DFULL );
1237         default :
1238             LOG(log_error, logtype_afpd, "afp_write: ad_write: %s", strerror(errno) );
1239             return( AFPERR_PARAM );
1240         }
1241     }
1242
1243     return cc;
1244 }
1245
1246
1247 /* FPWrite. NOTE: on an error, we always use afp_write_err as
1248  * the client may have sent us a bunch of data that's not reflected 
1249  * in reqcount et al. */
1250 static int write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, is64)
1251 AFPObj              *obj;
1252 char                *ibuf, *rbuf;
1253 int                 ibuflen, *rbuflen;
1254 int                 is64;
1255 {
1256     struct ofork        *ofork;
1257     off_t               offset, saveoff, reqcount;
1258     int                 endflag, eid, xlate = 0, err = AFP_OK;
1259     u_int16_t           ofrefnum;
1260     ssize_t             cc;
1261
1262     /* figure out parameters */
1263     ibuf++;
1264     endflag = ENDBIT(*ibuf);
1265     ibuf++;
1266     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1267     ibuf += sizeof( ofrefnum );
1268
1269     offset   = get_off_t(&ibuf, is64);
1270     reqcount = get_off_t(&ibuf, is64);
1271
1272     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1273         LOG(log_error, logtype_afpd, "afp_write: of_find");
1274         err = AFPERR_PARAM;
1275         goto afp_write_err;
1276     }
1277
1278     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1279         err = AFPERR_ACCESS;
1280         goto afp_write_err;
1281     }
1282
1283 #ifdef AFS
1284     writtenfork = ofork;
1285 #endif /* AFS */
1286
1287     if ( ofork->of_flags & AFPFORK_DATA) {
1288         eid = ADEID_DFORK;
1289         xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
1290     } else if (ofork->of_flags & AFPFORK_RSRC) {
1291         eid = ADEID_RFORK;
1292     } else {
1293         err = AFPERR_ACCESS; /* should never happen */
1294         goto afp_write_err;
1295     }
1296
1297     if (endflag)
1298         offset += ad_size(ofork->of_ad, eid);
1299
1300     /* handle bogus parameters */
1301     if (reqcount < 0 || offset < 0) {
1302         err = AFPERR_PARAM;
1303         goto afp_write_err;
1304     }
1305
1306     /* offset can overflow on 64-bit capable filesystems.
1307      * report disk full if that's going to happen. */
1308      if (sum_neg(is64, offset, reqcount)) {
1309         err = AFPERR_DFULL;
1310         goto afp_write_err;
1311     }
1312
1313     if (!reqcount) { /* handle request counts of 0 */
1314         err = AFP_OK;
1315         *rbuflen = set_off_t (offset, rbuf, is64);
1316         goto afp_write_err;
1317     }
1318
1319     saveoff = offset;
1320     if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
1321                    reqcount, ofork->of_refnum) < 0) {
1322         err = AFPERR_LOCK;
1323         goto afp_write_err;
1324     }
1325
1326     /* this is yucky, but dsi can stream i/o and asp can't */
1327     switch (obj->proto) {
1328 #ifndef NO_DDP
1329     case AFPPROTO_ASP:
1330         if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
1331             *rbuflen = 0;
1332             LOG(log_error, logtype_afpd, "afp_write: asp_wrtcont: %s", strerror(errno) );
1333             return( AFPERR_PARAM );
1334         }
1335
1336 #ifdef DEBUG1
1337         if (obj->options.flags & OPTION_DEBUG) {
1338             printf("(write) len: %d\n", *rbuflen);
1339             bprint(rbuf, *rbuflen);
1340         }
1341 #endif
1342         if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
1343                              xlate)) < 0) {
1344             *rbuflen = 0;
1345             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1346             return cc;
1347         }
1348         offset += cc;
1349         break;
1350 #endif /* no afp/asp */
1351
1352     case AFPPROTO_DSI:
1353         {
1354             DSI *dsi = obj->handle;
1355
1356             /* find out what we have already and write it out. */
1357             cc = dsi_writeinit(dsi, rbuf, *rbuflen);
1358             if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1359                 dsi_writeflush(dsi);
1360                 *rbuflen = 0;
1361                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1362                 return cc;
1363             }
1364             offset += cc;
1365
1366 #if 0 /*def HAVE_SENDFILE_WRITE*/
1367             if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
1368                 if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
1369                                        offset, dsi->datasize)) < 0) {
1370                     switch (errno) {
1371                     case EDQUOT :
1372                     case EFBIG :
1373                     case ENOSPC :
1374                         cc = AFPERR_DFULL;
1375                         break;
1376                     default :
1377                         LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1378                         goto afp_write_loop;
1379                     }
1380                     dsi_writeflush(dsi);
1381                     *rbuflen = 0;
1382                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1383                                reqcount,  ofork->of_refnum);
1384                     return cc;
1385                 }
1386
1387                 offset += cc;
1388                 goto afp_write_done;
1389             }
1390 #endif /* 0, was HAVE_SENDFILE_WRITE */
1391
1392             /* loop until everything gets written. currently
1393                     * dsi_write handles the end case by itself. */
1394             while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
1395 #ifdef DEBUG1
1396                 if ( obj->options.flags & OPTION_DEBUG ) {
1397                     printf("(write) command cont'd: %d\n", cc);
1398                     bprint(rbuf, cc);
1399                 }
1400 #endif
1401                 if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
1402                     dsi_writeflush(dsi);
1403                     *rbuflen = 0;
1404                     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
1405                                reqcount,  ofork->of_refnum);
1406                     return cc;
1407                 }
1408                 offset += cc;
1409             }
1410         }
1411         break;
1412     }
1413
1414     ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1415     if ( ad_hfileno( ofork->of_ad ) != -1 )
1416         ofork->of_flags |= AFPFORK_DIRTY;
1417
1418     *rbuflen = set_off_t (offset, rbuf, is64);
1419     return( AFP_OK );
1420
1421 afp_write_err:
1422     if (obj->proto == AFPPROTO_DSI) {
1423         dsi_writeinit(obj->handle, rbuf, *rbuflen);
1424         dsi_writeflush(obj->handle);
1425     }
1426     if (err != AFP_OK) {
1427         *rbuflen = 0;
1428     }
1429     return err;
1430 }
1431
1432 /* ---------------------------- */
1433 int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
1434 AFPObj              *obj;
1435 char                *ibuf, *rbuf;
1436 int                 ibuflen, *rbuflen;
1437 {
1438     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1439 }
1440
1441 /* ---------------------------- 
1442  * FIXME need to deal with SIGXFSZ signal
1443 */
1444 int afp_write_ext(obj, ibuf, ibuflen, rbuf, rbuflen)
1445 AFPObj              *obj;
1446 char                *ibuf, *rbuf;
1447 int                 ibuflen, *rbuflen;
1448 {
1449     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1450 }
1451
1452 /* ---------------------------- */
1453 int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1454 AFPObj      *obj;
1455 char    *ibuf, *rbuf;
1456 int             ibuflen, *rbuflen;
1457 {
1458     struct ofork        *ofork;
1459     int                 buflen, ret;
1460     u_int16_t           ofrefnum, bitmap;
1461     u_int16_t           attrbits = 0;
1462
1463     ibuf += 2;
1464     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1465     ibuf += sizeof( ofrefnum );
1466     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1467     bitmap = ntohs( bitmap );
1468     ibuf += sizeof( bitmap );
1469
1470     *rbuflen = 0;
1471     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1472         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find");
1473         return( AFPERR_PARAM );
1474     }
1475     attrbits = ((ofork->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1476     attrbits |= ((ofork->of_ad->ad_hf.adf_refcount > ofork->of_ad->ad_df.adf_refcount) ? ATTRBIT_ROPEN : 0);
1477
1478     if ( ad_hfileno( ofork->of_ad ) != -1 ) {
1479         if ( ad_refresh( ofork->of_ad ) < 0 ) {
1480             LOG(log_error, logtype_afpd, "getforkparams: ad_refresh: %s", strerror(errno) );
1481             return( AFPERR_PARAM );
1482         }
1483     }
1484
1485     if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
1486                                rbuf + sizeof( u_short ), &buflen, attrbits ))) {
1487         return( ret );
1488     }
1489
1490     *rbuflen = buflen + sizeof( u_short );
1491     bitmap = htons( bitmap );
1492     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1493     return( AFP_OK );
1494 }
1495