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