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