]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
AppleDouble directory creation debugging
[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 #ifdef DEBUG
573     syslog (LOG_INFO, "ad_mkdir: Creating directory with mode %d", mode);
574 #endif
575     return mkdir( path, ad_mode( path, mode ) );
576 }
577
578
579 /*
580  * It's not possible to open the header file O_RDONLY -- the read
581  * will fail and return an error. this refcounts things now. 
582  */
583 int ad_open( path, adflags, oflags, mode, ad )
584     char                *path;
585     int                 adflags, oflags, mode;
586     struct adouble      *ad;
587 {
588     const struct entry  *eid;
589     struct stat         st;
590     char                *slash, *ad_p;
591     int                 hoflags, admode;
592     u_int16_t           ashort;
593
594     if (ad->ad_inited != AD_INITED) {
595         ad_dfileno(ad) = -1;
596         ad_hfileno(ad) = -1;
597         adf_lock_init(&ad->ad_df);
598         adf_lock_init(&ad->ad_hf);
599 #ifdef USE_MMAPPED_HEADERS
600         ad->ad_data = MAP_FAILED;
601 #endif
602         ad->ad_inited = AD_INITED;
603     }
604
605     if (adflags & ADFLAGS_DF) { 
606         if (ad_dfileno(ad) == -1) {
607           if (( ad->ad_df.adf_fd =
608                 open( path, oflags, ad_mode( path, mode ) )) < 0 ) {
609             return( -1 );
610           }
611           ad->ad_df.adf_off = 0;
612           ad->ad_df.adf_flags = oflags;
613         } 
614         ad->ad_df.adf_refcount++;
615     }
616
617     if (adflags & ADFLAGS_HF) {
618         if (ad_hfileno(ad) == -1) {
619           ad_p = ad_path( path, adflags );
620           admode = ad_mode( ad_p, O_RDWR );
621           
622           hoflags = oflags & ~O_CREAT;
623           if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, admode )) < 0 ) {
624             if ( errno == ENOENT && hoflags != oflags ) {
625               /*
626                * We're expecting to create a new adouble header file,
627                * here.
628                */
629               errno = 0;
630               if (( ad->ad_hf.adf_fd = open( ad_p, oflags,
631                                              admode )) < 0 ) {
632                 /*
633                  * Probably .AppleDouble doesn't exist, try to
634                  * mkdir it.
635                  */
636                 if ((errno == ENOENT) && 
637                     ((adflags & ADFLAGS_NOADOUBLE) == 0)) {
638                   if (( slash = strrchr( ad_p, '/' )) == NULL ) {
639                     ad_close( ad, adflags );
640                     return( -1 );
641                   }
642                   *slash = '\0';
643                   errno = 0;
644                   if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
645                     ad_close( ad, adflags );
646                     return( -1 );
647                   }
648                   *slash = '/';
649                   if (( ad->ad_hf.adf_fd = 
650                         open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
651                     ad_close( ad, adflags );
652                     return( -1 );
653                   }
654                 } else {
655                   ad_close( ad, adflags );
656                   return( -1 );
657                 }
658               }
659               ad->ad_hf.adf_flags = oflags;
660             } else {
661               ad_close( ad, adflags );
662               return( -1 );
663             }
664           } else if ((fstat(ad->ad_hf.adf_fd, &st) == 0) && 
665                      (st.st_size == 0)) {
666             /* for 0 length files, treat them as new. */
667             ad->ad_hf.adf_flags = oflags;
668           } else {
669             ad->ad_hf.adf_flags = hoflags;
670           }
671           ad->ad_hf.adf_off = 0;
672           
673           /*
674            * This is a new adouble header file. Initialize the structure,
675            * instead of reading it.
676            */
677           memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
678           if ( ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT )) {
679             struct timeval tv;
680
681             ad->ad_magic = AD_MAGIC;
682             ad->ad_version = AD_VERSION;
683             memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
684
685 #ifdef USE_MMAPPED_HEADERS
686             /* truncate the header file and mmap it. */
687             ftruncate(ad->ad_hf.adf_fd, AD_DATASZ);
688             ad->ad_data = mmap(NULL, AD_DATASZ, PROT_READ | PROT_WRITE,
689                                MAP_SHARED, ad->ad_hf.adf_fd, 0);
690             if (ad->ad_data == MAP_FAILED) {
691               ad_close(ad, adflags);
692               return -1;
693             }       
694 #else
695             memset(ad->ad_data, 0, sizeof(ad->ad_data));
696 #endif
697
698             eid = entry_order;
699             while (eid->id) {
700               ad->ad_eid[eid->id].ade_off = eid->offset;
701               ad->ad_eid[eid->id].ade_len = eid->len;
702               eid++;
703             }
704             
705             /* put something sane in the directory finderinfo */
706             if (adflags & ADFLAGS_DIR) {
707               /* set default view */
708               ashort = htons(FINDERINFO_CLOSEDVIEW);
709               memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
710                      &ashort, sizeof(ashort));
711             } else {
712               /* set default creator/type fields */
713               memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,
714                      "TEXT", 4);
715               memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,
716                      "UNIX", 4);
717             }
718
719             /* make things invisible */
720             if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
721               ashort = htons(ATTRBIT_INVISIBLE);
722               ad_setattr(ad, ashort);
723               ashort = htons(FINDERINFO_INVISIBLE);
724               memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
725                      &ashort, sizeof(ashort));
726             }
727
728             if (gettimeofday(&tv, NULL) < 0) {
729               ad_close(ad, adflags);
730               return -1;
731             }
732             
733             /* put something sane in the date fields */
734             ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, tv.tv_sec);
735             ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, tv.tv_sec);
736             ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
737             ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
738
739           } else {
740             /*
741              * Read the adouble header in and parse it.
742              */
743             if ((ad_header_read( ad ) < 0)
744 #if AD_VERSION == AD_VERSION2
745                 || (ad_v1tov2(ad, ad_p) < 0)
746 #endif
747                 ) {
748               ad_close( ad, adflags );
749               return( -1 );
750             }
751           }
752         }
753         ad->ad_hf.adf_refcount++;
754     }
755         
756     return( 0 );
757 }
758
759 /* to do this with mmap, we need the hfs fs to understand how to mmap
760    header files. */
761 int ad_refresh(struct adouble *ad)
762 {
763 #ifdef USE_MMAPPED_HEADERS
764   off_t off;
765 #endif
766
767   if (ad->ad_hf.adf_fd < -1)
768     return -1;
769
770 #ifdef USE_MMAPPED_HEADERS
771   if (ad->ad_data == MAP_FAILED)
772     return -1;
773   
774   /* re-read the header */
775   off = ad_getentryoff(ad, ADEID_RFORK);
776   memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof(nentries));
777   nentries = ntohs(nentries);
778   parse_entries(ad, ad->ad_data + AD_HEADER_LEN, nentries);
779
780   /* check to see if something screwy happened */
781   if (!ad_getentryoff(ad, ADEID_RFORK))
782     return -1;
783
784   /* if there's a length discrepancy, remap the header. this shouldn't
785    * really ever happen. */
786   if (off != ad_getentryoff(ad, ADEID_RFORK)) {
787     char *buf = ad->ad_data;
788     buf = ad->ad_data;
789     ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK), 
790                        PROT_READ | PROT_WRITE,
791                        (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? 
792                        MAP_SHARED : MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
793     if (ad->ad_data == MAP_FAILED) {
794       ad->ad_data = buf;
795       return -1;
796     }
797     munmap(buf, off);
798   }
799   return 0;
800
801 #else
802   return ad_header_read(ad);
803 #endif
804 }