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