]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
d7251d25702d304cb801b696c25e4f95fa2b597c
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * $Id: ad_open.c,v 1.44 2009-07-20 23:23:02 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 <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_sfm( 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     if ((adflags == ADFLAGS_RF)) {
918         strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
919     }
920     else {
921         strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
922     }
923     return( pathbuf );
924 }
925
926 /* -------------------- */
927 static int ad_mkrf_sfm(char *path)
928 {
929     char *slash;
930     /*
931      * Probably .AppleDouble doesn't exist, try to mkdir it.
932      */
933      if (NULL == ( slash = strrchr( path, '/' )) ) {
934          return -1;
935      }
936      *slash = 0;
937      errno = 0;
938      if ( ad_mkdir( path, 0777 ) < 0 ) {
939          if ( errno == ENOENT ) {
940              char *slash1;
941              
942              if (NULL == ( slash1 = strrchr( path, '/' )) ) 
943                  return -1;
944              errno = 0;
945              *slash1 = 0;
946              if ( ad_mkdir( path, 0777 ) < 0 ) 
947                   return -1;
948              *slash1 = '/';
949              if ( ad_mkdir( path, 0777 ) < 0 )
950                  return -1;
951          }
952          else
953             return -1;
954      }     
955      *slash = '/';
956      return 0;
957 }
958
959 /* -------------------------
960  * Support inherited protection modes for AppleDouble files.  The supplied
961  * mode is ANDed with the parent directory's mask value in lieu of "umask",
962  * and that value is returned.
963  */
964
965 #define DEFMASK 07700   /* be conservative */
966
967 char 
968 *ad_dir(path)
969     const char          *path;
970 {
971     static char         modebuf[ MAXPATHLEN + 1];
972     char                *slash;
973     size_t              len;
974
975     if ( (len = strlen( path )) >= MAXPATHLEN ) {
976         errno = ENAMETOOLONG;
977         return NULL;  /* can't do it */
978     }
979
980     /*
981      * For a path with directories in it, remove the final component
982      * (path or subdirectory name) to get the name we want to stat.
983      * For a path which is just a filename, use "." instead.
984      */
985     strcpy( modebuf, path );
986     slash = strrchr( modebuf, '/' );
987     /* is last char a '/' */
988     if (slash && slash[1] == 0) {
989         while (modebuf < slash && slash[-1] == '/') {
990             --slash;
991         }
992         if (modebuf < slash) {
993             *slash = '\0';              /* remove pathname component */
994             slash = strrchr( modebuf, '/' );
995         }
996     }
997     if (slash) {
998         *slash = '\0';          /* remove pathname component */
999     } else {
1000         modebuf[0] = '.';       /* use current directory */
1001         modebuf[1] = '\0';
1002     }
1003     return modebuf;
1004 }
1005
1006 /* ---------------- */
1007 static uid_t default_uid = -1;
1008
1009 int ad_setfuid(const uid_t id)
1010 {
1011     default_uid = id;
1012     return 0;
1013 }
1014
1015 /* ---------------- */
1016 uid_t ad_getfuid(void) 
1017 {
1018     return default_uid;
1019 }
1020
1021 /* ---------------- 
1022    return inode of path parent directory
1023 */
1024 int ad_stat(const char *path, struct stat *stbuf)
1025 {
1026     char                *p;
1027
1028     p = ad_dir(path);
1029     if (!p) {
1030         return -1;
1031     }
1032
1033     return stat( p, stbuf );
1034 }
1035
1036 /* ---------------- 
1037    if we are root change path user/ group
1038    It can be a native function for BSD cf. FAQ.Q10
1039    path:  pathname to chown 
1040    stbuf: parent directory inode
1041    
1042    use fstat and fchown or lchown with linux?
1043 */
1044 #define EMULATE_SUIDDIR
1045  
1046 static int ad_chown(const char *path, struct stat *stbuf)
1047 {
1048 int ret = 0;
1049 #ifdef EMULATE_SUIDDIR
1050 uid_t id;
1051
1052     if (default_uid != (uid_t)-1) {  
1053         /* we are root (admin) */
1054         id = (default_uid)?default_uid:stbuf->st_uid;
1055         ret = chown( path, id, stbuf->st_gid );
1056     }
1057 #endif    
1058     return ret;
1059 }
1060
1061 /* ---------------- 
1062    return access right and inode of path parent directory
1063 */
1064 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
1065 {
1066     if (*mode == 0) {
1067        return -1;
1068     }
1069     if (ad_stat(path, stbuf) != 0) {
1070         *mode &= DEFMASK;
1071         return -1;
1072     }
1073     *mode &= stbuf->st_mode;
1074     return 0;    
1075 }
1076
1077 /* ---------------- 
1078    return access right of path parent directory
1079 */
1080 int
1081 ad_mode( path, mode )
1082     const char          *path;
1083     int                 mode;
1084 {
1085     struct stat         stbuf;
1086     ad_mode_st(path, &mode, &stbuf);
1087     return mode;
1088 }
1089
1090 /*
1091  * Use mkdir() with mode bits taken from ad_mode().
1092  */
1093 int
1094 ad_mkdir( path, mode )
1095     const char          *path;
1096     int                 mode;
1097 {
1098 int ret;
1099 int st_invalid;
1100 struct stat stbuf;
1101
1102 #ifdef DEBUG
1103     LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
1104 #endif /* DEBUG */
1105
1106     st_invalid = ad_mode_st(path, &mode, &stbuf);
1107     ret = mkdir( path, mode );
1108     if (ret || st_invalid)
1109         return ret;
1110     ad_chown(path, &stbuf);
1111
1112     return ret;    
1113 }
1114
1115 /* ----------------- */
1116 static int ad_error(struct adouble *ad, int adflags)
1117 {
1118 int err = errno;
1119     if ((adflags & ADFLAGS_NOHF)) {
1120         /* FIXME double check : set header offset ?*/
1121         return 0;
1122     }
1123     if ((adflags & ADFLAGS_DF)) {
1124         ad_close( ad, ADFLAGS_DF );
1125         err = errno;
1126     }
1127     return -1 ;
1128 }
1129
1130 static int new_rfork(const char *path, struct adouble *ad, int adflags);
1131
1132 #ifdef  HAVE_PREAD
1133 #define AD_SET(a) 
1134 #else 
1135 #define AD_SET(a) a = 0
1136 #endif
1137
1138 /* --------------------------- */
1139 static int ad_check_size(struct adouble *ad, struct stat *st)
1140 {
1141    if (st->st_size > 0 && st->st_size < AD_DATASZ1) 
1142         return 1;
1143    return 0;
1144 }
1145
1146 /* --------------------------- */
1147 static int ad_check_size_sfm(struct adouble *ad, struct stat *st)
1148 {
1149    if (st->st_size > 0 && st->st_size < AD_SFM_LEN) 
1150         return 1;
1151    return 0;
1152 }
1153
1154 /* --------------------------- */
1155 static int ad_header_upgrade(struct adouble *ad, char *name)
1156 {
1157 #if AD_VERSION == AD_VERSION2
1158     int ret;
1159     if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
1160        return ret;
1161     }
1162 #endif
1163     return 0;
1164 }
1165
1166 /* --------------------------- */
1167 static int ad_header_upgrade_none(struct adouble *ad, char *name)
1168 {
1169     return 0;
1170 }
1171
1172 /* --------------------------- */
1173 static struct adouble_fops ad_osx = {
1174         &ad_path_osx,
1175         &ad_mkrf_osx,
1176         &ad_rebuild_adouble_header,
1177         &ad_check_size,
1178
1179         &ad_header_read,
1180         &ad_header_upgrade,        
1181 };
1182
1183 static struct adouble_fops ad_sfm = {
1184         &ad_path_sfm,
1185         &ad_mkrf_sfm,
1186         &ad_rebuild_sfm_header,
1187         &ad_check_size_sfm,
1188         
1189         &ad_header_sfm_read,
1190         &ad_header_upgrade_none,
1191 };
1192
1193 static struct adouble_fops ad_adouble = {
1194         &ad_path,
1195         &ad_mkrf,
1196         &ad_rebuild_adouble_header,
1197         &ad_check_size,
1198
1199         &ad_header_read,
1200         &ad_header_upgrade,        
1201 };
1202
1203
1204 void ad_init(struct adouble *ad, int flags, int options)
1205 {
1206     memset( ad, 0, sizeof( struct adouble ) );
1207     ad->ad_flags = flags;
1208     if (flags == AD_VERSION2_OSX) {
1209         ad->ad_ops = &ad_osx;
1210     }
1211     else if (flags == AD_VERSION1_SFM) {
1212         ad->ad_ops = &ad_sfm;
1213     }
1214     else {
1215         ad->ad_ops = &ad_adouble;
1216     }
1217     ad->ad_options = options;
1218 }
1219
1220 /* -------------------
1221  * It's not possible to open the header file O_RDONLY -- the read
1222  * will fail and return an error. this refcounts things now. 
1223  */
1224 int ad_open( path, adflags, oflags, mode, ad )
1225     const char          *path;
1226     int                 adflags, oflags, mode;
1227     struct adouble      *ad;
1228 {
1229     struct stat         st_dir;
1230     struct stat         st_meta;
1231     struct stat         *pst = NULL;
1232     char                *ad_p;
1233     int                 hoflags, admode;
1234     int                 st_invalid = -1;
1235     int                 open_df = 0;
1236     
1237     if (ad->ad_inited != AD_INITED) {
1238         ad_data_fileno(ad) = -1;
1239         ad_reso_fileno(ad) = -1;
1240         adf_lock_init(&ad->ad_data_fork);
1241         adf_lock_init(&ad->ad_resource_fork);
1242         if (ad->ad_flags != AD_VERSION1_SFM) {
1243                 ad->ad_md = &ad->ad_resource_fork;
1244         }
1245         else {
1246                 adf_lock_init(&ad->ad_metadata_fork);
1247                 ad->ad_md = &ad->ad_metadata_fork;
1248                 ad_meta_fileno(ad) = -1;
1249         }
1250         ad->ad_inited = AD_INITED;
1251         ad->ad_refcount = 1;
1252         ad->ad_open_forks = 0;
1253         ad->ad_adflags = adflags;
1254     }
1255     else {
1256         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1257         /* XXX not true if we have a meta data fork ? */
1258         if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
1259                 ad->ad_open_forks |= ATTRBIT_ROPEN;
1260     }
1261
1262     if ((adflags & ADFLAGS_DF)) { 
1263         if (ad_data_fileno(ad) == -1) {
1264           hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1265           admode = mode;
1266           if ((oflags & O_CREAT)) {
1267               st_invalid = ad_mode_st(path, &admode, &st_dir);
1268               if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1269                   admode = mode;
1270               }
1271           }
1272           ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
1273           if (ad->ad_data_fork.adf_fd < 0 ) {
1274              if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1275                 hoflags = oflags;
1276                 ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
1277              }
1278           }
1279           if ( ad->ad_data_fork.adf_fd < 0)
1280                 return -1;
1281
1282           AD_SET(ad->ad_data_fork.adf_off);
1283           ad->ad_data_fork.adf_flags = hoflags;
1284           if (!st_invalid) {
1285               /* just created, set owner if admin (root) */
1286               ad_chown(path, &st_dir);
1287           }
1288         } 
1289         else {
1290             /* the file is already open... but */
1291             if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
1292                 !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
1293             {
1294                  errno = EACCES;
1295                  return -1;
1296             }
1297             /* FIXME 
1298              * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
1299              * already open. Should we check for it? ie
1300              * O_EXCL --> error 
1301              * O_TRUNC --> truncate the fork.
1302              * idem for ressource fork.
1303              */
1304         }
1305         open_df = ADFLAGS_DF;
1306         ad->ad_data_fork.adf_refcount++;
1307     }
1308
1309     if (!(adflags & ADFLAGS_HF)) 
1310         return 0;
1311
1312     /* ****************************************** */
1313         
1314     if (ad_meta_fileno(ad) != -1) { /* the file is already open */
1315         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
1316                 !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
1317             if (open_df) {
1318                 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
1319                 ad_close( ad, open_df );
1320             }
1321             errno = EACCES;
1322             return -1;
1323         }
1324         ad_refresh(ad);
1325         ad->ad_md->adf_refcount++;
1326         goto sfm;
1327     }
1328
1329     ad_p = ad->ad_ops->ad_path( path, adflags );
1330
1331     hoflags = oflags & ~O_CREAT;
1332     if (!(adflags & ADFLAGS_RDONLY)) {
1333         hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1334     }
1335     ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1336     if (ad->ad_md->adf_fd < 0 ) {
1337         if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1338             hoflags = oflags & ~O_CREAT;
1339             ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1340         }    
1341     }
1342
1343     if ( ad->ad_md->adf_fd < 0 ) { 
1344         if (errno == ENOENT && (oflags & O_CREAT) ) {
1345             /*
1346              * We're expecting to create a new adouble header file,
1347              * here.
1348              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
1349              */
1350             admode = mode;
1351             errno = 0;
1352             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1353             if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1354                 admode = mode;
1355             }
1356             admode = ad_hf_mode(admode); 
1357             if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
1358                 if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
1359                     return ad_error(ad, adflags);
1360                 }
1361                 admode = mode;
1362                 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1363                 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1364                     admode = mode;
1365                 }
1366                 admode = ad_hf_mode(admode); 
1367             }
1368             /* retry with O_CREAT */
1369             ad->ad_md->adf_fd = open( ad_p, oflags,admode );
1370             if ( ad->ad_md->adf_fd < 0 ) {
1371                 return ad_error(ad, adflags);
1372             }
1373             ad->ad_md->adf_flags = oflags;
1374             /* just created, set owner if admin owner (root) */
1375             if (!st_invalid) {
1376                 ad_chown(ad_p, &st_dir);
1377             }
1378         }
1379         else {
1380             return ad_error(ad, adflags);
1381         }
1382     } else if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
1383         /* for 0 length files, treat them as new. */
1384         ad->ad_md->adf_flags = hoflags| O_TRUNC;
1385     } else {
1386         ad->ad_md->adf_flags = hoflags;
1387     }
1388     AD_SET(ad->ad_md->adf_off);
1389           
1390     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1391     ad->ad_md->adf_refcount++;
1392     if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
1393         /*
1394          * This is a new adouble header file. Initialize the structure,
1395          * instead of reading it.
1396         */
1397         if (new_rfork(path, ad, adflags) < 0) {
1398             int err = errno;
1399             /* the file is already deleted, perm, whatever, so return an error*/
1400             ad_close(ad, adflags);
1401             errno = err;
1402             return -1;
1403         }
1404         ad_flush(ad);
1405     } else {
1406         /* Read the adouble header in and parse it.*/
1407         if (ad->ad_ops->ad_header_read( ad , pst) < 0
1408                 || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) 
1409         {
1410             int err = errno;
1411             
1412             ad_close( ad, adflags );
1413             errno = err;
1414             return -1;
1415         }
1416     }
1417
1418     /* ****************************************** */
1419     /* open the resource fork if SFM */
1420 sfm:
1421     if (ad->ad_flags != AD_VERSION1_SFM) {
1422         return 0;
1423     }
1424
1425     if ((adflags & ADFLAGS_DIR)) {
1426         /* no resource fork for directories / volumes XXX it's false! */
1427         return 0;
1428     }
1429
1430     /* untrue yet but ad_close will decremente it*/
1431     ad->ad_resource_fork.adf_refcount++;
1432
1433     if (ad_reso_fileno(ad) != -1) { /* the file is already open */
1434         if ((oflags & ( O_RDWR | O_WRONLY)) &&             
1435                 !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1436
1437             ad_close( ad, open_df | ADFLAGS_HF);
1438             errno = EACCES;
1439             return -1;
1440         }
1441         return 0;
1442     }
1443
1444     ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
1445
1446     hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1447     ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
1448     admode = mode;
1449     st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1450     
1451     if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1452        admode = mode;
1453     }
1454      
1455     if (ad->ad_resource_fork.adf_fd < 0 ) {
1456        if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1457           hoflags = oflags;
1458           ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
1459        }
1460     }
1461
1462     if ( ad->ad_resource_fork.adf_fd < 0) {
1463        int err = errno;
1464             
1465        ad_close( ad, adflags );
1466        errno = err;
1467        return -1;
1468     }
1469                         
1470     AD_SET(ad->ad_resource_fork.adf_off);
1471     ad->ad_resource_fork.adf_flags = hoflags;
1472     if ((oflags & O_CREAT) && !st_invalid) {
1473         /* just created, set owner if admin (root) */
1474         ad_chown(ad_p, &st_dir);
1475     }
1476     else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
1477         ad->ad_rlen = st_meta.st_size;
1478     }
1479     return 0 ;
1480 }
1481
1482 /* ----------------------------------- 
1483  * return only metadata but try very hard
1484 */
1485 int ad_metadata(const char *name, int flags, struct adouble *adp)
1486 {
1487     uid_t uid;
1488     int   ret, err;
1489     int   dir = flags & ADFLAGS_DIR;
1490
1491     /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */
1492     /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */
1493     if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) {
1494         uid = geteuid();
1495         if (seteuid(0)) {
1496             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1497             errno = EACCES;
1498             return -1;
1499         }
1500         /* we are root open read only */
1501         ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
1502         err = errno;
1503         if ( seteuid(uid) < 0) {
1504             LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1505             exit(EXITERR_SYS);
1506         }
1507         errno = err;
1508     }
1509
1510     if (!ret && (ADFLAGS_OPENFORKS & flags)) {
1511         u_int16_t attrbits = adp->ad_open_forks;
1512
1513         /* 
1514            we need to check if the file is open by another process.
1515            it's slow so we only do it if we have to:
1516            - it's requested.
1517            - we don't already have the answer!
1518         */
1519         
1520         if (!(attrbits & ATTRBIT_ROPEN)) {
1521             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_ROPEN : 0;
1522             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_ROPEN : 0;
1523         }
1524
1525         if (!(attrbits & ATTRBIT_DOPEN)) {
1526             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_DOPEN : 0;
1527             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_DOPEN : 0;
1528         }
1529         adp->ad_open_forks = attrbits;
1530     }
1531     return ret;
1532 }
1533
1534 /* ----------------------------------- */
1535 static int new_rfork(const char *path, struct adouble *ad, int adflags)
1536 {
1537     const struct entry  *eid;
1538     u_int16_t           ashort;
1539     struct stat         st;
1540
1541     ad->ad_magic = AD_MAGIC;
1542     ad->ad_version = ad->ad_flags & 0x0f0000;
1543     if (!ad->ad_version) {
1544         ad->ad_version = AD_VERSION;
1545     }
1546
1547     memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
1548     memset(ad->ad_data, 0, sizeof(ad->ad_data));
1549
1550 #if AD_VERSION == AD_VERSION2
1551     if (ad->ad_flags == AD_VERSION2)
1552        eid = entry_order2;
1553     else if (ad->ad_flags == AD_VERSION2_OSX)
1554        eid = entry_order_osx;
1555     else  if (ad->ad_flags == AD_VERSION1_SFM) {
1556        ad->ad_magic = SFM_MAGIC;
1557        eid = entry_order_sfm;
1558     }
1559     else
1560 #endif
1561        eid = entry_order1;
1562
1563     while (eid->id) {
1564         ad->ad_eid[eid->id].ade_off = eid->offset;
1565         ad->ad_eid[eid->id].ade_len = eid->len;
1566         eid++;
1567     }
1568             
1569     /* put something sane in the directory finderinfo */
1570     if ((adflags & ADFLAGS_DIR)) {
1571         /* set default view */
1572         ashort = htons(FINDERINFO_CLOSEDVIEW);
1573         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
1574                      &ashort, sizeof(ashort));
1575     } else {
1576         /* set default creator/type fields */
1577         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
1578         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
1579     }
1580
1581     /* make things invisible */
1582     if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) && 
1583            (*path == '.') && strcmp(path, ".") && strcmp(path, "..")) 
1584     {
1585         ashort = htons(ATTRBIT_INVISIBLE);
1586         ad_setattr(ad, ashort);
1587         ashort = htons(FINDERINFO_INVISIBLE);
1588         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1589     }
1590
1591     if (stat(path, &st) < 0) {
1592         return -1;
1593     }
1594             
1595     /* put something sane in the date fields */
1596     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
1597     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
1598     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
1599     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
1600     return 0;
1601 }
1602
1603 /* to do this with mmap, we need the hfs fs to understand how to mmap
1604    header files. */
1605 int ad_refresh(struct adouble *ad)
1606 {
1607
1608   if (ad_meta_fileno(ad) < 0)
1609     return -1;
1610
1611   return ad->ad_ops->ad_header_read(ad, NULL);
1612 }