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