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