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