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