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