]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/fork.c
Remove misleading logmessage added in previous commit e4e8f3a8
[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/bstrlib.h>
26 #include <atalk/bstradd.h>
27 #include <atalk/globals.h>
28 #include <atalk/netatalk_conf.h>
29 #include <atalk/ea.h>
30
31 #include "fork.h"
32 #include "file.h"
33 #include "directory.h"
34 #include "desktop.h"
35 #include "volume.h"
36
37 #ifdef AFS
38 struct ofork *writtenfork;
39 #endif
40
41 static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap, char *buf, size_t *buflen)
42 {
43     struct path         path;
44     struct stat     *st;
45
46     struct adouble  *adp;
47     struct dir      *dir;
48     struct vol      *vol;
49
50
51     /* can only get the length of the opened fork */
52     if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN)))
53            && (ofork->of_flags & AFPFORK_RSRC))
54          ||
55          ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN)))
56            && (ofork->of_flags & AFPFORK_DATA))) {
57         return( AFPERR_BITMAP );
58     }
59
60     if (! AD_META_OPEN(ofork->of_ad)) {
61         adp = NULL;
62     } else {
63         adp = ofork->of_ad;
64     }
65
66     vol = ofork->of_vol;
67     dir = dirlookup(vol, ofork->of_did);
68
69     if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) {
70         return( AFPERR_MISC );
71     }
72     path.m_name = of_name(ofork);
73     path.id = 0;
74     st = &path.st;
75     if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) |
76                     (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) |
77                     (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) {
78         if ( ad_data_fileno( ofork->of_ad ) <= 0 ) {
79             /* 0 is for symlink */
80             if (movecwd(vol, dir) < 0)
81                 return( AFPERR_NOOBJ );
82             if ( lstat( path.u_name, st ) < 0 )
83                 return( AFPERR_NOOBJ );
84         } else {
85             if ( fstat( ad_data_fileno( ofork->of_ad ), st ) < 0 ) {
86                 return( AFPERR_BITMAP );
87             }
88         }
89     }
90     return getmetadata(obj, vol, bitmap, &path, dir, buf, buflen, adp );
91 }
92
93 static off_t get_off_t(char **ibuf, int is64)
94 {
95     uint32_t             temp;
96     off_t                 ret;
97
98     ret = 0;
99     memcpy(&temp, *ibuf, sizeof( temp ));
100     ret = ntohl(temp); /* ntohl is unsigned */
101     *ibuf += sizeof(temp);
102
103     if (is64) {
104         memcpy(&temp, *ibuf, sizeof( temp ));
105         *ibuf += sizeof(temp);
106         ret = ntohl(temp)| (ret << 32);
107     }
108     else {
109         ret = (int)ret; /* sign extend */
110     }
111     return ret;
112 }
113
114 static int set_off_t(off_t offset, char *rbuf, int is64)
115 {
116     uint32_t  temp;
117     int        ret;
118
119     ret = 0;
120     if (is64) {
121         temp = htonl(offset >> 32);
122         memcpy(rbuf, &temp, sizeof( temp ));
123         rbuf += sizeof(temp);
124         ret = sizeof( temp );
125         offset &= 0xffffffff;
126     }
127     temp = htonl(offset);
128     memcpy(rbuf, &temp, sizeof( temp ));
129     ret += sizeof( temp );
130
131     return ret;
132 }
133
134 static int is_neg(int is64, off_t val)
135 {
136     if (val < 0 || (sizeof(off_t) == 8 && !is64 && (val & 0x80000000U)))
137         return 1;
138     return 0;
139 }
140
141 static int sum_neg(int is64, off_t offset, off_t reqcount)
142 {
143     if (is_neg(is64, offset +reqcount) )
144         return 1;
145     return 0;
146 }
147
148 static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int access, int ofrefnum)
149 {
150     int ret;
151     int readset;
152     int writeset;
153     int denyreadset;
154     int denywriteset;
155
156 #ifdef HAVE_FSHARE_T
157     fshare_t shmd;
158
159     if (obj->options.flags & OPTION_SHARE_RESERV) {
160         shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0);
161         if (shmd.f_access == 0)
162             /* we must give an access mode, otherwise fcntl will complain */
163             shmd.f_access = F_RDACC;
164         shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0;
165         shmd.f_id = ofrefnum;
166
167         int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp);
168
169         if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) {
170             LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno));
171             errno = EACCES;
172             return -1;
173         }
174     }
175 #endif
176
177     if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) {
178         return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum);
179     }
180
181     if ((access & (OPENACC_RD | OPENACC_DRD))) {
182         if ((readset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_RD)) <0)
183             return readset;
184         if ((denyreadset = ad_testlock(adp, eid, AD_FILELOCK_DENY_RD)) <0)
185             return denyreadset;
186
187         if ((access & OPENACC_RD) && denyreadset) {
188             errno = EACCES;
189             return -1;
190         }
191         if ((access & OPENACC_DRD) && readset) {
192             errno = EACCES;
193             return -1;
194         }
195         /* boolean logic is not enough, because getforkmode is not always telling the
196          * true
197          */
198         if ((access & OPENACC_RD)) {
199             ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_RD, 1, ofrefnum);
200             if (ret)
201                 return ret;
202         }
203         if ((access & OPENACC_DRD)) {
204             ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_RD, 1, ofrefnum);
205             if (ret)
206                 return ret;
207         }
208     }
209     /* ------------same for writing -------------- */
210     if ((access & (OPENACC_WR | OPENACC_DWR))) {
211         if ((writeset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_WR)) <0)
212             return writeset;
213         if ((denywriteset = ad_testlock(adp, eid, AD_FILELOCK_DENY_WR)) <0)
214             return denywriteset;
215
216         if ((access & OPENACC_WR) && denywriteset) {
217             errno = EACCES;
218             return -1;
219         }
220         if ((access & OPENACC_DWR) && writeset) {
221             errno = EACCES;
222             return -1;
223         }
224         if ((access & OPENACC_WR)) {
225             ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_WR, 1, ofrefnum);
226             if (ret)
227                 return ret;
228         }
229         if ((access & OPENACC_DWR)) {
230             ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_WR, 1, ofrefnum);
231             if (ret)
232                 return ret;
233         }
234     }
235
236     return 0;
237 }
238
239 /* ----------------------- */
240 int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
241 {
242     struct vol      *vol;
243     struct dir      *dir;
244     struct ofork    *ofork, *opened;
245     struct adouble  *adsame = NULL;
246     size_t          buflen;
247     int             ret, adflags, eid;
248     uint32_t        did;
249     uint16_t        vid, bitmap, access, ofrefnum;
250     char            fork, *path, *upath;
251     struct stat     *st;
252     uint16_t        bshort;
253     struct path     *s_path;
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
305     upath = s_path->u_name;
306     path = s_path->m_name;
307     st = &s_path->st;
308
309     if (!vol_unix_priv(vol)) {
310         if (check_access(obj, vol, upath, access ) < 0) {
311             return AFPERR_ACCESS;
312         }
313     } else {
314         if (file_access(obj, vol, s_path, access ) < 0) {
315             return AFPERR_ACCESS;
316         }
317     }
318
319     if ((opened = of_findname(vol, s_path))) {
320         adsame = opened->of_ad;
321     }
322
323     adflags = ADFLAGS_SETSHRMD;
324
325     if (fork == OPENFORK_DATA) {
326         eid = ADEID_DFORK;
327         adflags |= ADFLAGS_DF;
328     } else {
329         eid = ADEID_RFORK;
330         adflags |= ADFLAGS_RF;
331     }
332
333     if (access & OPENACC_WR) {
334         adflags |= ADFLAGS_RDWR;
335         if (fork != OPENFORK_DATA)
336             /*
337              * We only try to create the resource
338              * fork if the user wants to open it for write acess.
339              */
340             adflags |= ADFLAGS_CREATE;
341     } else {
342         adflags |= ADFLAGS_RDONLY;
343     }
344
345     if ((ofork = of_alloc(vol, curdir, path, &ofrefnum, eid, adsame, st)) == NULL)
346         return AFPERR_NFILE;
347
348     LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\", %s, %s)",
349         fullpathname(s_path->u_name),
350         (fork == OPENFORK_DATA) ? "data" : "reso",
351         !(access & OPENACC_WR) ? "O_RDONLY" : "O_RDWR");
352
353     ret = AFPERR_NOOBJ;
354
355     /* First ad_open(), opens data or ressource fork */
356     if (ad_open(ofork->of_ad, upath, adflags, 0666) < 0) {
357         switch (errno) {
358         case EROFS:
359             ret = AFPERR_VLOCK;
360         case EACCES:
361             goto openfork_err;
362         case ENOENT:
363             goto openfork_err;
364         case EMFILE :
365         case ENFILE :
366             ret = AFPERR_NFILE;
367             goto openfork_err;
368         case EISDIR :
369             ret = AFPERR_BADTYPE;
370             goto openfork_err;
371         default:
372             LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
373             ret = AFPERR_PARAM;
374             goto openfork_err;
375         }
376     }
377
378     /*
379      * Create metadata if we open rw, otherwise only open existing metadata
380      */
381     if (access & OPENACC_WR) {
382         adflags = ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE;
383     } else {
384         adflags = ADFLAGS_HF | ADFLAGS_RDONLY;
385     }
386
387     if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
388         ofork->of_flags |= AFPFORK_META;
389         if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) {
390             LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath);
391             cnid_t id;
392             if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
393                 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
394                 goto openfork_err;
395             }
396             (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp);
397             ad_flush(ofork->of_ad);
398         }
399     } else {
400         switch (errno) {
401         case EACCES:
402         case ENOENT:
403             /* no metadata? We don't care! */
404             break;
405         case EROFS:
406             ret = AFPERR_VLOCK;
407         case EMFILE :
408         case ENFILE :
409             ret = AFPERR_NFILE;
410             goto openfork_err;
411         case EISDIR :
412             ret = AFPERR_BADTYPE;
413             goto openfork_err;
414         default:
415             LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
416             ret = AFPERR_PARAM;
417             goto openfork_err;
418         }
419     }
420
421     if ((adflags & ADFLAGS_RF) && (ad_get_RF_flags( ofork->of_ad) & O_CREAT)) {
422         if (ad_setname(ofork->of_ad, path)) {
423             ad_flush( ofork->of_ad );
424         }
425     }
426
427     if ((ret = getforkparams(obj, ofork, bitmap, rbuf + 2 * sizeof(int16_t), &buflen)) != AFP_OK) {
428         ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
429         goto openfork_err;
430     }
431
432     *rbuflen = buflen + 2 * sizeof( uint16_t );
433     bitmap = htons( bitmap );
434     memcpy(rbuf, &bitmap, sizeof( uint16_t ));
435     rbuf += sizeof( uint16_t );
436
437     /* check  WriteInhibit bit if we have a ressource fork
438      * the test is done here, after some Mac trafic capture
439      */
440     if (ad_meta_fileno(ofork->of_ad) != -1) {   /* META */
441         ad_getattr(ofork->of_ad, &bshort);
442         if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
443             ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
444             of_dealloc(ofork);
445             ofrefnum = 0;
446             memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
447             return(AFPERR_OLOCK);
448         }
449     }
450
451     /*
452      * synchronization locks:
453      */
454
455     /* don't try to lock non-existent rforks. */
456     if ((eid == ADEID_DFORK)
457         || (ad_reso_fileno(ofork->of_ad) != -1)
458         || (ofork->of_ad->ad_vers == AD_VERSION_EA)) {
459         ret = fork_setmode(obj, ofork->of_ad, eid, access, ofrefnum);
460         /* can we access the fork? */
461         if (ret < 0) {
462             ofork->of_flags |= AFPFORK_ERROR;
463             ret = errno;
464             ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
465             of_dealloc(ofork);
466             switch (ret) {
467             case EAGAIN: /* return data anyway */
468             case EACCES:
469             case EINVAL:
470                 ofrefnum = 0;
471                 memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
472                 return( AFPERR_DENYCONF );
473                 break;
474             default:
475                 *rbuflen = 0;
476                 LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_lock: %s", s_path->m_name, strerror(ret) );
477                 return( AFPERR_PARAM );
478             }
479         }
480         if ((access & OPENACC_WR))
481             ofork->of_flags |= AFPFORK_ACCWR;
482     }
483     /* the file may be open read only without ressource fork */
484     if ((access & OPENACC_RD))
485         ofork->of_flags |= AFPFORK_ACCRD;
486
487     LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): fork: %" PRIu16,
488         fullpathname(s_path->m_name), ofork->of_refnum);
489
490     memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
491     return( AFP_OK );
492
493 openfork_err:
494     of_dealloc(ofork);
495     if (errno == EACCES)
496         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
497     return ret;
498 }
499
500 int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
501 {
502     struct ofork    *ofork;
503     struct vol      *vol;
504     struct dir      *dir;
505     off_t       size;
506     uint16_t       ofrefnum, bitmap;
507     int                 err;
508     int                 is64;
509     int                 eid;
510     off_t       st_size;
511
512     ibuf += 2;
513
514     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
515     ibuf += sizeof( ofrefnum );
516
517     memcpy(&bitmap, ibuf, sizeof(bitmap));
518     bitmap = ntohs(bitmap);
519     ibuf += sizeof( bitmap );
520
521     *rbuflen = 0;
522     if (NULL == ( ofork = of_find( ofrefnum )) ) {
523         LOG(log_error, logtype_afpd, "afp_setforkparams: of_find(%d) could not locate fork", ofrefnum );
524         return( AFPERR_PARAM );
525     }
526
527     vol = ofork->of_vol;
528     if ((dir = dirlookup(vol, ofork->of_did)) == NULL) {
529         LOG(log_error, logtype_afpd, "%s: bad fork did",  of_name(ofork));
530         return AFPERR_MISC;
531     }
532     if (movecwd(vol, dir) != 0) {
533         LOG(log_error, logtype_afpd, "%s: bad fork directory", dir->d_fullpath);
534         return AFPERR_MISC;
535     }
536
537     if (ofork->of_vol->v_flags & AFPVOL_RO)
538         return AFPERR_VLOCK;
539
540     if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
541         return AFPERR_ACCESS;
542
543     if ( ofork->of_flags & AFPFORK_DATA) {
544         eid = ADEID_DFORK;
545     } else if (ofork->of_flags & AFPFORK_RSRC) {
546         eid = ADEID_RFORK;
547     } else
548         return AFPERR_PARAM;
549
550     if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) ))
551            && eid == ADEID_RFORK
552              ) ||
553          ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) ))
554            && eid == ADEID_DFORK)) {
555         return AFPERR_BITMAP;
556     }
557
558     is64 = 0;
559     if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
560         if (obj->afp_version >= 30) {
561             is64 = 4;
562         }
563         else
564             return AFPERR_BITMAP;
565     }
566
567     if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
568         return AFPERR_PARAM ;
569
570     size = get_off_t(&ibuf, is64);
571
572     if (size < 0)
573         return AFPERR_PARAM; /* Some MacOS don't return an error they just don't change the size! */
574
575
576     if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
577         st_size = ad_size(ofork->of_ad, eid);
578         err = -2;
579         if (st_size > size &&
580             ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0)
581             goto afp_setfork_err;
582
583         err = ad_dtruncate( ofork->of_ad, size );
584         if (st_size > size)
585             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
586         if (err < 0)
587             goto afp_setfork_err;
588     } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
589         ad_refresh(NULL, ofork->of_ad );
590
591         st_size = ad_size(ofork->of_ad, eid);
592         err = -2;
593         if (st_size > size &&
594             ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
595             goto afp_setfork_err;
596         }
597
598         err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size);
599         if (st_size > size)
600             ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
601         if (err < 0)
602             goto afp_setfork_err;
603
604         if (ad_flush( ofork->of_ad ) < 0) {
605             LOG(log_error, logtype_afpd, "afp_setforkparams(%s): ad_flush: %s", of_name(ofork), strerror(errno) );
606             return AFPERR_PARAM;
607         }
608     } else
609         return AFPERR_BITMAP;
610
611 #ifdef AFS
612     if ( flushfork( ofork ) < 0 ) {
613         LOG(log_error, logtype_afpd, "afp_setforkparams(%s): flushfork: %s", of_name(ofork), strerror(errno) );
614     }
615 #endif /* AFS */
616
617     return( AFP_OK );
618
619 afp_setfork_err:
620     if (err == -2)
621         return AFPERR_LOCK;
622     else {
623         switch (errno) {
624         case EROFS:
625             return AFPERR_VLOCK;
626         case EPERM:
627         case EACCES:
628             return AFPERR_ACCESS;
629         case EDQUOT:
630         case EFBIG:
631         case ENOSPC:
632             LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL");
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 /*!
743  * Read *rbuflen bytes from fork at offset
744  *
745  * @param ofork    (r)  fork handle
746  * @param eid      (r)  data fork or ressource fork entry id
747  * @param offset   (r)  offset
748  * @param rbuf     (r)  data buffer
749  * @param rbuflen  (rw) in: number of bytes to read, out: bytes read
750  *
751  * @return         AFP status code
752  */
753 static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
754 {
755     ssize_t cc;
756
757     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
758     if ( cc < 0 ) {
759         LOG(log_error, logtype_afpd, "afp_read(%s): ad_read: %s", of_name(ofork), strerror(errno) );
760         *rbuflen = 0;
761         return( AFPERR_PARAM );
762     }
763     *rbuflen = cc;
764
765     if ((size_t)cc < *rbuflen)
766         return AFPERR_EOF;
767     return AFP_OK;
768 }
769
770 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
771 {
772     DSI          *dsi = obj->dsi;
773     struct ofork *ofork;
774     off_t        offset, saveoff, reqcount, savereqcount, size;
775     ssize_t      cc, err;
776     int          eid;
777     uint16_t     ofrefnum;
778
779     /* we break the AFP spec here by not supporting nlmask and nlchar anymore */
780
781     ibuf += 2;
782     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
783     ibuf += sizeof( u_short );
784
785     if (NULL == ( ofork = of_find( ofrefnum )) ) {
786         LOG(log_error, logtype_afpd, "afp_read: of_find(%d) could not locate fork", ofrefnum );
787         err = AFPERR_PARAM;
788         goto afp_read_err;
789     }
790
791     if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
792         err = AFPERR_ACCESS;
793         goto afp_read_err;
794     }
795
796     if ( ofork->of_flags & AFPFORK_DATA) {
797         eid = ADEID_DFORK;
798     } else if (ofork->of_flags & AFPFORK_RSRC) {
799         eid = ADEID_RFORK;
800     } else { /* fork wasn't opened. this should never really happen. */
801         err = AFPERR_ACCESS;
802         goto afp_read_err;
803     }
804
805     offset   = get_off_t(&ibuf, is64);
806     reqcount = get_off_t(&ibuf, is64);
807
808     /* zero request count */
809     err = AFP_OK;
810     if (!reqcount) {
811         goto afp_read_err;
812     }
813
814     AFP_READ_START((long)reqcount);
815
816     /* reqcount isn't always truthful. we need to deal with that. */
817     size = ad_size(ofork->of_ad, eid);
818
819     LOG(log_debug, logtype_afpd,
820         "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")",
821         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size);
822
823     if (offset >= size) {
824         err = AFPERR_EOF;
825         goto afp_read_err;
826     }
827
828     /* subtract off the offset */
829     if (reqcount + offset > size) {
830         reqcount = size - offset;
831         err = AFPERR_EOF;
832     }
833
834     savereqcount = reqcount;
835     saveoff = offset;
836
837     if (reqcount < 0 || offset < 0) {
838         err = AFPERR_PARAM;
839         goto afp_read_err;
840     }
841
842     if (obj->options.flags & OPTION_AFP_READ_LOCK) {
843         if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, offset, reqcount, ofork->of_refnum) < 0) {
844             err = AFPERR_LOCK;
845             goto afp_read_err;
846         }
847     }
848
849 #ifdef WITH_SENDFILE
850     if (!(eid == ADEID_DFORK && ad_data_fileno(ofork->of_ad) == AD_SYMLINK) &&
851         !(obj->options.flags & OPTION_NOSENDFILE)) {
852         int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
853         if (dsi_stream_read_file(dsi, fd, offset, reqcount, err) < 0) {
854             LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s",
855                 of_name(ofork), strerror(errno));
856             goto afp_read_exit;
857         }
858         goto afp_read_done;
859     }
860 #endif
861
862     *rbuflen = MIN(reqcount, dsi->server_quantum);
863
864     cc = read_file(ofork, eid, offset, ibuf, rbuflen);
865     if (cc < 0) {
866         err = cc;
867         goto afp_read_done;
868     }
869
870     LOG(log_debug, logtype_afpd,
871         "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
872         of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
873
874     offset += *rbuflen;
875
876     /*
877      * dsi_readinit() returns size of next read buffer. by this point,
878      * we know that we're sending some data. if we fail, something
879      * horrible happened.
880      */
881     if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0)
882         goto afp_read_exit;
883     *rbuflen = cc;
884
885     while (*rbuflen > 0) {
886         /*
887          * This loop isn't really entered anymore, we've already
888          * sent the whole requested block in dsi_readinit().
889          */
890         cc = read_file(ofork, eid, offset, ibuf, rbuflen);
891         if (cc < 0)
892             goto afp_read_exit;
893
894         offset += *rbuflen;
895         /* dsi_read() also returns buffer size of next allocation */
896         cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */
897         if (cc < 0)
898             goto afp_read_exit;
899         *rbuflen = cc;
900     }
901     dsi_readdone(dsi);
902     goto afp_read_done;
903
904 afp_read_exit:
905     LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
906     dsi_readdone(dsi);
907    if (obj->options.flags & OPTION_AFP_READ_LOCK)
908        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
909     obj->exit(EXITERR_CLNT);
910
911 afp_read_done:
912     if (obj->options.flags & OPTION_AFP_READ_LOCK)
913         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
914
915     AFP_READ_DONE();
916     return err;
917
918 afp_read_err:
919     *rbuflen = 0;
920     return err;
921 }
922
923 /* ---------------------- */
924 int afp_read(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
925 {
926     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
927 }
928
929 /* ---------------------- */
930 int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
931 {
932     return read_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
933 }
934
935 /* ---------------------- */
936 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
937 {
938     struct vol *vol;
939     uint16_t vid;
940
941     *rbuflen = 0;
942     ibuf += 2;
943
944     memcpy(&vid, ibuf, sizeof(vid));
945     if (NULL == ( vol = getvolbyvid( vid )) ) {
946         return( AFPERR_PARAM );
947     }
948
949     of_flush(vol);
950     return( AFP_OK );
951 }
952
953 int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
954 {
955     struct ofork    *ofork;
956     uint16_t       ofrefnum;
957
958     *rbuflen = 0;
959     ibuf += 2;
960     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
961
962     if (NULL == ( ofork = of_find( ofrefnum )) ) {
963         LOG(log_error, logtype_afpd, "afp_flushfork: of_find(%d) could not locate fork", ofrefnum );
964         return( AFPERR_PARAM );
965     }
966
967     LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
968         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
969
970     if ( flushfork( ofork ) < 0 ) {
971         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
972     }
973
974     return( AFP_OK );
975 }
976
977 /*
978   FIXME
979   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
980   fsync(2) on OSX is implemented differently than on other platforms.
981   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
982 */
983 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
984 {
985     struct ofork        *ofork;
986     uint16_t           ofrefnum;
987
988     *rbuflen = 0;
989     ibuf += 2;
990
991     memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
992     ibuf += sizeof( ofrefnum );
993
994     if (NULL == ( ofork = of_find( ofrefnum )) ) {
995         LOG(log_error, logtype_afpd, "afpd_syncfork: of_find(%d) could not locate fork", ofrefnum );
996         return( AFPERR_PARAM );
997     }
998
999     LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
1000         (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
1001
1002     if ( flushfork( ofork ) < 0 ) {
1003         LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
1004         return AFPERR_MISC;
1005     }
1006
1007     return( AFP_OK );
1008 }
1009
1010 /* this is very similar to closefork */
1011 int flushfork(struct ofork *ofork)
1012 {
1013     struct timeval tv;
1014
1015     int err = 0, doflush = 0;
1016
1017     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
1018          fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
1019         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
1020             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
1021         err = -1;
1022     }
1023
1024     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
1025          (ofork->of_flags & AFPFORK_RSRC)) {
1026
1027         /* read in the rfork length */
1028         ad_refresh(NULL, ofork->of_ad);
1029
1030         /* set the date if we're dirty */
1031         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
1032             ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
1033             ofork->of_flags &= ~AFPFORK_DIRTY;
1034             doflush++;
1035         }
1036
1037         /* flush the header */
1038         if (doflush && ad_flush(ofork->of_ad) < 0)
1039             err = -1;
1040
1041         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
1042             err = -1;
1043
1044         if (err < 0)
1045             LOG(log_error, logtype_afpd, "flushfork(%s): hfile(%d) %s",
1046                 of_name(ofork), ad_reso_fileno(ofork->of_ad), strerror(errno) );
1047     }
1048
1049     return( err );
1050 }
1051
1052 int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1053 {
1054     struct ofork    *ofork;
1055     uint16_t       ofrefnum;
1056
1057     *rbuflen = 0;
1058     ibuf += 2;
1059     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1060
1061     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1062         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
1063         return( AFPERR_PARAM );
1064     }
1065
1066     LOG(log_debug, logtype_afpd, "afp_closefork(fork: %" PRIu16 " [%s])",
1067         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc");
1068
1069     if (of_closefork(obj, ofork) < 0 ) {
1070         LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) );
1071         return( AFPERR_PARAM );
1072     }
1073
1074     return( AFP_OK );
1075 }
1076
1077
1078 static ssize_t write_file(struct ofork *ofork, int eid,
1079                           off_t offset, char *rbuf,
1080                           size_t rbuflen)
1081 {
1082     ssize_t cc;
1083
1084     LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
1085         (uintmax_t)offset, rbuflen);
1086
1087     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
1088                         rbuf, rbuflen)) < 0 ) {
1089         switch ( errno ) {
1090         case EDQUOT :
1091         case EFBIG :
1092         case ENOSPC :
1093             LOG(log_error, logtype_afpd, "write_file: DISK FULL");
1094             return( AFPERR_DFULL );
1095         case EACCES:
1096             return AFPERR_ACCESS;
1097         default :
1098             LOG(log_error, logtype_afpd, "afp_write(%s): ad_write: %s", of_name(ofork), strerror(errno) );
1099             return( AFPERR_PARAM );
1100         }
1101     }
1102
1103     return cc;
1104 }
1105
1106
1107 /*
1108  * FPWrite. NOTE: on an error, we always use afp_write_err as
1109  * the client may have sent us a bunch of data that's not reflected
1110  * in reqcount et al.
1111  */
1112 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
1113 {
1114     struct ofork    *ofork;
1115     off_t           offset, saveoff, reqcount, oldsize, newsize;
1116     int             endflag, eid, err = AFP_OK;
1117     uint16_t        ofrefnum;
1118     ssize_t         cc;
1119     DSI             *dsi = obj->dsi;
1120     char            *rcvbuf = (char *)dsi->commands;
1121     size_t          rcvbuflen = dsi->server_quantum;
1122
1123     /* figure out parameters */
1124     ibuf++;
1125     endflag = ENDBIT(*ibuf);
1126     ibuf++;
1127     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1128     ibuf += sizeof( ofrefnum );
1129
1130     offset   = get_off_t(&ibuf, is64);
1131     reqcount = get_off_t(&ibuf, is64);
1132
1133     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1134         LOG(log_error, logtype_afpd, "afp_write: of_find(%d) could not locate fork", ofrefnum );
1135         err = AFPERR_PARAM;
1136         goto afp_write_err;
1137     }
1138
1139     LOG(log_debug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu64 ")",
1140         ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount);
1141
1142     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
1143         err = AFPERR_ACCESS;
1144         goto afp_write_err;
1145     }
1146
1147 #ifdef AFS
1148     writtenfork = ofork;
1149 #endif /* AFS */
1150
1151     if ( ofork->of_flags & AFPFORK_DATA) {
1152         eid = ADEID_DFORK;
1153     } else if (ofork->of_flags & AFPFORK_RSRC) {
1154         eid = ADEID_RFORK;
1155     } else {
1156         err = AFPERR_ACCESS; /* should never happen */
1157         goto afp_write_err;
1158     }
1159
1160     oldsize = ad_size(ofork->of_ad, eid);
1161     if (endflag)
1162         offset += oldsize;
1163
1164     /* handle bogus parameters */
1165     if (reqcount < 0 || offset < 0) {
1166         err = AFPERR_PARAM;
1167         goto afp_write_err;
1168     }
1169
1170     newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
1171
1172     /* offset can overflow on 64-bit capable filesystems.
1173      * report disk full if that's going to happen. */
1174     if (sum_neg(is64, offset, reqcount)) {
1175         LOG(log_error, logtype_afpd, "write_fork: DISK FULL");
1176         err = AFPERR_DFULL;
1177         goto afp_write_err;
1178     }
1179
1180     if (!reqcount) { /* handle request counts of 0 */
1181         err = AFP_OK;
1182         *rbuflen = set_off_t (offset, rbuf, is64);
1183         goto afp_write_err;
1184     }
1185
1186     AFP_WRITE_START((long)reqcount);
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     if ((cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen)) > 0) {
1198         ssize_t written;
1199         if ((written = write_file(ofork, eid, offset, rcvbuf, cc)) != cc) {
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             if (written > 0)
1205                 /* It's used for the read size and as error code in write_file(), ugh */
1206                 written = AFPERR_MISC;
1207             return written;
1208         }
1209     }
1210
1211     offset += cc;
1212
1213 #ifdef WITH_RECVFILE
1214     if (obj->options.flags & OPTION_RECVFILE) {
1215         LOG(log_maxdebug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu32 ")",
1216             ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, dsi->datasize);
1217
1218         if ((cc = ad_recvfile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize, obj->options.splice_size)) < dsi->datasize) {
1219             switch (errno) {
1220             case EDQUOT:
1221             case EFBIG:
1222             case ENOSPC:
1223                 cc = AFPERR_DFULL;
1224                 dsi_writeflush(dsi);
1225                 break;
1226             case ENOSYS:
1227                 goto afp_write_loop;
1228             default:
1229                 /* Low level error, can't do much to back up */
1230                 cc = AFPERR_MISC;
1231                 LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno));
1232             }
1233             *rbuflen = 0;
1234             if (obj->options.flags & OPTION_AFP_READ_LOCK)
1235                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1236             return cc;
1237         }
1238
1239         offset += cc;
1240         goto afp_write_done;
1241     }
1242 #endif
1243
1244 afp_write_loop:
1245     /* loop until everything gets written. currently
1246      * dsi_write handles the end case by itself. */
1247     while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) {
1248
1249         if ((cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
1250             dsi_writeflush(dsi);
1251             *rbuflen = 0;
1252             if (obj->options.flags & OPTION_AFP_READ_LOCK)
1253                 ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1254             return cc;
1255         }
1256
1257         LOG(log_debug, logtype_afpd, "afp_write: wrote: %jd, offset: %jd",
1258             (intmax_t)cc, (intmax_t)offset);
1259
1260         offset += cc;
1261     }
1262
1263 afp_write_done:
1264     if (obj->options.flags & OPTION_AFP_READ_LOCK)
1265         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
1266     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
1267         ofork->of_flags |= AFPFORK_DIRTY;
1268
1269     /* we have modified any fork, remember until close_fork */
1270     ofork->of_flags |= AFPFORK_MODIFIED;
1271
1272     /* update write count */
1273     ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
1274
1275     *rbuflen = set_off_t (offset, rbuf, is64);
1276     AFP_WRITE_DONE();
1277     return( AFP_OK );
1278
1279 afp_write_err:
1280     dsi_writeinit(dsi, rcvbuf, rcvbuflen);
1281     dsi_writeflush(dsi);
1282
1283     if (err != AFP_OK) {
1284         *rbuflen = 0;
1285     }
1286     return err;
1287 }
1288
1289 /* ---------------------------- */
1290 int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1291 {
1292     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
1293 }
1294
1295 /* ----------------------------
1296  * FIXME need to deal with SIGXFSZ signal
1297  */
1298 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
1299 {
1300     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
1301 }
1302
1303 /* ---------------------------- */
1304 int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1305 {
1306     struct ofork    *ofork;
1307     int             ret;
1308     uint16_t       ofrefnum, bitmap;
1309     size_t          buflen;
1310     ibuf += 2;
1311     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
1312     ibuf += sizeof( ofrefnum );
1313     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1314     bitmap = ntohs( bitmap );
1315     ibuf += sizeof( bitmap );
1316
1317     *rbuflen = 0;
1318     if (NULL == ( ofork = of_find( ofrefnum )) ) {
1319         LOG(log_error, logtype_afpd, "afp_getforkparams: of_find(%d) could not locate fork", ofrefnum );
1320         return( AFPERR_PARAM );
1321     }
1322
1323     if (AD_META_OPEN(ofork->of_ad)) {
1324         if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
1325             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
1326             return( AFPERR_PARAM );
1327         }
1328     }
1329
1330     if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
1331         return( ret );
1332     }
1333
1334     *rbuflen = buflen + sizeof( u_short );
1335     bitmap = htons( bitmap );
1336     memcpy(rbuf, &bitmap, sizeof( bitmap ));
1337     return( AFP_OK );
1338 }
1339