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