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