]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
big merge for db frontend and unicode.
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * $Id: ad_open.c,v 1.30.6.1 2003-09-09 16:42:21 didg 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  * NOTE: I don't use inline because a good compiler should be
27  * able to optimize all the static below. Didier
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif /* HAVE_CONFIG_H */
33
34 #include <string.h>
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif /* HAVE_FCNTL_H */
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif /* HAVE_UNISTD_H */
41 #include <errno.h>
42 #include <atalk/logger.h>
43
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/param.h>
48 #include <sys/mman.h>
49
50 #include <netatalk/endian.h>
51 #include <atalk/adouble.h>
52
53 #include "ad_private.h"
54
55 #ifndef MAX
56 #define MAX(a, b)  ((a) < (b) ? (b) : (a))
57 #endif /* ! MAX */
58
59 /*
60  * AppleDouble entry default offsets.
61  * The layout looks like this:
62  *
63  * this is the v1 layout:
64  *        255     200             16      32              N
65  *      |  NAME |    COMMENT    | FILEI |    FINDERI    | RFORK |
66  *
67  * we need to change it to look like this:
68  *
69  * v2 layout:
70  * field       length (in bytes)
71  * NAME        255
72  * COMMENT     200
73  * FILEDATESI  16     replaces FILEI
74  * FINDERI     32  
75  * DID          4     new
76  * AFPFILEI     4     new
77  * SHORTNAME   12     8.3 new
78  * RFORK        N
79  * 
80  * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
81  * and add in the new fields.
82  *
83  * NOTE: the HFS module will need similar modifications to interact with
84  * afpd correctly.
85  */
86  
87 #define ADEDOFF_MAGIC        (0)
88 #define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
89 #define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
90 #define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
91
92 /* initial lengths of some of the fields */
93 #define ADEDLEN_INIT     0
94
95 /* make sure we don't redefine ADEDOFF_FILEI */
96 #ifdef ADEDOFF_FILEI
97 #undef ADEDOFF_FILEI
98 #endif /* ADEDOFF_FILEI */
99
100 #define ADEDOFF_NAME_V1      (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
101 #define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
102 #define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
103 #define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
104 #define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
105
106 /* i stick things in a slightly different order than their eid order in 
107  * case i ever want to separate RootInfo behaviour from the rest of the 
108  * stuff. */
109 #define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
110 #define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
111 #define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
112 #define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
113 #define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
114 #define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
115 #define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
116 #define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
117 #define ADEDOFF_PRIVDEV      (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
118 #define ADEDOFF_PRIVINO      (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
119 #define ADEDOFF_PRIVSYN      (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
120
121 #define ADEDOFF_RFORK_V2     (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
122
123 /* we keep local copies of a bunch of stuff so that we can initialize things 
124  * correctly. */
125
126 /* Bits in the finderinfo data. 
127  * see etc/afpd/{directory.c,file.c} for the finderinfo structure
128  * layout. */
129 #define FINDERINFO_CUSTOMICON 0x4
130 #define FINDERINFO_CLOSEDVIEW 0x100
131
132 /* offsets in finderinfo */
133 #define FINDERINFO_FRTYPEOFF   0
134 #define FINDERINFO_FRCREATOFF  4
135 #define FINDERINFO_FRFLAGOFF   8
136 #define FINDERINFO_FRVIEWOFF  14
137
138 /* invisible bit for dot files */
139 #define ATTRBIT_INVISIBLE     (1 << 0)
140 #define FINDERINFO_INVISIBLE  (1 << 14)
141
142 /* this is to prevent changing timezones from causing problems with
143    localtime volumes. the screw-up is 30 years. we use a delta of 5
144    years.  */
145 #define TIMEWARP_DELTA 157680000
146
147
148 struct entry {
149   u_int32_t id, offset, len;
150 };
151
152 static const struct entry entry_order1[ADEID_NUM_V1 +1] = {
153   {ADEID_NAME,    ADEDOFF_NAME_V1,    ADEDLEN_INIT},      /* 3 */
154   {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},      /* 4 */
155   {ADEID_FILEI,   ADEDOFF_FILEI,      ADEDLEN_FILEI},     /* 7 */
156   {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},   /* 9 */
157   {ADEID_RFORK,   ADEDOFF_RFORK_V1,   ADEDLEN_INIT},      /* 2 */
158   {0, 0, 0}
159 };
160
161 #if AD_VERSION == AD_VERSION1 
162 #define DISK_EID(ad, a) (a)
163
164 #else /* AD_VERSION == AD_VERSION2 */
165
166 static u_int32_t get_eid(struct adouble *ad, u_int32_t eid) 
167 {
168     if (eid <= 15)
169         return eid;
170     if (ad->ad_version == AD_VERSION1)
171         return 0;
172     if (eid == AD_DEV)
173         return ADEID_PRIVDEV;
174     if (eid == AD_INO)
175         return ADEID_PRIVINO;
176     if (eid == AD_SYN)
177         return ADEID_PRIVSYN;
178
179     return 0;
180 }
181
182 #define DISK_EID(ad, a) get_eid(ad, a)
183
184 static const struct entry entry_order2[ADEID_NUM_V2 +1] = {
185   {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
186   {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
187   {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
188   {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
189   {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
190   {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
191   {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
192   {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
193   {ADEID_PRIVDEV,     ADEDOFF_PRIVDEV, ADEDLEN_INIT},
194   {ADEID_PRIVINO,     ADEDOFF_PRIVINO, ADEDLEN_INIT},
195   {ADEID_PRIVSYN,     ADEDOFF_PRIVSYN, ADEDLEN_INIT},
196   {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
197
198   {0, 0, 0}
199 };
200 #endif /* AD_VERSION == AD_VERSION2 */
201
202 #if AD_VERSION == AD_VERSION2
203
204 /* FIXME work only if < 2GB */
205 static int ad_v1tov2(struct adouble *ad, const char *path)
206 {
207   struct stat st;
208   u_int16_t attr;
209   char *buf;
210   int fd, off;
211   
212   /* check to see if we should convert this header. */
213   if (!path || (ad->ad_version != AD_VERSION1))
214     return 0;
215
216   /* we want version1 anyway */
217   if (ad->ad_flags == AD_VERSION1)
218       return 0;
219
220
221   if (!ad->ad_flags) {
222       /* we don't really know what we want */
223       ad->ad_flags = ad->ad_version;
224       return 0;
225   }
226
227   /* convert from v1 to v2. what does this mean?
228    *  1) change FILEI into FILEDATESI
229    *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
230    *  3) move FILEI attributes into AFPFILEI
231    *  4) initialize ACCESS field of FILEDATESI.
232    *
233    *  so, we need 4*12 (entry ids) + 12 (shortname) + 4 (afpfilei) +
234    *  4 (did) + 8 (prodosi) = 76 more bytes.  */
235   
236 #define SHIFTDATA (AD_DATASZ2 - AD_DATASZ1)
237
238   /* bail if we can't get a lock */
239   if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) 
240     goto bail_err;
241   
242   if ((fd = open(path, O_RDWR)) < 0) 
243     goto bail_lock;
244   
245   if (fstat(fd, &st) ||
246       ftruncate(fd, st.st_size + SHIFTDATA) < 0) {
247     goto bail_open;
248   }
249   if (st.st_size > 0x7fffffff) {
250       LOG(log_debug, logtype_default, "ad_v1tov2: file too big."); 
251       goto bail_truncate;
252   }
253   
254   /* last place for failure. */
255   if ((void *) (buf = (char *) 
256                 mmap(NULL, st.st_size + SHIFTDATA,
257                      PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 
258           MAP_FAILED) {
259     goto bail_truncate;
260   }
261   
262   off = ad->ad_eid[ADEID_RFORK].ade_off;
263
264   /* move the RFORK. this assumes that the RFORK is at the end */
265   memmove(buf + off + SHIFTDATA, buf + off, 
266           ad->ad_eid[ADEID_RFORK].ade_len);
267   
268   munmap(buf, st.st_size + SHIFTDATA);
269   close(fd);
270
271   /* now, fix up our copy of the header */
272   memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
273   
274   /* replace FILEI with FILEDATESI */
275   ad_getattr(ad, &attr);
276   ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
277   ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
278   ad->ad_eid[ADEID_FILEI].ade_off = 0;
279   ad->ad_eid[ADEID_FILEI].ade_len = 0;
280   
281   /* add in the new entries */
282   ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
283   ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
284   ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
285   ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
286   ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
287   ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
288   ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
289   ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
290   
291   ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV;
292   ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT;
293   ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO;
294   ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT;
295   ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN;
296   ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT;
297   
298   /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
299   ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
300   ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
301   ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
302   ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
303   
304   /* switch to v2 */
305   ad->ad_version = AD_VERSION2;
306   
307   /* move our data buffer to make space for the new entries. */
308   memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1,
309           ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
310   
311   /* now, fill in the space with appropriate stuff. we're
312      operating as a v2 file now. */
313   ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
314   memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
315   memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
316   ad_setattr(ad, attr);
317   memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
318   memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
319   
320   /* rebuild the header and cleanup */
321   ad_flush(ad, ADFLAGS_HF );
322   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
323
324   return 0;
325   
326 bail_truncate:
327   ftruncate(fd, st.st_size);
328 bail_open:
329   close(fd);
330 bail_lock:
331   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
332 bail_err:
333   return -1;
334 }
335 #endif /* AD_VERSION == AD_VERSION2 */
336
337 #ifdef ATACC
338 mode_t ad_hf_mode (mode_t mode)
339 {
340     /* fnctl lock need write access */
341     if ((mode & S_IRUSR))
342         mode |= S_IWUSR;
343     if ((mode & S_IRGRP))
344         mode |= S_IWGRP;
345     if ((mode & S_IROTH))
346         mode |= S_IWOTH;
347     /* if write mode set add read mode */
348     if ((mode & S_IWUSR))
349         mode |= S_IRUSR;
350     if ((mode & S_IWGRP))
351         mode |= S_IRGRP;
352     if ((mode & S_IWOTH))
353         mode |= S_IROTH;
354
355     return mode;
356 }
357
358 #endif
359
360 /* ------------------------------------- 
361   read in the entries 
362 */
363 static void parse_entries(struct adouble *ad, char *buf,
364                                     u_int16_t nentries)
365 {
366     u_int32_t   eid, len, off;
367     int         warning = 0;
368
369     /* now, read in the entry bits */
370     for (; nentries > 0; nentries-- ) {
371         memcpy(&eid, buf, sizeof( eid ));
372         eid = DISK_EID(ad, ntohl( eid ));
373         buf += sizeof( eid );
374         memcpy(&off, buf, sizeof( off ));
375         off = ntohl( off );
376         buf += sizeof( off );
377         memcpy(&len, buf, sizeof( len ));
378         len = ntohl( len );
379         buf += sizeof( len );
380
381         if ( 0 < eid && eid < ADEID_MAX ) {
382             ad->ad_eid[ eid ].ade_off = off;
383             ad->ad_eid[ eid ].ade_len = len;
384         } else if (!warning) {
385             warning = 1;
386             LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d\n",
387                     nentries, eid );
388         }
389     }
390 }
391
392
393 /* this reads enough of the header so that we can figure out all of
394  * the entry lengths and offsets. once that's done, we just read/mmap
395  * the rest of the header in.
396  *
397  * NOTE: we're assuming that the resource fork is kept at the end of
398  *       the file. also, mmapping won't work for the hfs fs until it
399  *       understands how to mmap header files. */
400 static int ad_header_read(struct adouble *ad, struct stat *hst)
401 {
402     char                *buf = ad->ad_data;
403     u_int16_t           nentries;
404     int                 len;
405     ssize_t             header_len;
406     static int          warning = 0;
407     struct stat         st;
408
409     /* read the header */
410     if ((header_len = adf_pread( &ad->ad_hf, buf, sizeof(ad->ad_data), 0)) < 0) {
411         return -1;
412     }
413     if (header_len < AD_HEADER_LEN) {
414         errno = EIO;
415         return -1;
416     }
417
418     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
419     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
420
421     /* tag broken v1 headers. just assume they're all right. 
422      * we detect two cases: null magic/version
423      *                      byte swapped magic/version
424      * XXX: in the future, you'll need the v1compat flag. 
425      * (ad->ad_flags & ADFLAGS_V1COMPAT) */
426     if (!ad->ad_magic && !ad->ad_version) {
427       if (!warning) {
428         LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
429         warning++;
430       }
431       ad->ad_magic = AD_MAGIC;
432       ad->ad_version = AD_VERSION1;
433
434     } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
435       if (!warning) {
436         LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
437         warning++;
438       }
439
440     } else {
441       ad->ad_magic = ntohl( ad->ad_magic );
442       ad->ad_version = ntohl( ad->ad_version );
443     }
444
445     if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
446 #if AD_VERSION == AD_VERSION2
447                                        && (ad->ad_version != AD_VERSION2)
448 #endif /* AD_VERSION == AD_VERSION2 */
449                                        )) {
450       errno = EIO;
451       LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
452       return -1;
453     }
454
455     memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
456     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
457     nentries = ntohs( nentries );
458
459     /* read in all the entry headers. if we have more than the 
460      * maximum, just hope that the rfork is specified early on. */
461     len = nentries*AD_ENTRY_LEN;
462
463     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
464       len = sizeof(ad->ad_data) - AD_HEADER_LEN;
465
466     buf += AD_HEADER_LEN;
467     if (len > header_len - AD_HEADER_LEN) {
468         errno = EIO;
469         LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
470         return -1;
471     }
472
473     /* figure out all of the entry offsets and lengths. if we aren't
474      * able to read a resource fork entry, bail. */
475     nentries = len / AD_ENTRY_LEN;
476     parse_entries(ad, buf, nentries);
477     if (!ad_getentryoff(ad, ADEID_RFORK)
478         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
479         ) {
480       LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); 
481       return -1;
482     }
483
484     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
485         errno = EIO;
486         LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
487         return -1;
488     }
489     
490     if (hst == NULL) {
491         hst = &st;
492         if (fstat(ad->ad_hf.adf_fd, &st) < 0) {
493             return 1; /* fail silently */
494          }
495     }
496     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
497
498     /* fix up broken dates */
499     if (ad->ad_version == AD_VERSION1) {
500       u_int32_t aint;
501       
502       /* check to see if the ad date is wrong. just see if we have
503       * a modification date in the future. */
504       if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
505           (aint > TIMEWARP_DELTA + hst->st_mtime)) {
506         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
507         ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
508         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
509         ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
510         ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
511       }
512     }
513
514     return 0;
515 }
516
517
518 /*
519  * Put the .AppleDouble where it needs to be:
520  *
521  *          /   a/.AppleDouble/b
522  *      a/b
523  *          \   b/.AppleDouble/.Parent
524  */
525 char *
526 ad_path( path, adflags )
527     const char  *path;
528     int         adflags;
529 {
530     static char pathbuf[ MAXPATHLEN + 1];
531     char        c, *slash, buf[MAXPATHLEN + 1];
532
533     strncpy(buf, path, MAXPATHLEN);
534     if ( adflags & ADFLAGS_DIR ) {
535         strncpy( pathbuf, buf, MAXPATHLEN );
536         if ( *buf != '\0' ) {
537             strcat( pathbuf, "/" );
538         }
539         slash = ".Parent";
540     } else {
541         if (NULL != ( slash = strrchr( buf, '/' )) ) {
542             c = *++slash;
543             *slash = '\0';
544             strncpy( pathbuf, buf, MAXPATHLEN);
545             *slash = c;
546         } else {
547             pathbuf[ 0 ] = '\0';
548             slash = buf;
549         }
550     }
551     strncat( pathbuf, ".AppleDouble/", MAXPATHLEN - strlen(pathbuf));
552     strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf));
553
554     return( pathbuf );
555 }
556
557 /*
558  * Support inherited protection modes for AppleDouble files.  The supplied
559  * mode is ANDed with the parent directory's mask value in lieu of "umask",
560  * and that value is returned.
561  */
562
563 #define DEFMASK 07700   /* be conservative */
564
565 char 
566 *ad_dir(path)
567     const char          *path;
568 {
569     static char         modebuf[ MAXPATHLEN + 1];
570     char                *slash;
571     size_t              len;
572
573     if ( (len = strlen( path )) >= MAXPATHLEN ) {
574         errno = ENAMETOOLONG;
575         return NULL;  /* can't do it */
576     }
577
578     /*
579      * For a path with directories in it, remove the final component
580      * (path or subdirectory name) to get the name we want to stat.
581      * For a path which is just a filename, use "." instead.
582      */
583     strcpy( modebuf, path );
584     slash = strrchr( modebuf, '/' );
585     /* is last char a '/' */
586     if (slash && slash[1] == 0) {
587         while (modebuf < slash && slash[-1] == '/') {
588             --slash;
589         }
590         if (modebuf < slash) {
591             *slash = '\0';              /* remove pathname component */
592             slash = strrchr( modebuf, '/' );
593         }
594     }
595     if (slash) {
596         *slash = '\0';          /* remove pathname component */
597     } else {
598         modebuf[0] = '.';       /* use current directory */
599         modebuf[1] = '\0';
600     }
601     return modebuf;
602 }
603
604 /* ---------------- */
605 static uid_t default_uid = -1;
606
607 int ad_setfuid(const uid_t id)
608 {
609     default_uid = id;
610     return 0;
611 }
612
613 /* ---------------- */
614 uid_t ad_getfuid(void) 
615 {
616     return default_uid;
617 }
618
619 /* ---------------- 
620    return inode of path parent directory
621 */
622 int ad_stat(const char *path, struct stat *stbuf)
623 {
624     char                *p;
625
626     p = ad_dir(path);
627     if (!p) {
628         return -1;
629     }
630
631     return stat( p, stbuf );
632 }
633
634 /* ---------------- 
635    if we are root change path user/ group
636    It can be a native function for BSD cf. FAQ.Q10
637    path:  pathname to chown 
638    stbuf: parent directory inode
639    
640    use fstat and fchown or lchown with linux?
641 */
642 #define EMULATE_SUIDDIR
643  
644 static int ad_chown(const char *path, struct stat *stbuf)
645 {
646 int ret = 0;
647 #ifdef EMULATE_SUIDDIR
648 uid_t id;
649
650     if (default_uid != -1) {  
651         /* we are root (admin) */
652         id = (default_uid)?default_uid:stbuf->st_uid;
653         ret = chown( path, id, stbuf->st_gid );
654     }
655 #endif    
656     return ret;
657 }
658
659 /* ---------------- 
660    return access right and inode of path parent directory
661 */
662 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
663 {
664     if (*mode == 0) {
665        return -1;
666     }
667     if (ad_stat(path, stbuf) != 0) {
668         *mode &= DEFMASK;
669         return -1;
670     }
671     *mode &= stbuf->st_mode;
672     return 0;    
673 }
674
675 /* ---------------- 
676    return access right of path parent directory
677 */
678 int
679 ad_mode( path, mode )
680     const char          *path;
681     int                 mode;
682 {
683     struct stat         stbuf;
684     ad_mode_st(path, &mode, &stbuf);
685     return mode;
686 }
687
688 /*
689  * Use mkdir() with mode bits taken from ad_mode().
690  */
691 int
692 ad_mkdir( path, mode )
693     const char          *path;
694     int                 mode;
695 {
696 int ret;
697 int st_invalid;
698 struct stat stbuf;
699
700 #ifdef DEBUG
701     LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
702 #endif /* DEBUG */
703
704     st_invalid = ad_mode_st(path, &mode, &stbuf);
705     ret = mkdir( path, mode );
706     if (ret || st_invalid)
707         return ret;
708     ad_chown(path, &stbuf);
709
710     return ret;    
711 }
712
713 /* ----------------- */
714 static int ad_error(struct adouble *ad, int adflags)
715 {
716     if ((adflags & ADFLAGS_NOHF)) {
717         /* FIXME double check : set header offset ?*/
718         return 0;
719     }
720     if ((adflags & ADFLAGS_DF)) {
721         ad_close( ad, ADFLAGS_DF );
722     }
723     return -1 ;
724 }
725
726 static int new_rfork(const char *path, struct adouble *ad, int adflags);
727
728 #ifdef  HAVE_PREAD
729 #define AD_SET(a) 
730 #else 
731 #define AD_SET(a) a = 0
732 #endif
733
734 void ad_init(struct adouble *ad, int flags)
735 {
736     memset( ad, 0, sizeof( struct adouble ) );
737     ad->ad_flags = flags;
738 }
739
740 /* -------------------
741  * It's not possible to open the header file O_RDONLY -- the read
742  * will fail and return an error. this refcounts things now. 
743  */
744 int ad_open( path, adflags, oflags, mode, ad )
745     const char          *path;
746     int                 adflags, oflags, mode;
747     struct adouble      *ad;
748 {
749     struct stat         st;
750     char                *slash, *ad_p;
751     int                 hoflags, admode;
752     int                 st_invalid;
753     int                 open_df = 0;
754     
755     if (ad->ad_inited != AD_INITED) {
756         ad_dfileno(ad) = -1;
757         ad_hfileno(ad) = -1;
758         adf_lock_init(&ad->ad_df);
759         adf_lock_init(&ad->ad_hf);
760         ad->ad_inited = AD_INITED;
761         ad->ad_refcount = 1;
762     }
763
764     if ((adflags & ADFLAGS_DF)) { 
765         if (ad_dfileno(ad) == -1) {
766           hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
767           admode = mode;
768           st_invalid = ad_mode_st(path, &admode, &st);
769           ad->ad_df.adf_fd =open( path, hoflags, admode );
770           if (ad->ad_df.adf_fd < 0 ) {
771              if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
772                 hoflags = oflags;
773                 ad->ad_df.adf_fd =open( path, hoflags, admode );
774              }
775           }
776           if ( ad->ad_df.adf_fd < 0)
777                 return -1;
778
779           AD_SET(ad->ad_df.adf_off);
780           ad->ad_df.adf_flags = hoflags;
781           if ((oflags & O_CREAT) && !st_invalid) {
782               /* just created, set owner if admin (root) */
783               ad_chown(path, &st);
784           }
785         } 
786         else {
787             /* the file is already open... but */
788             if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
789                 !(ad->ad_df.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
790             {
791                  errno = EACCES;
792                  return -1;
793             }
794             /* FIXME 
795              * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
796              * already open. Should we check for it? ie
797              * O_EXCL --> error 
798              * O_TRUNC --> truncate the fork.
799              * idem for ressource fork.
800              */
801         }
802         open_df = ADFLAGS_DF;
803         ad->ad_df.adf_refcount++;
804     }
805
806     if (!(adflags & ADFLAGS_HF)) 
807         return 0;
808         
809     if (ad_hfileno(ad) != -1) { /* the file is already open */
810         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
811                 !(ad->ad_hf.adf_flags & ( O_RDWR | O_WRONLY))) {
812             if (open_df) {
813                 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
814                 ad_close( ad, open_df );
815             }
816             errno = EACCES;
817             return -1;
818         }
819         ad_refresh(ad);
820         ad->ad_hf.adf_refcount++;
821         return 0;
822     }
823
824     ad_p = ad_path( path, adflags );
825
826     hoflags = oflags & ~O_CREAT;
827     hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
828     ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 );
829     if (ad->ad_hf.adf_fd < 0 ) {
830         if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
831             hoflags = oflags & ~O_CREAT;
832             ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 );
833         }    
834     }
835
836     if ( ad->ad_hf.adf_fd < 0 ) { 
837         if (errno == ENOENT && (oflags & O_CREAT) ) {
838             /*
839              * We're expecting to create a new adouble header file,
840              * here.
841              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
842              */
843             admode = mode;
844             st_invalid = ad_mode_st(ad_p, &admode, &st);
845             admode = ad_hf_mode(admode); 
846             errno = 0;
847             ad->ad_hf.adf_fd = open( ad_p, oflags,admode );
848             if ( ad->ad_hf.adf_fd < 0 ) {
849                 /*
850                  * Probably .AppleDouble doesn't exist, try to
851                  * mkdir it.
852                  */
853                 if (errno == ENOENT && (adflags & ADFLAGS_NOADOUBLE) == 0) {
854                     if (NULL == ( slash = strrchr( ad_p, '/' )) ) {
855                         return ad_error(ad, adflags);
856                     }
857                     *slash = '\0';
858                     errno = 0;
859                     if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
860                         return ad_error(ad, adflags);
861                     }
862                     *slash = '/';
863                     admode = mode;
864                     st_invalid = ad_mode_st(ad_p, &admode, &st);
865                     admode = ad_hf_mode(admode); 
866                     ad->ad_hf.adf_fd = open( ad_p, oflags, admode);
867                     if ( ad->ad_hf.adf_fd < 0 ) {
868                         return ad_error(ad, adflags);
869                     }
870                 } else {
871                      return ad_error(ad, adflags);
872                 }
873             }
874             ad->ad_hf.adf_flags = oflags;
875             /* just created, set owner if admin owner (root) */
876             if (!st_invalid) {
877                 ad_chown(path, &st);
878             }
879         }
880         else {
881             return ad_error(ad, adflags);
882         }
883     } else if (fstat(ad->ad_hf.adf_fd, &st) == 0 && st.st_size == 0) {
884         /* for 0 length files, treat them as new. */
885         ad->ad_hf.adf_flags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR | O_TRUNC;
886     } else {
887         ad->ad_hf.adf_flags = hoflags;
888     }
889     AD_SET(ad->ad_hf.adf_off);
890           
891     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
892     ad->ad_hf.adf_refcount++;
893     if ((ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT ))) {
894         /*
895          * This is a new adouble header file. Initialize the structure,
896          * instead of reading it.
897         */
898         if (new_rfork(path, ad, adflags) < 0) {
899             /* the file is already deleted, perm, whatever, so return an error*/
900             ad_close(ad, adflags);
901             return -1;
902         }
903     } else {
904             /* Read the adouble header in and parse it.*/
905         if ((ad_header_read( ad , &st) < 0)
906 #if AD_VERSION == AD_VERSION2
907                 || (ad_v1tov2(ad, ad_p) < 0)
908 #endif /* AD_VERSION == AD_VERSION2 */
909         ) {
910             ad_close( ad, adflags );
911             return( -1 );
912         }
913     }
914     return 0 ;
915 }
916
917 /* ----------------------------------- */
918 static int new_rfork(const char *path, struct adouble *ad, int adflags)
919 {
920 #if 0
921     struct timeval      tv;
922 #endif    
923     const struct entry  *eid;
924     u_int16_t           ashort;
925     struct stat         st;
926
927     ad->ad_magic = AD_MAGIC;
928     ad->ad_version = ad->ad_flags;
929     if (!ad->ad_version)
930     ad->ad_version = AD_VERSION;
931
932     memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
933     memset(ad->ad_data, 0, sizeof(ad->ad_data));
934
935 #if AD_VERSION == AD_VERSION2
936     if (ad->ad_version == AD_VERSION2)
937        eid = entry_order2;
938     else
939 #endif
940        eid = entry_order1;
941
942     while (eid->id) {
943         ad->ad_eid[eid->id].ade_off = eid->offset;
944         ad->ad_eid[eid->id].ade_len = eid->len;
945         eid++;
946     }
947             
948     /* put something sane in the directory finderinfo */
949     if ((adflags & ADFLAGS_DIR)) {
950         /* set default view */
951         ashort = htons(FINDERINFO_CLOSEDVIEW);
952         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
953                      &ashort, sizeof(ashort));
954     } else {
955         /* set default creator/type fields */
956         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"TEXT", 4);
957         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"UNIX", 4);
958     }
959
960     /* make things invisible */
961     if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
962         ashort = htons(ATTRBIT_INVISIBLE);
963         ad_setattr(ad, ashort);
964         ashort = htons(FINDERINFO_INVISIBLE);
965         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
966                      &ashort, sizeof(ashort));
967     }
968
969 #if 0
970     if (gettimeofday(&tv, NULL) < 0) {
971         return -1;
972     } 
973 #endif
974     
975     if (stat(path, &st) < 0) {
976         return -1;
977     }
978             
979     /* put something sane in the date fields */
980     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
981     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
982     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
983     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
984     return 0;
985 }
986
987 /* to do this with mmap, we need the hfs fs to understand how to mmap
988    header files. */
989 int ad_refresh(struct adouble *ad)
990 {
991
992   if (ad->ad_hf.adf_fd < -1)
993     return -1;
994
995   return ad_header_read(ad, NULL);
996 }