]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
Fix some errno assignments (== vs. =).
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * $Id: ad_open.c,v 1.22 2002-11-14 17:11:54 srittau Exp $
3  *
4  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
5  * Copyright (c) 1990,1991 Regents of The University of Michigan.
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software and
9  * its documentation for any purpose and without fee is hereby granted,
10  * provided that the above copyright notice appears in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation, and that the name of The University
13  * of Michigan not be used in advertising or publicity pertaining to
14  * distribution of the software without specific, written prior
15  * permission. This software is supplied as is without expressed or
16  * implied warranties of any kind.
17  *
18  *      Research Systems Unix Group
19  *      The University of Michigan
20  *      c/o Mike Clark
21  *      535 W. William Street
22  *      Ann Arbor, Michigan
23  *      +1-313-763-0525
24  *      netatalk@itd.umich.edu
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30
31 #include <string.h>
32 #ifdef HAVE_FCNTL_H
33 #include <fcntl.h>
34 #endif /* HAVE_FCNTL_H */
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif /* HAVE_UNISTD_H */
38 #include <errno.h>
39 #include <atalk/logger.h>
40
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/param.h>
45 #include <sys/mman.h>
46
47 #include <netatalk/endian.h>
48 #include <atalk/adouble.h>
49
50 #include "ad_private.h"
51
52 #ifndef MAX
53 #define MAX(a, b)  ((a) < (b) ? (b) : (a))
54 #endif /* ! MAX */
55
56 /*
57  * AppleDouble entry default offsets.
58  * The layout looks like this:
59  *
60  * this is the v1 layout:
61  *        255     200             16      32              N
62  *      |  NAME |    COMMENT    | FILEI |    FINDERI    | RFORK |
63  *
64  * we need to change it to look like this:
65  *
66  * v2 layout:
67  * field       length (in bytes)
68  * NAME        255
69  * COMMENT     200
70  * FILEDATESI  16     replaces FILEI
71  * FINDERI     32  
72  * DID          4     new
73  * AFPFILEI     4     new
74  * SHORTNAME   12     8.3 new
75  * RFORK        N
76  * 
77  * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
78  * and add in the new fields.
79  *
80  * NOTE: the HFS module will need similar modifications to interact with
81  * afpd correctly.
82  */
83  
84 #define ADEDOFF_MAGIC        (0)
85 #define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
86 #define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
87 #define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
88
89 /* initial lengths of some of the fields */
90 #define ADEDLEN_INIT     0
91
92 /* make sure we don't redefine ADEDOFF_FILEI */
93 #ifdef ADEDOFF_FILEI
94 #undef ADEDOFF_FILEI
95 #endif /* ADEDOFF_FILEI */
96
97 #define ADEID_NUM_V1         5
98 #define ADEDOFF_NAME_V1      (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
99 #define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
100 #define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
101 #define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
102 #define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
103
104 /* i stick things in a slightly different order than their eid order in 
105  * case i ever want to separate RootInfo behaviour from the rest of the 
106  * stuff. */
107 #define ADEID_NUM_V2         9
108 #define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
109 #define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
110 #define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
111 #define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
112 #define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
113 #define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
114 #define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
115 #define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
116 #define ADEDOFF_RFORK_V2     (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
117
118
119
120 /* we keep local copies of a bunch of stuff so that we can initialize things 
121  * correctly. */
122
123 /* Bits in the finderinfo data. 
124  * see etc/afpd/{directory.c,file.c} for the finderinfo structure
125  * layout. */
126 #define FINDERINFO_CUSTOMICON 0x4
127 #define FINDERINFO_CLOSEDVIEW 0x100
128
129 /* offsets in finderinfo */
130 #define FINDERINFO_FRTYPEOFF   0
131 #define FINDERINFO_FRCREATOFF  4
132 #define FINDERINFO_FRFLAGOFF   8
133 #define FINDERINFO_FRVIEWOFF  14
134
135 /* invisible bit for dot files */
136 #define ATTRBIT_INVISIBLE     (1 << 0)
137 #define FINDERINFO_INVISIBLE  (1 << 14)
138
139 /* this is to prevent changing timezones from causing problems with
140    localtime volumes. the screw-up is 30 years. we use a delta of 5
141    years.  */
142 #define TIMEWARP_DELTA 157680000
143
144
145 struct entry {
146   u_int32_t id, offset, len;
147 };
148
149 #if AD_VERSION == AD_VERSION1 
150 static const struct entry entry_order[] = {
151   {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT},
152   {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},
153   {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI},
154   {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},
155   {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT},
156   {0, 0, 0}
157 };
158 #else /* AD_VERSION == AD_VERSION2 */
159 static const struct entry entry_order[] = {
160   {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
161   {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
162   {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
163   {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
164   {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
165   {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
166   {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
167   {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
168   {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
169   {0, 0, 0}
170 };
171 #endif /* AD_VERSION == AD_VERSION2 */
172
173 #if AD_VERSION == AD_VERSION2
174
175
176 static __inline__ int ad_v1tov2(struct adouble *ad, const char *path)
177 {
178   struct stat st;
179   u_int16_t attr;
180   char *buf;
181   int fd, off;
182   
183   /* check to see if we should convert this header. */
184   if (!path || (ad->ad_version != AD_VERSION1))
185     return 0;
186
187   /* convert from v1 to v2. what does this mean?
188    *  1) change FILEI into FILEDATESI
189    *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
190    *  3) move FILEI attributes into AFPFILEI
191    *  4) initialize ACCESS field of FILEDATESI.
192    *
193    *  so, we need 4*12 (entry ids) + 12 (shortname) + 4 (afpfilei) +
194    *  4 (did) + 8 (prodosi) = 76 more bytes.  */
195   
196 #define SHIFTDATA (AD_DATASZ2 - AD_DATASZ1)
197
198   /* bail if we can't get a lock */
199   if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0) 
200     goto bail_err;
201   
202   if ((fd = open(path, O_RDWR)) < 0) 
203     goto bail_lock;
204   
205   if (fstat(fd, &st) ||
206       ftruncate(fd, st.st_size + SHIFTDATA) < 0) {
207     goto bail_open;
208   }
209   
210   /* last place for failure. */
211   if ((void *) (buf = (char *) 
212                 mmap(NULL, st.st_size + SHIFTDATA,
213                      PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 
214           MAP_FAILED) {
215     goto bail_truncate;
216   }
217   
218   off = ad->ad_eid[ADEID_RFORK].ade_off;
219
220   /* move the RFORK. this assumes that the RFORK is at the end */
221   memmove(buf + off + SHIFTDATA, buf + off, 
222           ad->ad_eid[ADEID_RFORK].ade_len);
223   
224   /* now, fix up our copy of the header */
225   memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
226   
227   /* replace FILEI with FILEDATESI */
228   ad_getattr(ad, &attr);
229   ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
230   ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
231   ad->ad_eid[ADEID_FILEI].ade_off = 0;
232   ad->ad_eid[ADEID_FILEI].ade_len = 0;
233   
234   /* add in the new entries */
235   ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
236   ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
237   ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
238   ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
239   ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
240   ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
241   ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
242   ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
243   
244   /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
245   ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
246   ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
247   ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
248   ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
249   
250   /* switch to v2 */
251   ad->ad_version = AD_VERSION2;
252   
253   /* move our data buffer to make space for the new entries. */
254   memmove(buf + ADEDOFF_NAME_V2, buf + ADEDOFF_NAME_V1,
255           ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
256   
257   /* now, fill in the space with appropriate stuff. we're
258      operating as a v2 file now. */
259   ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
260   memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
261   memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
262   ad_setattr(ad, attr);
263   memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
264   memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
265   
266   /* rebuild the header and cleanup */
267   ad_rebuild_header(ad);
268   munmap(buf, st.st_size + SHIFTDATA);
269   close(fd);
270   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
271
272   return 0;
273   
274 bail_truncate:
275   ftruncate(fd, st.st_size);
276 bail_open:
277   close(fd);
278 bail_lock:
279   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
280 bail_err:
281   return -1;
282 }
283 #endif /* AD_VERSION == AD_VERSION2 */
284
285 #ifdef ATACC
286 mode_t ad_hf_mode (mode_t mode)
287 {
288 #ifndef USE_FLOCK_LOCKS
289     /* fnctl lock need write access */
290     if ((mode & S_IRUSR))
291         mode |= S_IWUSR;
292     if ((mode & S_IRGRP))
293         mode |= S_IWGRP;
294     if ((mode & S_IROTH))
295         mode |= S_IWOTH;
296 #endif
297     /* if write mode set add read mode */
298     if ((mode & S_IWUSR))
299         mode |= S_IRUSR;
300     if ((mode & S_IWGRP))
301         mode |= S_IRGRP;
302     if ((mode & S_IWOTH))
303         mode |= S_IROTH;
304
305     return mode;
306 }
307
308 #endif
309
310 /* ------------------------------------- 
311   read in the entries 
312 */
313 static void parse_entries(struct adouble *ad, char *buf,
314                                     u_int16_t nentries)
315 {
316     u_int32_t           eid, len, off;
317
318     /* now, read in the entry bits */
319     for (; nentries > 0; nentries-- ) {
320         memcpy(&eid, buf, sizeof( eid ));
321         eid = ntohl( eid );
322         buf += sizeof( eid );
323         memcpy(&off, buf, sizeof( off ));
324         off = ntohl( off );
325         buf += sizeof( off );
326         memcpy(&len, buf, sizeof( len ));
327         len = ntohl( len );
328         buf += sizeof( len );
329
330         if ( 0 < eid && eid < ADEID_MAX ) {
331             ad->ad_eid[ eid ].ade_off = off;
332             ad->ad_eid[ eid ].ade_len = len;
333         } else {
334             LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d\n",
335                     nentries, eid );
336         }
337     }
338 }
339
340
341 /* this reads enough of the header so that we can figure out all of
342  * the entry lengths and offsets. once that's done, we just read/mmap
343  * the rest of the header in.
344  *
345  * NOTE: we're assuming that the resource fork is kept at the end of
346  *       the file. also, mmapping won't work for the hfs fs until it
347  *       understands how to mmap header files. */
348 static int ad_header_read(struct adouble *ad, struct stat *hst)
349 {
350     char                *buf = ad->ad_data;
351     u_int16_t           nentries;
352     int                 len;
353     ssize_t             header_len;
354     static int          warning = 0;
355     struct stat         st;
356
357     /* read the header */
358     if ((header_len = adf_pread( &ad->ad_hf, buf, sizeof(ad->ad_data), 0)) < 0) {
359         return -1;
360     }
361     if (header_len < AD_HEADER_LEN) {
362         errno = EIO;
363         return -1;
364     }
365
366     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
367     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
368
369     /* tag broken v1 headers. just assume they're all right. 
370      * we detect two cases: null magic/version
371      *                      byte swapped magic/version
372      * XXX: in the future, you'll need the v1compat flag. 
373      * (ad->ad_flags & ADFLAGS_V1COMPAT) */
374     if (!ad->ad_magic && !ad->ad_version) {
375       if (!warning) {
376         LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
377         warning++;
378       }
379       ad->ad_magic = AD_MAGIC;
380       ad->ad_version = AD_VERSION1;
381
382     } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
383       if (!warning) {
384         LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
385         warning++;
386       }
387
388     } else {
389       ad->ad_magic = ntohl( ad->ad_magic );
390       ad->ad_version = ntohl( ad->ad_version );
391     }
392
393     if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
394 #if AD_VERSION == AD_VERSION2
395                                        && (ad->ad_version != AD_VERSION2)
396 #endif /* AD_VERSION == AD_VERSION2 */
397                                        )) {
398       errno = EIO;
399       LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
400       return -1;
401     }
402
403     memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
404     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
405     nentries = ntohs( nentries );
406
407     /* read in all the entry headers. if we have more than the 
408      * maximum, just hope that the rfork is specified early on. */
409     len = nentries*AD_ENTRY_LEN;
410
411     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
412       len = sizeof(ad->ad_data) - AD_HEADER_LEN;
413
414     buf += AD_HEADER_LEN;
415     if (len > header_len - AD_HEADER_LEN) {
416         errno = EIO;
417         LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
418         return -1;
419     }
420
421     /* figure out all of the entry offsets and lengths. if we aren't
422      * able to read a resource fork entry, bail. */
423     parse_entries(ad, buf, nentries);
424     if (!ad_getentryoff(ad, ADEID_RFORK)
425         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
426         ) {
427       LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); 
428       return -1;
429     }
430
431     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
432         errno = EIO;
433         LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
434         return -1;
435     }
436     
437     if (hst == NULL) {
438         hst = &st;
439         if (fstat(ad->ad_hf.adf_fd, &st) < 0) {
440             return 1; /* fail silently */
441          }
442     }
443     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
444
445     /* fix up broken dates */
446     if (ad->ad_version == AD_VERSION1) {
447       u_int32_t aint;
448       
449       /* check to see if the ad date is wrong. just see if we have
450       * a modification date in the future. */
451       if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
452           (aint > TIMEWARP_DELTA + hst->st_mtime)) {
453         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
454         ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
455         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
456         ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
457         ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
458       }
459     }
460
461     return 0;
462 }
463
464
465 /*
466  * Put the .AppleDouble where it needs to be:
467  *
468  *          /   a/.AppleDouble/b
469  *      a/b
470  *          \   b/.AppleDouble/.Parent
471  */
472 char *
473 ad_path( path, adflags )
474     const char  *path;
475     int         adflags;
476 {
477     static char pathbuf[ MAXPATHLEN + 1];
478     char        c, *slash, buf[MAXPATHLEN + 1];
479
480     strncpy(buf, path, MAXPATHLEN);
481     if ( adflags & ADFLAGS_DIR ) {
482         strncpy( pathbuf, buf, MAXPATHLEN );
483         if ( *buf != '\0' ) {
484             strcat( pathbuf, "/" );
485         }
486         slash = ".Parent";
487     } else {
488         if (( slash = strrchr( buf, '/' )) != NULL ) {
489             c = *++slash;
490             *slash = '\0';
491             strncpy( pathbuf, buf, MAXPATHLEN);
492             *slash = c;
493         } else {
494             pathbuf[ 0 ] = '\0';
495             slash = buf;
496         }
497     }
498     strncat( pathbuf, ".AppleDouble/", MAXPATHLEN - strlen(pathbuf));
499     strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf));
500
501     return( pathbuf );
502 }
503
504 /*
505  * Support inherited protection modes for AppleDouble files.  The supplied
506  * mode is ANDed with the parent directory's mask value in lieu of "umask",
507  * and that value is returned.
508  */
509
510 #define DEFMASK 07700   /* be conservative */
511
512 char 
513 *ad_dir(path)
514     const char          *path;
515 {
516     static char         modebuf[ MAXPATHLEN + 1];
517     char                *slash;
518
519     if ( strlen( path ) >= MAXPATHLEN ) {
520         return NULL;  /* can't do it */
521     }
522
523     /*
524      * For a path with directories in it, remove the final component
525      * (path or subdirectory name) to get the name we want to stat.
526      * For a path which is just a filename, use "." instead.
527      */
528     strcpy( modebuf, path );
529     if (( slash = strrchr( modebuf, '/' )) != NULL ) {
530         *slash = '\0';          /* remove pathname component */
531     } else {
532         modebuf[0] = '.';       /* use current directory */
533         modebuf[1] = '\0';
534     }
535     return modebuf;
536 }
537
538 int
539 ad_mode( path, mode )
540     const char          *path;
541     int                 mode;
542 {
543     struct stat         stbuf;
544     char                *p;
545     
546     if ( mode == 0 ) {
547         return( mode );         /* save on syscalls */
548     }
549     p = ad_dir(path);
550     if (!p) {
551         return( mode & DEFMASK );  /* can't do it */
552     }
553
554     if ( stat( p, &stbuf ) != 0 ) {
555         return( mode & DEFMASK );       /* bail out... can't stat dir? */
556     }
557
558     return( mode & stbuf.st_mode );
559 }
560
561 /*
562  * Use mkdir() with mode bits taken from ad_mode().
563  */
564 int
565 ad_mkdir( path, mode )
566     const char          *path;
567     int                 mode;
568 {
569 #ifdef DEBUG
570     LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
571 #endif /* DEBUG */
572     return mkdir( path, ad_mode( path, mode ) );
573 }
574
575
576 static int new_rfork(const char *path, struct adouble *ad, int adflags);
577
578 #ifdef  HAVE_PREAD
579 #define AD_SET(a) 
580 #else 
581 #define AD_SET(a) a = 0
582 #endif
583 /*
584  * It's not possible to open the header file O_RDONLY -- the read
585  * will fail and return an error. this refcounts things now. 
586  */
587 int ad_open( path, adflags, oflags, mode, ad )
588     const char          *path;
589     int                 adflags, oflags, mode;
590     struct adouble      *ad;
591 {
592     struct stat         st;
593     char                *slash, *ad_p;
594     int                 hoflags, admode;
595
596     if (ad->ad_inited != AD_INITED) {
597         ad_dfileno(ad) = -1;
598         ad_hfileno(ad) = -1;
599         adf_lock_init(&ad->ad_df);
600         adf_lock_init(&ad->ad_hf);
601         ad->ad_inited = AD_INITED;
602         ad->ad_refcount = 1;
603     }
604
605     if ((adflags & ADFLAGS_DF)) { 
606         if (ad_dfileno(ad) == -1) {
607           hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
608           admode = ad_mode( path, mode ); 
609           if (( ad->ad_df.adf_fd = open( path, hoflags, admode )) < 0 ) {
610              if (errno == EACCES && !(oflags & O_RDWR)) {
611                 hoflags = oflags;
612                 ad->ad_df.adf_fd =open( path, hoflags, admode );
613              }
614           }
615           if ( ad->ad_df.adf_fd < 0)
616                 return -1;
617
618           AD_SET(ad->ad_df.adf_off);
619           ad->ad_df.adf_flags = hoflags;
620         } 
621         else {
622             /* the file is already open... but */
623             if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
624                 !(ad->ad_df.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
625             {
626                  errno = EACCES;
627                  return -1;
628             }
629         }
630         ad->ad_df.adf_refcount++;
631     }
632
633     if (!(adflags & ADFLAGS_HF)) 
634         return 0;
635         
636     if (ad_hfileno(ad) != -1) { /* the file is already open */
637         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
638                 !(ad->ad_hf.adf_flags & ( O_RDWR | O_WRONLY))) {
639             if (adflags & ADFLAGS_DF) {
640                 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
641                 ad_close( ad, ADFLAGS_DF );
642             }
643             errno = EACCES;
644             return -1;
645         }
646         ad_refresh(ad);
647         ad->ad_hf.adf_refcount++;
648         return 0;
649     }
650
651     ad_p = ad_path( path, adflags );
652
653     hoflags = oflags & ~O_CREAT;
654     hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
655     if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 )) < 0 ) {
656         if (errno == EACCES && !(oflags & O_RDWR)) {
657             hoflags = oflags & ~O_CREAT;
658             ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 );
659         }    
660     }
661
662     if ( ad->ad_hf.adf_fd < 0 ) { 
663         if (errno == ENOENT && (oflags & O_CREAT) ) {
664             /*
665              * We're expecting to create a new adouble header file,
666              * here.
667              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
668              */
669             admode = ad_hf_mode(ad_mode( ad_p, mode )); 
670             errno = 0;
671             if (( ad->ad_hf.adf_fd = open( ad_p, oflags,admode )) < 0 ) {
672                 /*
673                  * Probably .AppleDouble doesn't exist, try to
674                  * mkdir it.
675                  */
676                 if (errno == ENOENT && (adflags & ADFLAGS_NOADOUBLE) == 0) {
677                     if (( slash = strrchr( ad_p, '/' )) == NULL ) {
678                         ad_close( ad, adflags );
679                         return( -1 );
680                     }
681                     *slash = '\0';
682                     errno = 0;
683                     if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
684                         ad_close( ad, adflags );
685                         return( -1 );
686                     }
687                     *slash = '/';
688                     if (( ad->ad_hf.adf_fd = 
689                                    open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
690                         ad_close( ad, adflags );
691                         return( -1 );
692                     }
693                 } else {
694                   ad_close( ad, adflags );
695                   return( -1 );
696                 }
697             }
698             ad->ad_hf.adf_flags = oflags;
699         } else {
700             ad_close( ad, adflags );
701             return( -1 );
702         }
703     } else if (fstat(ad->ad_hf.adf_fd, &st) == 0 && st.st_size == 0) {
704         /* for 0 length files, treat them as new. */
705         ad->ad_hf.adf_flags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR | O_TRUNC;
706     } else {
707         ad->ad_hf.adf_flags = hoflags;
708     }
709     AD_SET(ad->ad_hf.adf_off);
710           
711     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
712     if ((ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT ))) {
713         /*
714          * This is a new adouble header file. Initialize the structure,
715          * instead of reading it.
716         */
717         if (new_rfork(path, ad, adflags) < 0) {
718             ad_close(ad, adflags);
719             return -1;
720         }
721     } else {
722             /* Read the adouble header in and parse it.*/
723         if ((ad_header_read( ad , &st) < 0)
724 #if AD_VERSION == AD_VERSION2
725                 || (ad_v1tov2(ad, ad_p) < 0)
726 #endif /* AD_VERSION == AD_VERSION2 */
727         ) {
728             ad_close( ad, adflags );
729             return( -1 );
730         }
731     }
732     ad->ad_hf.adf_refcount++;
733     return 0 ;
734 }
735
736 /* ----------------------------------- */
737 static int new_rfork(const char *path, struct adouble *ad, int adflags)
738 {
739 #if 0
740     struct timeval      tv;
741 #endif    
742     const struct entry  *eid;
743     u_int16_t           ashort;
744     struct stat         st;
745
746     ad->ad_magic = AD_MAGIC;
747     ad->ad_version = AD_VERSION;
748
749     memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
750     memset(ad->ad_data, 0, sizeof(ad->ad_data));
751
752     eid = entry_order;
753     while (eid->id) {
754         ad->ad_eid[eid->id].ade_off = eid->offset;
755         ad->ad_eid[eid->id].ade_len = eid->len;
756         eid++;
757     }
758             
759     /* put something sane in the directory finderinfo */
760     if (adflags & ADFLAGS_DIR) {
761         /* set default view */
762         ashort = htons(FINDERINFO_CLOSEDVIEW);
763         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
764                      &ashort, sizeof(ashort));
765     } else {
766         /* set default creator/type fields */
767         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"TEXT", 4);
768         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"UNIX", 4);
769     }
770
771     /* make things invisible */
772     if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
773         ashort = htons(ATTRBIT_INVISIBLE);
774         ad_setattr(ad, ashort);
775         ashort = htons(FINDERINFO_INVISIBLE);
776         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
777                      &ashort, sizeof(ashort));
778     }
779
780 #if 0
781     if (gettimeofday(&tv, NULL) < 0) {
782         return -1;
783     } 
784 #endif
785     
786     if (stat(path, &st) < 0) {
787         return -1;
788     }
789             
790     /* put something sane in the date fields */
791     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
792     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
793     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
794     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
795     return 0;
796 }
797
798 /* to do this with mmap, we need the hfs fs to understand how to mmap
799    header files. */
800 int ad_refresh(struct adouble *ad)
801 {
802
803   if (ad->ad_hf.adf_fd < -1)
804     return -1;
805
806   return ad_header_read(ad, NULL);
807 }