]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
Fix typo
[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 != AD_SYMLINK && 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(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(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(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             LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL");
634             return AFPERR_DFULL;
635         default:
636             return AFPERR_PARAM;
637         }
638     }
639 }
640
641 /* for this to work correctly, we need to check for locks before each
642  * read and write. that's most easily handled by always doing an
643  * appropriate check before each ad_read/ad_write. other things
644  * that can change files like truncate are handled internally to those
645  * functions.
646  */
647 #define ENDBIT(a)  ((a) & 0x80)
648 #define UNLOCKBIT(a) ((a) & 0x01)
649
650
651 /* ---------------------- */
652 static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
653 {
654     struct ofork    *ofork;
655     off_t               offset, length;
656     int                 eid;
657     uint16_t       ofrefnum;
658     uint8_t            flags;
659     int                 lockop;
660
661     *rbuflen = 0;
662
663     /* figure out parameters */
664     ibuf++;
665     flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
666     ibuf++;
667     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
668     ibuf += sizeof(ofrefnum);
669
670     if (NULL == ( ofork = of_find( ofrefnum )) ) {
671         LOG(log_error, logtype_afpd, "byte_lock: of_find(%d) could not locate fork", ofrefnum );
672         return( AFPERR_PARAM );
673     }
674
675     if ( ofork->of_flags & AFPFORK_DATA) {
676         eid = ADEID_DFORK;
677     } else if (ofork->of_flags & AFPFORK_RSRC) {
678         eid = ADEID_RFORK;
679     } else
680         return AFPERR_PARAM;
681
682     offset = get_off_t(&ibuf, is64);
683     length = get_off_t(&ibuf, is64);
684
685     if (length == -1)
686         length = BYTELOCK_MAX;
687     else if (!length || is_neg(is64, length)) {
688         return AFPERR_PARAM;
689     } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
690         return AFPERR_LOCK;
691     }
692
693     if (ENDBIT(flags)) {
694         offset += ad_size(ofork->of_ad, eid);
695         /* FIXME what do we do if file size > 2 GB and
696            it's not byte_lock_ext?
697         */
698     }
699     if (offset < 0)    /* error if we have a negative offset */
700         return AFPERR_PARAM;
701
702     /* if the file is a read-only file, we use read locks instead of
703      * write locks. that way, we can prevent anyone from initiating
704      * a write lock. */
705     lockop = UNLOCKBIT(flags) ? ADLOCK_CLR : ADLOCK_WR;
706     if (ad_lock(ofork->of_ad, eid, lockop, offset, length,
707                 ofork->of_refnum) < 0) {
708         switch (errno) {
709         case EACCES:
710         case EAGAIN:
711             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
712             break;
713         case ENOLCK:
714             return AFPERR_NLOCK;
715             break;
716         case EINVAL:
717             return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
718             break;
719         case EBADF:
720         default:
721             return AFPERR_PARAM;
722             break;
723         }
724     }
725     *rbuflen = set_off_t (offset, rbuf, is64);
726     return( AFP_OK );
727 }
728
729 /* --------------------------- */
730 int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
731 {
732     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
733 }
734
735 /* --------------------------- */
736 int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
737 {
738     return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
739 }
740
741 #undef UNLOCKBIT
742
743 /*!
744  * Read *rbuflen bytes from fork at offset
745  *
746  * @param ofork    (r)  fork handle
747  * @param eid      (r)  data fork or ressource fork entry id
748  * @param offset   (r)  offset
749  * @param rbuf     (r)  data buffer
750  * @param rbuflen  (rw) in: number of bytes to read, out: bytes read
751  *
752  * @return         AFP status code
753  */
754 static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
755 {
756     ssize_t cc;
757     int eof = 0;
758     char *p, *q;
759
760     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
761     if ( cc < 0 ) {
762         LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
763         *rbuflen = 0;
764         return( AFPERR_PARAM );
765     }
766     *rbuflen = cc;
767
768     if ((size_t)cc < *rbuflen)
769         return AFPERR_EOF;
770     return AFP_OK;
771 }
772
773 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
774 {
775     DSI          *dsi = obj->dsi;
776     struct ofork *ofork;
777     off_t        offset, saveoff, reqcount, savereqcount, size;
778     ssize_t      cc, err;
779     int          eid;
780     uint16_t     ofrefnum;
781
782     /* we break the AFP spec here by not supporting nlmask and nlchar anymore */
783
784     ibuf += 2;
785     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
786     ibuf += sizeof( u_short );
787
788     if (NULL == ( ofork = of_find( ofrefnum )) ) {
789         LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
790         err = AFPERR_PARAM;
791         goto afp_read_err;
792     }
793
794     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
795         err = AFPERR_ACCESS;
796         goto afp_read_err;
797     }
798
799     if ( ofork->of_flags & AFPFORK_DATA) {
800         eid = ADEID_DFORK;
801     } else if (ofork->of_flags & AFPFORK_RSRC) {
802         eid = ADEID_RFORK;
803     } else { /* fork wasn't opened. this should never really happen. */
804         err = AFPERR_ACCESS;
805         goto afp_read_err;
806     }
807
808     offset   = get_off_t(&ibuf, is64);
809     reqcount = get_off_t(&ibuf, is64);
810
811     /* zero request count */
812     err = AFP_OK;
813     if (!reqcount) {
814         goto afp_read_err;
815     }
816
817     /* reqcount isn't always truthful. we need to deal with that. */
818     size = ad_size(ofork->of_ad, eid);
819
820     LOG(log_debug, logtype_afpd,
821         "afp_read(off: %" PRIu64 ", len: %" PRIu64 ", fork: %s, size: %" PRIu64 ")",
822         offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r", size);
823
824     if (offset > size) {
825         err = AFPERR_EOF;
826         goto afp_read_err;
827     }
828
829     /* subtract off the offset */
830     if (reqcount + offset > size) {
831         reqcount = size - offset;
832         err = AFPERR_EOF;
833     }
834
835     savereqcount = reqcount;
836     saveoff = offset;
837
838     LOG(log_debug, logtype_afpd,
839         "afp_read(off: %" PRIu64 ", len: %" PRIu64 ", fork: %s)",
840         offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
841
842     if (reqcount < 0 || offset < 0) {
843         err = AFPERR_PARAM;
844         goto afp_read_err;
845     }
846
847     LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)",
848         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount);
849
850     if (obj->options.flags & OPTION_AFP_READ_LOCK) {
851         if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, offset, reqcount, ofork->of_refnum) < 0) {
852             err = AFPERR_LOCK;
853             goto afp_read_err;
854         }
855     }
856
857 #ifdef WITH_SENDFILE
858     if (!(eid == ADEID_DFORK && ad_data_fileno(ofork->of_ad) == AD_SYMLINK) &&
859         !(obj->options.flags & OPTION_NOSENDFILE)) {
860         int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
861         if (dsi_stream_read_file(dsi, fd, offset, reqcount, err) < 0) {
862             LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s",
863                 of_name(ofork), strerror(errno));
864             goto afp_read_exit;
865         }
866         dsi_readdone(dsi);
867         goto afp_read_done;
868     }
869 #endif
870
871     *rbuflen = MIN(reqcount, *rbuflen);
872
873     err = read_file(ofork, eid, offset, rbuf, rbuflen);
874     if (err < 0)
875         goto afp_read_done;
876
877     LOG(log_debug, logtype_afpd,
878         "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
879         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
880
881     offset += *rbuflen;
882
883     /*
884      * dsi_readinit() returns size of next read buffer. by this point,
885      * we know that we're sending some data. if we fail, something
886      * horrible happened.
887      */
888     if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
889         goto afp_read_exit;
890     *rbuflen = cc;
891
892     while (*rbuflen > 0) {
893         cc = read_file(ofork, eid, offset, rbuf, rbuflen);
894         if (cc < 0)
895             goto afp_read_exit;
896
897         offset += *rbuflen;
898         /* dsi_read() also returns buffer size of next allocation */
899         cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
900         if (cc < 0)
901             goto afp_read_exit;
902         *rbuflen = cc;
903     }
904     dsi_readdone(dsi);
905     goto afp_read_done;
906
907 afp_read_exit:
908     LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
909     dsi_readdone(dsi);
910    if (obj->options.flags & OPTION_AFP_READ_LOCK)
911        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
912     obj->exit(EXITERR_CLNT);
913
914 afp_read_done:
915     if (obj->options.flags & OPTION_AFP_READ_LOCK)
916         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
917     return err;
918
919 afp_read_err:
920     *rbuflen = 0;
921     return err;
922 }
923
924 /* ---------------------- */
925 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
926 {
927     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
928 }
929
930 /* ---------------------- */
931 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
932 {
933     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
934 }
935
936 /* ---------------------- */
937 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
938 {
939     struct vol *vol;
940     uint16_t vid;
941
942     *rbuflen = 0;
943     ibuf += 2;
944
945     memcpy(&vid, ibuf, sizeof(vid));
946     if (NULL == ( vol = getvolbyvid( vid )) ) {
947         return( AFPERR_PARAM );
948     }
949
950     of_flush(vol);
951     return( AFP_OK );
952 }
953
954 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
955 {
956     struct ofork    *ofork;
957     uint16_t       ofrefnum;
958
959     *rbuflen = 0;
960     ibuf += 2;
961     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
962
963     if (NULL == ( ofork = of_find( ofrefnum )) ) {
964         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
965         return( AFPERR_PARAM );
966     }
967
968     LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
969         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
970
971     if ( flushfork( ofork ) < 0 ) {
972         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
973     }
974
975     return( AFP_OK );
976 }
977
978 /*
979   FIXME
980   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
981   fsync(2) on OSX is implemented differently than on other platforms.
982   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
983 */
984 int afp_syncfork(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
992     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
993     ibuf += sizeof( ofrefnum );
994
995     if (NULL == ( ofork = of_find( ofrefnum )) ) {
996         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
997         return( AFPERR_PARAM );
998     }
999
1000     LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
1001         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1002
1003     if ( flushfork( ofork ) < 0 ) {
1004         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1005         return AFPERR_MISC;
1006     }
1007
1008     return( AFP_OK );
1009 }
1010
1011 /* this is very similar to closefork */
1012 int flushfork(struct ofork *ofork)
1013 {
1014     struct timeval tv;
1015
1016     int err = 0, doflush = 0;
1017
1018     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1019          fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1020         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1021             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1022         err = -1;
1023     }
1024
1025     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1026          (ofork->of_flags & AFPFORK_RSRC)) {
1027
1028         /* read in the rfork length */
1029         ad_refresh(NULL, ofork->of_ad);
1030
1031         /* set the date if we're dirty */
1032         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1033             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1034             ofork->of_flags &= ~AFPFORK_DIRTY;
1035             doflush++;
1036         }
1037
1038         /* flush the header */
1039         if (doflush && ad_flush(ofork->of_ad) < 0)
1040             err = -1;
1041
1042         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1043             err = -1;
1044
1045         if (err < 0)
1046             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1047                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1048     }
1049
1050     return( err );
1051 }
1052
1053 int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1054 {
1055     struct ofork    *ofork;
1056     uint16_t       ofrefnum;
1057
1058     *rbuflen = 0;
1059     ibuf += 2;
1060     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1061
1062     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1063         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1064         return( AFPERR_PARAM );
1065     }
1066
1067     LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)",
1068         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1069
1070     if (of_closefork(obj, ofork) < 0 ) {
1071         LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
1072         return( AFPERR_PARAM );
1073     }
1074
1075     return( AFP_OK );
1076 }
1077
1078
1079 static ssize_t write_file(struct ofork *ofork, int eid,
1080                           off_t offset, char *rbuf,
1081                           size_t rbuflen)
1082 {
1083     char *p, *q;
1084     ssize_t cc;
1085
1086     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
1087         (uintmax_t)offset, rbuflen);
1088
1089     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1090                         rbuf, rbuflen)) < 0 ) {
1091         switch ( errno ) {
1092         case EDQUOT :
1093         case EFBIG :
1094         case ENOSPC :
1095             LOG(log_error, logtype_afpd, "write_file: DISK FULL");
1096             return( AFPERR_DFULL );
1097         case EACCES:
1098             return AFPERR_ACCESS;
1099         default :
1100             LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1101             return( AFPERR_PARAM );
1102         }
1103     }
1104
1105     return cc;
1106 }
1107
1108
1109 /*
1110  * FPWrite. NOTE: on an error, we always use afp_write_err as
1111  * the client may have sent us a bunch of data that's not reflected
1112  * in reqcount et al.
1113  */
1114 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1115 {
1116     struct ofork    *ofork;
1117     off_t           offset, saveoff, reqcount, oldsize, newsize;
1118     int             endflag, eid, err = AFP_OK;
1119     uint16_t        ofrefnum;
1120     ssize_t         cc;
1121     DSI             *dsi = obj->dsi;
1122     char            *rcvbuf = dsi->buffer;
1123     size_t          rcvbuflen = dsi->dsireadbuf * dsi->server_quantum;
1124
1125     /* figure out parameters */
1126     ibuf++;
1127     endflag = ENDBIT(*ibuf);
1128     ibuf++;
1129     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1130     ibuf += sizeof( ofrefnum );
1131
1132     offset   = get_off_t(&ibuf, is64);
1133     reqcount = get_off_t(&ibuf, is64);
1134
1135     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1136         LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1137         err = AFPERR_PARAM;
1138         goto afp_write_err;
1139     }
1140
1141     LOG(log_debug, logtype_afpd, "afp_write(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)",
1142         offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1143
1144     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1145         err = AFPERR_ACCESS;
1146         goto afp_write_err;
1147     }
1148
1149 #ifdef AFS
1150     writtenfork = ofork;
1151 #endif /* AFS */
1152
1153     if ( ofork->of_flags & AFPFORK_DATA) {
1154         eid = ADEID_DFORK;
1155     } else if (ofork->of_flags & AFPFORK_RSRC) {
1156         eid = ADEID_RFORK;
1157     } else {
1158         err = AFPERR_ACCESS; /* should never happen */
1159         goto afp_write_err;
1160     }
1161
1162     oldsize = ad_size(ofork->of_ad, eid);
1163     if (endflag)
1164         offset += oldsize;
1165
1166     /* handle bogus parameters */
1167     if (reqcount < 0 || offset < 0) {
1168         err = AFPERR_PARAM;
1169         goto afp_write_err;
1170     }
1171
1172     newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1173
1174     /* offset can overflow on 64-bit capable filesystems.
1175      * report disk full if that's going to happen. */
1176     if (sum_neg(is64, offset, reqcount)) {
1177         LOG(log_error, logtype_afpd, "write_fork: DISK FULL");
1178         err = AFPERR_DFULL;
1179         goto afp_write_err;
1180     }
1181
1182     if (!reqcount) { /* handle request counts of 0 */
1183         err = AFP_OK;
1184         *rbuflen = set_off_t (offset, rbuf, is64);
1185         goto afp_write_err;
1186     }
1187
1188     saveoff = offset;
1189     if (obj->options.flags & OPTION_AFP_READ_LOCK) {
1190         if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) {
1191             err = AFPERR_LOCK;
1192             goto afp_write_err;
1193         }
1194     }
1195
1196     /* find out what we have already */
1197     cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen);
1198
1199     if (!cc || (cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
1200         dsi_writeflush(dsi);
1201         *rbuflen = 0;
1202         if (obj->options.flags & OPTION_AFP_READ_LOCK)
1203             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
1204         return cc;
1205     }
1206
1207     offset += cc;
1208
1209 #if 0 /*def HAVE_SENDFILE_WRITE*/
1210     if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize)) < 0) {
1211         switch (errno) {
1212         case EDQUOT:
1213         case EFBIG:
1214         case ENOSPC:
1215             cc = AFPERR_DFULL;
1216             break;
1217         default:
1218             LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
1219             goto afp_write_loop;
1220         }
1221         dsi_writeflush(dsi);
1222         *rbuflen = 0;
1223         if (obj->options.flags & OPTION_AFP_READ_LOCK)
1224             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1225         return cc;
1226     }
1227
1228     offset += cc;
1229     goto afp_write_done;
1230 #endif /* 0, was HAVE_SENDFILE_WRITE */
1231
1232     /* loop until everything gets written. currently
1233      * dsi_write handles the end case by itself. */
1234     while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) {
1235
1236         if ((cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
1237             dsi_writeflush(dsi);
1238             *rbuflen = 0;
1239             if (obj->options.flags & OPTION_AFP_READ_LOCK)
1240                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1241             return cc;
1242         }
1243
1244         LOG(log_debug, logtype_afpd, "afp_write: wrote: %jd, offset: %jd",
1245             (intmax_t)cc, (intmax_t)offset);
1246
1247         offset += cc;
1248     }
1249
1250     if (obj->options.flags & OPTION_AFP_READ_LOCK)
1251         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1252     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1253         ofork->of_flags |= AFPFORK_DIRTY;
1254
1255     /* we have modified any fork, remember until close_fork */
1256     ofork->of_flags |= AFPFORK_MODIFIED;
1257
1258     /* update write count */
1259     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1260
1261     *rbuflen = set_off_t (offset, rbuf, is64);
1262     return( AFP_OK );
1263
1264 afp_write_err:
1265     dsi_writeinit(obj->dsi, rbuf, *rbuflen);
1266     dsi_writeflush(obj->dsi);
1267
1268     if (err != AFP_OK) {
1269         *rbuflen = 0;
1270     }
1271     return err;
1272 }
1273
1274 /* ---------------------------- */
1275 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1276 {
1277     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1278 }
1279
1280 /* ----------------------------
1281  * FIXME need to deal with SIGXFSZ signal
1282  */
1283 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1284 {
1285     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1286 }
1287
1288 /* ---------------------------- */
1289 int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1290 {
1291     struct ofork    *ofork;
1292     int             ret;
1293     uint16_t       ofrefnum, bitmap;
1294     size_t          buflen;
1295     ibuf += 2;
1296     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1297     ibuf += sizeof( ofrefnum );
1298     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1299     bitmap = ntohs( bitmap );
1300     ibuf += sizeof( bitmap );
1301
1302     *rbuflen = 0;
1303     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1304         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1305         return( AFPERR_PARAM );
1306     }
1307
1308     if (AD_META_OPEN(ofork->of_ad)) {
1309         if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
1310             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1311             return( AFPERR_PARAM );
1312         }
1313     }
1314
1315     if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
1316         return( ret );
1317     }
1318
1319     *rbuflen = buflen + sizeof( u_short );
1320     bitmap = htons( bitmap );
1321     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1322     return( AFP_OK );
1323 }
1324