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