]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
e32dcba6da75a5799a190b532e8f3d7e08587410
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * $Id: ad_open.c,v 1.43 2009-06-22 12:05:15 franklahm 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 <errno.h>
35
36 #include <atalk/adouble.h>
37 #include <sys/param.h>
38 #include <atalk/logger.h>
39
40 #include <atalk/util.h>
41 #include <string.h>
42
43 #include "ad_private.h"
44 #include <stdlib.h>
45
46 #ifndef MAX
47 #define MAX(a, b)  ((a) < (b) ? (b) : (a))
48 #endif /* ! MAX */
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 /* ADEDOFF_FILEI */
90
91 #define ADEDOFF_NAME_V1      (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
92 #define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
93 #define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
94 #define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
95 #define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
96
97 /* i stick things in a slightly different order than their eid order in 
98  * case i ever want to separate RootInfo behaviour from the rest of the 
99  * stuff. */
100 #define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
101 #define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
102 #define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
103 #define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
104 #define ADEDOFF_DID          (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
105 #define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
106 #define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
107 #define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
108 #define ADEDOFF_PRIVDEV      (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
109 #define ADEDOFF_PRIVINO      (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
110 #define ADEDOFF_PRIVSYN      (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
111 #define ADEDOFF_PRIVID       (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
112
113 #define ADEDOFF_RFORK_V2     (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
114
115 #define ADEID_NUM_OSX        2
116 #define ADEDOFF_FINDERI_OSX  (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN)
117 #define ADEDOFF_RFORK_OSX    (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI)
118
119 /* we keep local copies of a bunch of stuff so that we can initialize things 
120  * correctly. */
121
122 /* this is to prevent changing timezones from causing problems with
123    localtime volumes. the screw-up is 30 years. we use a delta of 5
124    years.  */
125 #define TIMEWARP_DELTA 157680000
126
127
128 struct entry {
129   u_int32_t id, offset, len;
130 };
131
132 static const struct entry entry_order1[ADEID_NUM_V1 +1] = {
133   {ADEID_NAME,    ADEDOFF_NAME_V1,    ADEDLEN_INIT},      /* 3 */
134   {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},      /* 4 */
135   {ADEID_FILEI,   ADEDOFF_FILEI,      ADEDLEN_FILEI},     /* 7 */
136   {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},   /* 9 */
137   {ADEID_RFORK,   ADEDOFF_RFORK_V1,   ADEDLEN_INIT},      /* 2 */
138   {0, 0, 0}
139 };
140
141 #if AD_VERSION == AD_VERSION1 
142 #define DISK_EID(ad, a) (a)
143
144 #else /* AD_VERSION == AD_VERSION2 */
145
146 static u_int32_t get_eid(struct adouble *ad, u_int32_t eid) 
147 {
148     if (eid <= 15)
149         return eid;
150     if (ad->ad_version == AD_VERSION1)
151         return 0;
152     if (eid == AD_DEV)
153         return ADEID_PRIVDEV;
154     if (eid == AD_INO)
155         return ADEID_PRIVINO;
156     if (eid == AD_SYN)
157         return ADEID_PRIVSYN;
158     if (eid == AD_ID)
159         return ADEID_PRIVID;
160
161     return 0;
162 }
163
164 #define DISK_EID(ad, a) get_eid(ad, a)
165
166 static const struct entry entry_order2[ADEID_NUM_V2 +1] = {
167   {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
168   {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
169   {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
170   {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
171   {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
172   {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
173   {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
174   {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
175   {ADEID_PRIVDEV,     ADEDOFF_PRIVDEV, ADEDLEN_INIT},
176   {ADEID_PRIVINO,     ADEDOFF_PRIVINO, ADEDLEN_INIT},
177   {ADEID_PRIVSYN,     ADEDOFF_PRIVSYN, ADEDLEN_INIT},
178   {ADEID_PRIVID,     ADEDOFF_PRIVID, ADEDLEN_INIT},
179   {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
180
181   {0, 0, 0}
182 };
183
184 /* OS X adouble finder info and resource fork only
185 */
186 static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = {
187   {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
188   {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
189
190   {0, 0, 0}
191 };
192
193 #define ADEID_NUM_SFM 3
194 static const struct entry entry_order_sfm[ADEID_NUM_SFM +1] = {
195   {ADEID_FINDERI,     16,         ADEDLEN_FINDERI},   /* 9 */
196   {ADEID_SFMRESERVE2, 16+32,      6},                 /* 21 */
197   {ADEID_FILEI,       60,         ADEDLEN_FILEI},     /* 7 */
198
199   {0, 0, 0}
200 };
201
202 #endif /* AD_VERSION == AD_VERSION2 */
203
204 #if AD_VERSION == AD_VERSION2
205
206 /* update a version 2 adouble resource fork with our private entries */
207 static int ad_update(struct adouble *ad, const char *path)
208 {
209   struct stat st;
210   u_int16_t nentries = 0;
211   off_t     off, shiftdata=0;
212   const struct entry  *eid;
213   static off_t entry_len[ADEID_MAX];
214   static char  databuf[ADEID_MAX][256], *buf;
215   int fd;
216   int ret = -1;
217
218   /* check to see if we should convert this header. */
219   if (!path || ad->ad_flags != AD_VERSION2)
220     return 0;
221   
222   if (!(ad->ad_md->adf_flags & O_RDWR)) {
223       /* we were unable to open the file read write the last time */
224       return 0;
225   }
226
227   if (ad->ad_eid[ADEID_RFORK].ade_off) {
228       shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
229   }
230
231   memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries ));
232   nentries = ntohs( nentries );
233
234   if ( shiftdata == 0 && nentries == ADEID_NUM_V2)
235     return 0;
236
237   memset(entry_len, 0, sizeof(entry_len));
238   memset(databuf, 0, sizeof(databuf));
239
240   /* bail if we can't get a lock */
241   if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
242     goto bail_err;
243
244   fd = ad->ad_md->adf_fd;
245
246   if (fstat(fd, &st)) {
247     goto bail_lock;
248   }
249
250   if (st.st_size > 0x7fffffff) {
251       LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path);
252       errno = EIO;
253       goto bail_lock;
254   }
255
256   off = ad->ad_eid[ADEID_RFORK].ade_off;
257   if (off > st.st_size) {
258       LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off); 
259       errno = EIO;
260       goto bail_lock;
261   }
262
263   if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
264       LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); 
265       errno = EIO;
266       goto bail_lock;
267   }
268   
269   if ((void *) (buf = (char *)
270                 mmap(NULL, st.st_size + shiftdata,
271                      PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
272           MAP_FAILED) {
273     goto bail_lock;
274   }
275
276   /* last place for failure. */
277   if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
278     goto bail_lock;
279   }
280
281   /* move the RFORK. this assumes that the RFORK is at the end */
282   if (off) {
283     memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
284   }
285
286   munmap(buf, st.st_size + shiftdata);
287
288   /* now, fix up our copy of the header */
289   memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
290  
291   /* save the header entries */ 
292   eid = entry_order2;
293   while (eid->id) {
294     if( ad->ad_eid[eid->id].ade_off != 0) {
295       if ( eid->id > 2 && ad->ad_eid[eid->id].ade_len < 256)
296         memcpy( databuf[eid->id], ad->ad_data +ad->ad_eid[eid->id].ade_off, ad->ad_eid[eid->id].ade_len);
297       entry_len[eid->id] = ad->ad_eid[eid->id].ade_len;
298     }
299     eid++;
300   }
301
302   memset(ad->ad_data + AD_HEADER_LEN, 0, AD_DATASZ - AD_HEADER_LEN);
303
304   /* copy the saved entries to the new header */
305   eid = entry_order2;
306   while (eid->id) {
307     if ( eid->id > 2 && entry_len[eid->id] > 0) {
308       memcpy(ad->ad_data+eid->offset, databuf[eid->id], entry_len[eid->id]);
309     }
310     ad->ad_eid[eid->id].ade_off = eid->offset;
311     ad->ad_eid[eid->id].ade_len = entry_len[eid->id];
312     eid++;
313   }
314
315   /* rebuild the header and cleanup */
316   LOG(log_debug, logtype_default, "updated AD2 header %s", path);
317   ad_flush(ad );
318   ret = 0;
319
320 bail_lock:
321   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
322 bail_err:
323   return ret;
324 }
325
326 /* ------------------------------------------
327    FIXME work only if < 2GB 
328 */
329 static int ad_convert(struct adouble *ad, const char *path)
330 {
331   struct stat st;
332   u_int16_t attr;
333   char *buf;
334   int fd, off;
335   int ret = -1;
336   /* use resource fork offset from file */
337   int shiftdata;
338   int toV2;
339   int toV1;
340   
341   if (!path) {
342       return 0;
343   }
344   
345   if (!(ad->ad_md->adf_flags & ( O_RDWR))) {
346       /* we were unable to open the file read write the last time */
347       return 0;
348   }
349
350   /* check to see if we should convert this header. */
351   toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2;
352   toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1;
353
354   if (!toV2 && !toV1)
355       return 0;
356
357   /* convert from v1 to v2. what does this mean?
358    *  1) change FILEI into FILEDATESI
359    *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
360    *  3) move FILEI attributes into AFPFILEI
361    *  4) initialize ACCESS field of FILEDATESI.
362    *  5) move the resource fork
363    */
364   
365   /* bail if we can't get a lock */
366   if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) 
367     goto bail_err;
368
369   /* we reuse fd from the resource fork */
370   fd = ad->ad_md->adf_fd;
371
372   if (ad->ad_eid[ADEID_RFORK].ade_off) {
373       shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
374   }
375   else {
376       shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */
377   }
378
379   if (fstat(fd, &st)) { 
380       goto bail_lock;
381   }
382
383   if (st.st_size > 0x7fffffff -shiftdata) {
384       LOG(log_debug, logtype_default, "ad_v1tov2: file too big."); 
385       errno = EIO;
386       goto bail_lock;
387   }
388   
389   off = ad->ad_eid[ADEID_RFORK].ade_off;
390
391   if (off > st.st_size) {
392       LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off); 
393       errno = EIO;
394       goto bail_lock;
395   }
396
397   if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
398       LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); 
399       errno = EIO;
400       goto bail_lock;
401   }
402   
403   if ((void *) (buf = (char *) 
404                 mmap(NULL, st.st_size + shiftdata,
405                      PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 
406           MAP_FAILED) {
407     goto bail_lock;
408   }
409
410   /* last place for failure. */
411
412   if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
413       goto bail_lock;
414   }
415   
416   /* move the RFORK. this assumes that the RFORK is at the end */
417   if (off) {
418       memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
419   }
420
421   munmap(buf, st.st_size + shiftdata);
422
423   /* now, fix up our copy of the header */
424   memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
425   
426   /* replace FILEI with FILEDATESI */
427   ad_getattr(ad, &attr);
428   ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
429   ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
430   ad->ad_eid[ADEID_FILEI].ade_off = 0;
431   ad->ad_eid[ADEID_FILEI].ade_len = 0;
432   
433   /* add in the new entries */
434   ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
435   ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
436   ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
437   ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
438   ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
439   ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
440   ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
441   ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
442   
443   ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV;
444   ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT;
445   ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO;
446   ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT;
447   ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN;
448   ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT;
449   ad->ad_eid[ADEID_PRIVID].ade_off  = ADEDOFF_PRIVID;
450   ad->ad_eid[ADEID_PRIVID].ade_len =  ADEDLEN_INIT;
451   
452   /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
453   ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
454   ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
455   ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
456   ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
457   
458   /* switch to dest version */
459   ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1;
460   
461   /* move our data buffer to make space for the new entries. */
462   memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1,
463           ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
464   
465   /* now, fill in the space with appropriate stuff. we're
466      operating as a v2 file now. */
467   ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
468   memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
469   memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
470   ad_setattr(ad, attr);
471   memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
472   memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
473   
474   /* rebuild the header and cleanup */
475   ad_flush(ad );
476   ret = 0;
477   
478 bail_lock:
479   ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
480 bail_err:
481   return ret;
482 }
483 #endif /* AD_VERSION == AD_VERSION2 */
484
485 /* --------------------------- */
486 #ifdef ATACC
487 mode_t ad_hf_mode (mode_t mode)
488 {
489     /* we always need RW mode for file owner */
490 #if 0
491     mode |= S_IRUSR;
492 #endif    
493     mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
494     /* fnctl lock need write access */
495     if ((mode & S_IRUSR))
496         mode |= S_IWUSR;
497     if ((mode & S_IRGRP))
498         mode |= S_IWGRP;
499     if ((mode & S_IROTH))
500         mode |= S_IWOTH;
501     /* if write mode set add read mode */
502     if ((mode & S_IWUSR))
503         mode |= S_IRUSR;
504     if ((mode & S_IWGRP))
505         mode |= S_IRGRP;
506     if ((mode & S_IWOTH))
507         mode |= S_IROTH;
508
509     return mode;
510 }
511
512 #endif
513
514 /* ------------------------------------- 
515   read in the entries 
516 */
517 static void parse_entries(struct adouble *ad, char *buf,
518                                     u_int16_t nentries)
519 {
520     u_int32_t   eid, len, off;
521     int         warning = 0;
522
523     /* now, read in the entry bits */
524     for (; nentries > 0; nentries-- ) {
525         memcpy(&eid, buf, sizeof( eid ));
526         eid = DISK_EID(ad, ntohl( eid ));
527         buf += sizeof( eid );
528         memcpy(&off, buf, sizeof( off ));
529         off = ntohl( off );
530         buf += sizeof( off );
531         memcpy(&len, buf, sizeof( len ));
532         len = ntohl( len );
533         buf += sizeof( len );
534
535         if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) && 
536                  (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
537             ad->ad_eid[ eid ].ade_off = off;
538             ad->ad_eid[ eid ].ade_len = len;
539         } else if (!warning) {
540             warning = 1;
541             LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d",
542                     nentries, eid );
543         }
544     }
545 }
546
547
548 /* this reads enough of the header so that we can figure out all of
549  * the entry lengths and offsets. once that's done, we just read/mmap
550  * the rest of the header in.
551  *
552  * NOTE: we're assuming that the resource fork is kept at the end of
553  *       the file. also, mmapping won't work for the hfs fs until it
554  *       understands how to mmap header files. */
555 static int ad_header_read(struct adouble *ad, struct stat *hst)
556 {
557     char                *buf = ad->ad_data;
558     u_int16_t           nentries;
559     int                 len;
560     ssize_t             header_len;
561     static int          warning = 0;
562     struct stat         st;
563
564     /* read the header */
565     if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
566         return -1;
567     }
568     if (header_len < AD_HEADER_LEN) {
569         errno = EIO;
570         return -1;
571     }
572
573     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
574     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
575
576     /* tag broken v1 headers. just assume they're all right. 
577      * we detect two cases: null magic/version
578      *                      byte swapped magic/version
579      * XXX: in the future, you'll need the v1compat flag. 
580      * (ad->ad_flags & ADFLAGS_V1COMPAT) */
581     if (!ad->ad_magic && !ad->ad_version) {
582       if (!warning) {
583         LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
584         warning++;
585       }
586       ad->ad_magic = AD_MAGIC;
587       ad->ad_version = AD_VERSION1;
588
589     } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
590       if (!warning) {
591         LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
592         warning++;
593       }
594
595     } else {
596       ad->ad_magic = ntohl( ad->ad_magic );
597       ad->ad_version = ntohl( ad->ad_version );
598     }
599
600     if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
601 #if AD_VERSION == AD_VERSION2
602                                        && (ad->ad_version != AD_VERSION2)
603 #endif /* AD_VERSION == AD_VERSION2 */
604                                        )) {
605       errno = EIO;
606       LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
607       return -1;
608     }
609
610     memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
611     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
612     nentries = ntohs( nentries );
613
614     /* read in all the entry headers. if we have more than the 
615      * maximum, just hope that the rfork is specified early on. */
616     len = nentries*AD_ENTRY_LEN;
617
618     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
619       len = sizeof(ad->ad_data) - AD_HEADER_LEN;
620
621     buf += AD_HEADER_LEN;
622     if (len > header_len - AD_HEADER_LEN) {
623         errno = EIO;
624         LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
625         return -1;
626     }
627
628     /* figure out all of the entry offsets and lengths. if we aren't
629      * able to read a resource fork entry, bail. */
630     nentries = len / AD_ENTRY_LEN;
631     parse_entries(ad, buf, nentries);
632     if (!ad_getentryoff(ad, ADEID_RFORK)
633         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
634         ) {
635         errno = EIO;
636         LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); 
637         return -1;
638     }
639
640     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
641         errno = EIO;
642         LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
643         return -1;
644     }
645     
646     if (hst == NULL) {
647         hst = &st;
648         if (fstat(ad->ad_md->adf_fd, &st) < 0) {
649             return 1; /* fail silently */
650          }
651     }
652     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
653
654     /* fix up broken dates */
655     if (ad->ad_version == AD_VERSION1) {
656       u_int32_t aint;
657       
658       /* check to see if the ad date is wrong. just see if we have
659       * a modification date in the future. */
660       if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
661           (aint > TIMEWARP_DELTA + hst->st_mtime)) {
662         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
663         ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
664         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
665         ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
666         ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
667       }
668     }
669
670     return 0;
671 }
672
673 /* ---------------------------
674    SFM structure 
675  */
676 #if 0
677 typedef struct {
678     byte    afpi_Signature[4];      /* Must be 0x00504641 */
679     byte    afpi_Version[4];        /* Must be 0x00010000 */
680     byte    afpi_Reserved1[4];
681     byte    afpi_BackupTime[4];     /* Backup time for the file/dir */
682     byte    finderinfo[32];         /* Finder info */
683     byte    afpi_ProDosInfo[6];     /* ProDos Info */
684     byte    afpi_Reserved2[6];
685 } sfm_info; 
686 #endif
687
688 static int ad_header_sfm_read(struct adouble *ad, struct stat *hst)
689 {
690     char                *buf = ad->ad_data;
691     const struct entry  *eid;
692     ssize_t             header_len;
693     struct stat         st;
694
695     /* read the header */
696     if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
697         return -1;
698     }
699     if (header_len != AD_SFM_LEN) {
700         errno = EIO;
701         return -1;
702     }
703
704     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
705     memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version ));
706
707     /* FIXME in the great Microsoft tradition they aren't in network order */
708 #if 0
709     if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) {
710       static int          warning = 0;
711       if (!warning) {
712         LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
713         warning++;
714       }
715
716     } else {
717       ad->ad_magic = ntohl( ad->ad_magic );
718       ad->ad_version = ntohl( ad->ad_version );
719     }
720 #endif
721     if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) {
722       errno = EIO;
723       LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header.");
724       return -1;
725     }
726
727     /* reinit adouble table */
728     eid = entry_order_sfm;
729     while (eid->id) {
730         ad->ad_eid[eid->id].ade_off = eid->offset;
731         ad->ad_eid[eid->id].ade_len = eid->len;
732         eid++;
733     }
734
735     /* steal some prodos for attribute */
736     {
737     
738         u_int16_t attribute;
739         memcpy(&attribute, buf + 48 +4, sizeof(attribute));
740         ad_setattr(ad, attribute );
741     }
742     
743     if (ad->ad_resource_fork.adf_fd != -1) {
744         /* we have a resource fork use it rather than the metadata */
745         if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) {
746             /* XXX set to zero ?
747             ad->ad_rlen =  0;
748             */
749             return 1;
750         }
751         ad->ad_rlen = st.st_size;
752         hst = &st;
753     }
754     else if (hst == NULL) {
755         hst = &st;
756         if (fstat(ad->ad_md->adf_fd, &st) < 0) {
757             return 1; /* fail silently */
758         }
759     }
760
761     /* fix up broken dates */
762     if (ad->ad_version == AD_VERSION1) {
763       u_int32_t aint;
764       
765       /* check to see if the ad date is wrong. just see if we have
766       * a modification date in the future. */
767       if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
768           (aint > TIMEWARP_DELTA + hst->st_mtime)) {
769         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
770         ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
771         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
772         ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
773         ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
774       }
775     }
776
777     return 0;
778 }
779
780 /* ---------------------------------------
781  * Put the .AppleDouble where it needs to be:
782  *
783  *          /   a/.AppleDouble/b
784  *      a/b
785  *          \   b/.AppleDouble/.Parent
786  *
787  * FIXME: should do something for pathname > MAXPATHLEN
788  */
789 char *
790 ad_path( path, adflags )
791     const char  *path;
792     int         adflags;
793 {
794     static char pathbuf[ MAXPATHLEN + 1];
795     char        c, *slash, buf[MAXPATHLEN + 1];
796     size_t      l;
797
798     l = strlcpy(buf, path, MAXPATHLEN +1);
799     if ( adflags & ADFLAGS_DIR ) {
800         strcpy( pathbuf, buf);
801         if ( *buf != '\0' && l < MAXPATHLEN) {
802             pathbuf[l++] = '/';
803             pathbuf[l] = 0;
804         }
805         slash = ".Parent";
806     } else {
807         if (NULL != ( slash = strrchr( buf, '/' )) ) {
808             c = *++slash;
809             *slash = '\0';
810             strcpy( pathbuf, buf);
811             *slash = c;
812         } else {
813             pathbuf[ 0 ] = '\0';
814             slash = buf;
815         }
816     }
817     strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
818     strlcat( pathbuf, slash, MAXPATHLEN +1);
819
820     return( pathbuf );
821 }
822
823 /* -------------------- */
824 static int ad_mkrf(char *path)
825 {
826     char *slash;
827     /*
828      * Probably .AppleDouble doesn't exist, try to mkdir it.
829      */
830      if (NULL == ( slash = strrchr( path, '/' )) ) {
831          return -1;
832      }
833      *slash = '\0';
834      errno = 0;
835      if ( ad_mkdir( path, 0777 ) < 0 ) {
836           return -1;
837      }
838      *slash = '/';
839      return 0;
840 }
841
842 /* ---------------------------------------
843  * Put the resource fork where it needs to be:
844  * ._name
845  */
846 char *
847 ad_path_osx(const char *path, int adflags _U_)
848 {
849     static char pathbuf[ MAXPATHLEN + 1];
850     char        c, *slash, buf[MAXPATHLEN + 1];
851     
852     if (!strcmp(path,".")) {
853             /* fixme */
854         getcwd(buf, MAXPATHLEN);
855     }
856     else {
857         strlcpy(buf, path, MAXPATHLEN +1);
858     }
859     if (NULL != ( slash = strrchr( buf, '/' )) ) {
860         c = *++slash;
861         *slash = '\0';
862         strlcpy( pathbuf, buf, MAXPATHLEN +1);
863         *slash = c;
864     } else {
865         pathbuf[ 0 ] = '\0';
866         slash = buf;
867     }
868     strlcat( pathbuf, "._", MAXPATHLEN  +1);  
869     strlcat( pathbuf, slash, MAXPATHLEN +1);
870     return pathbuf;
871 }
872 /* -------------------- */
873 static int ad_mkrf_osx(char *path _U_)
874 {
875     return 0;
876 }
877
878 /* ---------------------------------------
879  * Put the .AppleDouble where it needs to be:
880  *
881  *          /   a/.AppleDouble/b/Afp_AfpInfo
882  *      a/b     
883  *          \   b/.AppleDouble/.Parent/Afp_AfpInfo
884  *
885  */
886 char *
887 ad_path_ads( path, adflags )
888     const char  *path;
889     int         adflags;
890 {
891     static char pathbuf[ MAXPATHLEN + 1];
892     char        c, *slash, buf[MAXPATHLEN + 1];
893     size_t      l;
894
895     l = strlcpy(buf, path, MAXPATHLEN +1);
896     if ( adflags & ADFLAGS_DIR ) {
897         strcpy( pathbuf, buf);
898         if ( *buf != '\0' && l < MAXPATHLEN) {
899             pathbuf[l++] = '/';
900             pathbuf[l] = 0;
901         }
902         slash = ".Parent";
903     } else {
904         if (NULL != ( slash = strrchr( buf, '/' )) ) {
905             c = *++slash;
906             *slash = '\0';
907             strcpy( pathbuf, buf);
908             *slash = c;
909         } else {
910             pathbuf[ 0 ] = '\0';
911             slash = buf;
912         }
913     }
914     strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
915     strlcat( pathbuf, slash, MAXPATHLEN +1);
916
917     strlcat( pathbuf, "/Afp_AfpInfo", MAXPATHLEN +1);
918     return( pathbuf );
919 }
920
921 /* ---------------------- 
922  * mostly the same as ads, only case change Afp --> AFP and AFP_Resoure
923 */
924 char *
925 ad_path_sfm( path, adflags )
926     const char  *path;
927     int         adflags;
928 {
929     static char pathbuf[ MAXPATHLEN + 1];
930     char        c, *slash, buf[MAXPATHLEN + 1];
931     size_t      l;
932
933     l = strlcpy(buf, path, MAXPATHLEN +1);
934     if ( adflags & ADFLAGS_DIR ) {
935         strcpy( pathbuf, buf);
936         if ( *buf != '\0' && l < MAXPATHLEN) {
937             pathbuf[l++] = '/';
938             pathbuf[l] = 0;
939         }
940         slash = ".Parent";
941     } else {
942         if (NULL != ( slash = strrchr( buf, '/' )) ) {
943             c = *++slash;
944             *slash = '\0';
945             strcpy( pathbuf, buf);
946             *slash = c;
947         } else {
948             pathbuf[ 0 ] = '\0';
949             slash = buf;
950         }
951     }
952     strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
953     strlcat( pathbuf, slash, MAXPATHLEN +1);
954
955     if ((adflags == ADFLAGS_RF)) {
956         strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
957     }
958     else {
959         strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
960     }
961     return( pathbuf );
962 }
963
964 /* -------------------- */
965 static int ad_mkrf_ads(char *path)
966 {
967     char *slash;
968     /*
969      * Probably .AppleDouble doesn't exist, try to mkdir it.
970      */
971      if (NULL == ( slash = strrchr( path, '/' )) ) {
972          return -1;
973      }
974      *slash = 0;
975      errno = 0;
976      if ( ad_mkdir( path, 0777 ) < 0 ) {
977          if ( errno == ENOENT ) {
978              char *slash1;
979              
980              if (NULL == ( slash1 = strrchr( path, '/' )) ) 
981                  return -1;
982              errno = 0;
983              *slash1 = 0;
984              if ( ad_mkdir( path, 0777 ) < 0 ) 
985                   return -1;
986              *slash1 = '/';
987              if ( ad_mkdir( path, 0777 ) < 0 )
988                  return -1;
989          }
990          else
991             return -1;
992      }     
993      *slash = '/';
994      return 0;
995 }
996
997 /* -------------------------
998  * Support inherited protection modes for AppleDouble files.  The supplied
999  * mode is ANDed with the parent directory's mask value in lieu of "umask",
1000  * and that value is returned.
1001  */
1002
1003 #define DEFMASK 07700   /* be conservative */
1004
1005 char 
1006 *ad_dir(path)
1007     const char          *path;
1008 {
1009     static char         modebuf[ MAXPATHLEN + 1];
1010     char                *slash;
1011     size_t              len;
1012
1013     if ( (len = strlen( path )) >= MAXPATHLEN ) {
1014         errno = ENAMETOOLONG;
1015         return NULL;  /* can't do it */
1016     }
1017
1018     /*
1019      * For a path with directories in it, remove the final component
1020      * (path or subdirectory name) to get the name we want to stat.
1021      * For a path which is just a filename, use "." instead.
1022      */
1023     strcpy( modebuf, path );
1024     slash = strrchr( modebuf, '/' );
1025     /* is last char a '/' */
1026     if (slash && slash[1] == 0) {
1027         while (modebuf < slash && slash[-1] == '/') {
1028             --slash;
1029         }
1030         if (modebuf < slash) {
1031             *slash = '\0';              /* remove pathname component */
1032             slash = strrchr( modebuf, '/' );
1033         }
1034     }
1035     if (slash) {
1036         *slash = '\0';          /* remove pathname component */
1037     } else {
1038         modebuf[0] = '.';       /* use current directory */
1039         modebuf[1] = '\0';
1040     }
1041     return modebuf;
1042 }
1043
1044 /* ---------------- */
1045 static uid_t default_uid = -1;
1046
1047 int ad_setfuid(const uid_t id)
1048 {
1049     default_uid = id;
1050     return 0;
1051 }
1052
1053 /* ---------------- */
1054 uid_t ad_getfuid(void) 
1055 {
1056     return default_uid;
1057 }
1058
1059 /* ---------------- 
1060    return inode of path parent directory
1061 */
1062 int ad_stat(const char *path, struct stat *stbuf)
1063 {
1064     char                *p;
1065
1066     p = ad_dir(path);
1067     if (!p) {
1068         return -1;
1069     }
1070
1071     return stat( p, stbuf );
1072 }
1073
1074 /* ---------------- 
1075    if we are root change path user/ group
1076    It can be a native function for BSD cf. FAQ.Q10
1077    path:  pathname to chown 
1078    stbuf: parent directory inode
1079    
1080    use fstat and fchown or lchown with linux?
1081 */
1082 #define EMULATE_SUIDDIR
1083  
1084 static int ad_chown(const char *path, struct stat *stbuf)
1085 {
1086 int ret = 0;
1087 #ifdef EMULATE_SUIDDIR
1088 uid_t id;
1089
1090     if (default_uid != (uid_t)-1) {  
1091         /* we are root (admin) */
1092         id = (default_uid)?default_uid:stbuf->st_uid;
1093         ret = chown( path, id, stbuf->st_gid );
1094     }
1095 #endif    
1096     return ret;
1097 }
1098
1099 /* ---------------- 
1100    return access right and inode of path parent directory
1101 */
1102 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
1103 {
1104     if (*mode == 0) {
1105        return -1;
1106     }
1107     if (ad_stat(path, stbuf) != 0) {
1108         *mode &= DEFMASK;
1109         return -1;
1110     }
1111     *mode &= stbuf->st_mode;
1112     return 0;    
1113 }
1114
1115 /* ---------------- 
1116    return access right of path parent directory
1117 */
1118 int
1119 ad_mode( path, mode )
1120     const char          *path;
1121     int                 mode;
1122 {
1123     struct stat         stbuf;
1124     ad_mode_st(path, &mode, &stbuf);
1125     return mode;
1126 }
1127
1128 /*
1129  * Use mkdir() with mode bits taken from ad_mode().
1130  */
1131 int
1132 ad_mkdir( path, mode )
1133     const char          *path;
1134     int                 mode;
1135 {
1136 int ret;
1137 int st_invalid;
1138 struct stat stbuf;
1139
1140 #ifdef DEBUG
1141     LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
1142 #endif /* DEBUG */
1143
1144     st_invalid = ad_mode_st(path, &mode, &stbuf);
1145     ret = mkdir( path, mode );
1146     if (ret || st_invalid)
1147         return ret;
1148     ad_chown(path, &stbuf);
1149
1150     return ret;    
1151 }
1152
1153 /* ----------------- */
1154 static int ad_error(struct adouble *ad, int adflags)
1155 {
1156 int err = errno;
1157     if ((adflags & ADFLAGS_NOHF)) {
1158         /* FIXME double check : set header offset ?*/
1159         return 0;
1160     }
1161     if ((adflags & ADFLAGS_DF)) {
1162         ad_close( ad, ADFLAGS_DF );
1163         err = errno;
1164     }
1165     return -1 ;
1166 }
1167
1168 static int new_rfork(const char *path, struct adouble *ad, int adflags);
1169
1170 #ifdef  HAVE_PREAD
1171 #define AD_SET(a) 
1172 #else 
1173 #define AD_SET(a) a = 0
1174 #endif
1175
1176 /* --------------------------- */
1177 static int ad_check_size(struct adouble *ad, struct stat *st)
1178 {
1179    if (st->st_size > 0 && st->st_size < AD_DATASZ1) 
1180         return 1;
1181    return 0;
1182 }
1183
1184 /* --------------------------- */
1185 static int ad_check_size_sfm(struct adouble *ad, struct stat *st)
1186 {
1187    if (st->st_size > 0 && st->st_size < AD_SFM_LEN) 
1188         return 1;
1189    return 0;
1190 }
1191
1192 /* --------------------------- */
1193 static int ad_header_upgrade(struct adouble *ad, char *name)
1194 {
1195 #if AD_VERSION == AD_VERSION2
1196     int ret;
1197     if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
1198        return ret;
1199     }
1200 #endif
1201     return 0;
1202 }
1203
1204 /* --------------------------- */
1205 static int ad_header_upgrade_none(struct adouble *ad, char *name)
1206 {
1207     return 0;
1208 }
1209
1210 /* --------------------------- */
1211 static struct adouble_fops ad_osx = {
1212         &ad_path_osx,
1213         &ad_mkrf_osx,
1214         &ad_rebuild_adouble_header,
1215         &ad_check_size,
1216
1217         &ad_header_read,
1218         &ad_header_upgrade,        
1219 };
1220
1221 static struct adouble_fops ad_ads = {
1222         &ad_path_ads,
1223         &ad_mkrf_ads,
1224         &ad_rebuild_adouble_header,
1225         &ad_check_size,
1226
1227         &ad_header_read,
1228         &ad_header_upgrade,        
1229 };
1230
1231 static struct adouble_fops ad_sfm = {
1232         &ad_path_sfm,
1233         &ad_mkrf_ads,
1234         &ad_rebuild_sfm_header,
1235         &ad_check_size_sfm,
1236         
1237         &ad_header_sfm_read,
1238         &ad_header_upgrade_none,
1239 };
1240
1241 static struct adouble_fops ad_adouble = {
1242         &ad_path,
1243         &ad_mkrf,
1244         &ad_rebuild_adouble_header,
1245         &ad_check_size,
1246
1247         &ad_header_read,
1248         &ad_header_upgrade,        
1249 };
1250
1251
1252 void ad_init(struct adouble *ad, int flags, int options)
1253 {
1254     memset( ad, 0, sizeof( struct adouble ) );
1255     ad->ad_flags = flags;
1256     if (flags == AD_VERSION2_OSX) {
1257         ad->ad_ops = &ad_osx;
1258     }
1259     else if (flags == AD_VERSION1_ADS) {
1260         ad->ad_ops = &ad_ads;
1261     }
1262     else if (flags == AD_VERSION1_SFM) {
1263         ad->ad_ops = &ad_sfm;
1264     }
1265     else {
1266         ad->ad_ops = &ad_adouble;
1267     }
1268     ad->ad_options = options;
1269 }
1270
1271 /* -------------------
1272  * It's not possible to open the header file O_RDONLY -- the read
1273  * will fail and return an error. this refcounts things now. 
1274  */
1275 int ad_open( path, adflags, oflags, mode, ad )
1276     const char          *path;
1277     int                 adflags, oflags, mode;
1278     struct adouble      *ad;
1279 {
1280     struct stat         st_dir;
1281     struct stat         st_meta;
1282     struct stat         *pst = NULL;
1283     char                *ad_p;
1284     int                 hoflags, admode;
1285     int                 st_invalid = -1;
1286     int                 open_df = 0;
1287     
1288     if (ad->ad_inited != AD_INITED) {
1289         ad_data_fileno(ad) = -1;
1290         ad_reso_fileno(ad) = -1;
1291         adf_lock_init(&ad->ad_data_fork);
1292         adf_lock_init(&ad->ad_resource_fork);
1293         if (ad->ad_flags != AD_VERSION1_SFM) {
1294                 ad->ad_md = &ad->ad_resource_fork;
1295         }
1296         else {
1297                 adf_lock_init(&ad->ad_metadata_fork);
1298                 ad->ad_md = &ad->ad_metadata_fork;
1299                 ad_meta_fileno(ad) = -1;
1300         }
1301         ad->ad_inited = AD_INITED;
1302         ad->ad_refcount = 1;
1303         ad->ad_open_forks = 0;
1304         ad->ad_adflags = adflags;
1305     }
1306     else {
1307         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1308         /* XXX not true if we have a meta data fork ? */
1309         if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
1310                 ad->ad_open_forks |= ATTRBIT_ROPEN;
1311     }
1312
1313     if ((adflags & ADFLAGS_DF)) { 
1314         if (ad_data_fileno(ad) == -1) {
1315           hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1316           admode = mode;
1317           if ((oflags & O_CREAT)) {
1318               st_invalid = ad_mode_st(path, &admode, &st_dir);
1319               if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1320                   admode = mode;
1321               }
1322           }
1323           ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
1324           if (ad->ad_data_fork.adf_fd < 0 ) {
1325              if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1326                 hoflags = oflags;
1327                 ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
1328              }
1329           }
1330           if ( ad->ad_data_fork.adf_fd < 0)
1331                 return -1;
1332
1333           AD_SET(ad->ad_data_fork.adf_off);
1334           ad->ad_data_fork.adf_flags = hoflags;
1335           if (!st_invalid) {
1336               /* just created, set owner if admin (root) */
1337               ad_chown(path, &st_dir);
1338           }
1339         } 
1340         else {
1341             /* the file is already open... but */
1342             if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
1343                 !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
1344             {
1345                  errno = EACCES;
1346                  return -1;
1347             }
1348             /* FIXME 
1349              * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
1350              * already open. Should we check for it? ie
1351              * O_EXCL --> error 
1352              * O_TRUNC --> truncate the fork.
1353              * idem for ressource fork.
1354              */
1355         }
1356         open_df = ADFLAGS_DF;
1357         ad->ad_data_fork.adf_refcount++;
1358     }
1359
1360     if (!(adflags & ADFLAGS_HF)) 
1361         return 0;
1362
1363     /* ****************************************** */
1364         
1365     if (ad_meta_fileno(ad) != -1) { /* the file is already open */
1366         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
1367                 !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
1368             if (open_df) {
1369                 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
1370                 ad_close( ad, open_df );
1371             }
1372             errno = EACCES;
1373             return -1;
1374         }
1375         ad_refresh(ad);
1376         ad->ad_md->adf_refcount++;
1377         goto sfm;
1378     }
1379
1380     ad_p = ad->ad_ops->ad_path( path, adflags );
1381
1382     hoflags = oflags & ~O_CREAT;
1383     if (!(adflags & ADFLAGS_RDONLY)) {
1384         hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1385     }
1386     ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1387     if (ad->ad_md->adf_fd < 0 ) {
1388         if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1389             hoflags = oflags & ~O_CREAT;
1390             ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1391         }    
1392     }
1393
1394     if ( ad->ad_md->adf_fd < 0 ) { 
1395         if (errno == ENOENT && (oflags & O_CREAT) ) {
1396             /*
1397              * We're expecting to create a new adouble header file,
1398              * here.
1399              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
1400              */
1401             admode = mode;
1402             errno = 0;
1403             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1404             if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1405                 admode = mode;
1406             }
1407             admode = ad_hf_mode(admode); 
1408             if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
1409                 if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
1410                     return ad_error(ad, adflags);
1411                 }
1412                 admode = mode;
1413                 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1414                 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1415                     admode = mode;
1416                 }
1417                 admode = ad_hf_mode(admode); 
1418             }
1419             /* retry with O_CREAT */
1420             ad->ad_md->adf_fd = open( ad_p, oflags,admode );
1421             if ( ad->ad_md->adf_fd < 0 ) {
1422                 return ad_error(ad, adflags);
1423             }
1424             ad->ad_md->adf_flags = oflags;
1425             /* just created, set owner if admin owner (root) */
1426             if (!st_invalid) {
1427                 ad_chown(ad_p, &st_dir);
1428             }
1429         }
1430         else {
1431             return ad_error(ad, adflags);
1432         }
1433     } else if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
1434         /* for 0 length files, treat them as new. */
1435         ad->ad_md->adf_flags = hoflags| O_TRUNC;
1436     } else {
1437         ad->ad_md->adf_flags = hoflags;
1438     }
1439     AD_SET(ad->ad_md->adf_off);
1440           
1441     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1442     ad->ad_md->adf_refcount++;
1443     if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
1444         /*
1445          * This is a new adouble header file. Initialize the structure,
1446          * instead of reading it.
1447         */
1448         if (new_rfork(path, ad, adflags) < 0) {
1449             int err = errno;
1450             /* the file is already deleted, perm, whatever, so return an error*/
1451             ad_close(ad, adflags);
1452             errno = err;
1453             return -1;
1454         }
1455         ad_flush(ad);
1456     } else {
1457         /* Read the adouble header in and parse it.*/
1458         if (ad->ad_ops->ad_header_read( ad , pst) < 0
1459                 || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) 
1460         {
1461             int err = errno;
1462             
1463             ad_close( ad, adflags );
1464             errno = err;
1465             return -1;
1466         }
1467     }
1468
1469     /* ****************************************** */
1470     /* open the resource fork if SFM */
1471 sfm:
1472     if (ad->ad_flags != AD_VERSION1_SFM) {
1473         return 0;
1474     }
1475
1476     if ((adflags & ADFLAGS_DIR)) {
1477         /* no resource fork for directories / volumes XXX it's false! */
1478         return 0;
1479     }
1480
1481     /* untrue yet but ad_close will decremente it*/
1482     ad->ad_resource_fork.adf_refcount++;
1483
1484     if (ad_reso_fileno(ad) != -1) { /* the file is already open */
1485         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
1486                 !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1487
1488             ad_close( ad, open_df | ADFLAGS_HF);
1489             errno = EACCES;
1490             return -1;
1491         }
1492         return 0;
1493     }
1494
1495     ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
1496
1497     hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1498     ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
1499     admode = mode;
1500     st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1501     
1502     if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1503        admode = mode;
1504     }
1505      
1506     if (ad->ad_resource_fork.adf_fd < 0 ) {
1507        if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1508           hoflags = oflags;
1509           ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
1510        }
1511     }
1512
1513     if ( ad->ad_resource_fork.adf_fd < 0) {
1514        int err = errno;
1515             
1516        ad_close( ad, adflags );
1517        errno = err;
1518        return -1;
1519     }
1520                         
1521     AD_SET(ad->ad_resource_fork.adf_off);
1522     ad->ad_resource_fork.adf_flags = hoflags;
1523     if ((oflags & O_CREAT) && !st_invalid) {
1524         /* just created, set owner if admin (root) */
1525         ad_chown(ad_p, &st_dir);
1526     }
1527     else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
1528         ad->ad_rlen = st_meta.st_size;
1529     }
1530     return 0 ;
1531 }
1532
1533 /* ----------------------------------- 
1534  * return only metadata but try very hard
1535 */
1536 int ad_metadata(const char *name, int flags, struct adouble *adp)
1537 {
1538     uid_t uid;
1539     int   ret, err;
1540     int   dir = flags & ADFLAGS_DIR;
1541
1542     /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */
1543     /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */
1544     if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) {
1545         uid = geteuid();
1546         if (seteuid(0)) {
1547             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1548             errno = EACCES;
1549             return -1;
1550         }
1551         /* we are root open read only */
1552         ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
1553         err = errno;
1554         if ( seteuid(uid) < 0) {
1555             LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1556             exit(EXITERR_SYS);
1557         }
1558         errno = err;
1559     }
1560
1561     if (!ret && (ADFLAGS_OPENFORKS & flags)) {
1562         u_int16_t attrbits = adp->ad_open_forks;
1563
1564         /* 
1565            we need to check if the file is open by another process.
1566            it's slow so we only do it if we have to:
1567            - it's requested.
1568            - we don't already have the answer!
1569         */
1570         
1571         if (!(attrbits & ATTRBIT_ROPEN)) {
1572             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_ROPEN : 0;
1573             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_ROPEN : 0;
1574         }
1575
1576         if (!(attrbits & ATTRBIT_DOPEN)) {
1577             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_DOPEN : 0;
1578             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_DOPEN : 0;
1579         }
1580         adp->ad_open_forks = attrbits;
1581     }
1582     return ret;
1583 }
1584
1585 /* ----------------------------------- */
1586 static int new_rfork(const char *path, struct adouble *ad, int adflags)
1587 {
1588     const struct entry  *eid;
1589     u_int16_t           ashort;
1590     struct stat         st;
1591
1592     ad->ad_magic = AD_MAGIC;
1593     ad->ad_version = ad->ad_flags & 0x0f0000;
1594     if (!ad->ad_version) {
1595         ad->ad_version = AD_VERSION;
1596     }
1597
1598     memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
1599     memset(ad->ad_data, 0, sizeof(ad->ad_data));
1600
1601 #if AD_VERSION == AD_VERSION2
1602     if (ad->ad_flags == AD_VERSION2)
1603        eid = entry_order2;
1604     else if (ad->ad_flags == AD_VERSION2_OSX)
1605        eid = entry_order_osx;
1606     else  if (ad->ad_flags == AD_VERSION1_SFM) {
1607        ad->ad_magic = SFM_MAGIC;
1608        eid = entry_order_sfm;
1609     }
1610     else
1611 #endif
1612        eid = entry_order1;
1613
1614     while (eid->id) {
1615         ad->ad_eid[eid->id].ade_off = eid->offset;
1616         ad->ad_eid[eid->id].ade_len = eid->len;
1617         eid++;
1618     }
1619             
1620     /* put something sane in the directory finderinfo */
1621     if ((adflags & ADFLAGS_DIR)) {
1622         /* set default view */
1623         ashort = htons(FINDERINFO_CLOSEDVIEW);
1624         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
1625                      &ashort, sizeof(ashort));
1626     } else {
1627         /* set default creator/type fields */
1628         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
1629         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
1630     }
1631
1632     /* make things invisible */
1633     if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) && 
1634            (*path == '.') && strcmp(path, ".") && strcmp(path, "..")) 
1635     {
1636         ashort = htons(ATTRBIT_INVISIBLE);
1637         ad_setattr(ad, ashort);
1638         ashort = htons(FINDERINFO_INVISIBLE);
1639         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1640     }
1641
1642     if (stat(path, &st) < 0) {
1643         return -1;
1644     }
1645             
1646     /* put something sane in the date fields */
1647     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
1648     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
1649     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
1650     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
1651     return 0;
1652 }
1653
1654 /* to do this with mmap, we need the hfs fs to understand how to mmap
1655    header files. */
1656 int ad_refresh(struct adouble *ad)
1657 {
1658
1659   if (ad_meta_fileno(ad) < 0)
1660     return -1;
1661
1662   return ad->ad_ops->ad_header_read(ad, NULL);
1663 }