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