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