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