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