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