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