]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_open.c
remove obsolete ATACC ifdef
[netatalk.git] / libatalk / adouble / ad_open.c
1 /*
2  * $Id: ad_open.c,v 1.47 2009-09-14 00:02:21 didg Exp $
3  *
4  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
5  * Copyright (c) 1990,1991 Regents of The University of Michigan.
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software and
9  * its documentation for any purpose and without fee is hereby granted,
10  * provided that the above copyright notice appears in all copies and
11  * that both that copyright notice and this permission notice appear
12  * in supporting documentation, and that the name of The University
13  * of Michigan not be used in advertising or publicity pertaining to
14  * distribution of the software without specific, written prior
15  * permission. This software is supplied as is without expressed or
16  * implied warranties of any kind.
17  *
18  *  Research Systems Unix Group
19  *  The University of Michigan
20  *  c/o Mike Clark
21  *  535 W. William Street
22  *  Ann Arbor, Michigan
23  *  +1-313-763-0525
24  *  netatalk@itd.umich.edu
25  *
26  * NOTE: I don't use inline because a good compiler should be
27  * able to optimize all the static below. Didier
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif /* HAVE_CONFIG_H */
33
34 #include <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    read in the entries
487 */
488 static void parse_entries(struct adouble *ad, char *buf,
489                           u_int16_t nentries)
490 {
491     u_int32_t   eid, len, off;
492     int         warning = 0;
493
494     /* now, read in the entry bits */
495     for (; nentries > 0; nentries-- ) {
496         memcpy(&eid, buf, sizeof( eid ));
497         eid = DISK_EID(ad, ntohl( eid ));
498         buf += sizeof( eid );
499         memcpy(&off, buf, sizeof( off ));
500         off = ntohl( off );
501         buf += sizeof( off );
502         memcpy(&len, buf, sizeof( len ));
503         len = ntohl( len );
504         buf += sizeof( len );
505
506         if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) &&
507             (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
508             ad->ad_eid[ eid ].ade_off = off;
509             ad->ad_eid[ eid ].ade_len = len;
510         } else if (!warning) {
511             warning = 1;
512             LOG(log_debug, logtype_default, "ad_refresh: nentries %hd  eid %d",
513                 nentries, eid );
514         }
515     }
516 }
517
518
519 /* this reads enough of the header so that we can figure out all of
520  * the entry lengths and offsets. once that's done, we just read/mmap
521  * the rest of the header in.
522  *
523  * NOTE: we're assuming that the resource fork is kept at the end of
524  *       the file. also, mmapping won't work for the hfs fs until it
525  *       understands how to mmap header files. */
526 static int ad_header_read(struct adouble *ad, struct stat *hst)
527 {
528     char                *buf = ad->ad_data;
529     u_int16_t           nentries;
530     int                 len;
531     ssize_t             header_len;
532     static int          warning = 0;
533     struct stat         st;
534
535     /* read the header */
536     if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
537         return -1;
538     }
539     if (header_len < AD_HEADER_LEN) {
540         errno = EIO;
541         return -1;
542     }
543
544     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
545     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
546
547     /* tag broken v1 headers. just assume they're all right.
548      * we detect two cases: null magic/version
549      *                      byte swapped magic/version
550      * XXX: in the future, you'll need the v1compat flag.
551      * (ad->ad_flags & ADFLAGS_V1COMPAT) */
552     if (!ad->ad_magic && !ad->ad_version) {
553         if (!warning) {
554             LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
555             warning++;
556         }
557         ad->ad_magic = AD_MAGIC;
558         ad->ad_version = AD_VERSION1;
559
560     } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
561         if (!warning) {
562             LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
563             warning++;
564         }
565
566     } else {
567         ad->ad_magic = ntohl( ad->ad_magic );
568         ad->ad_version = ntohl( ad->ad_version );
569     }
570
571     if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
572 #if AD_VERSION == AD_VERSION2
573                                        && (ad->ad_version != AD_VERSION2)
574 #endif /* AD_VERSION == AD_VERSION2 */
575             )) {
576         errno = EIO;
577         LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
578         return -1;
579     }
580
581     memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
582     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
583     nentries = ntohs( nentries );
584
585     /* read in all the entry headers. if we have more than the
586      * maximum, just hope that the rfork is specified early on. */
587     len = nentries*AD_ENTRY_LEN;
588
589     if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
590         len = sizeof(ad->ad_data) - AD_HEADER_LEN;
591
592     buf += AD_HEADER_LEN;
593     if (len > header_len - AD_HEADER_LEN) {
594         errno = EIO;
595         LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
596         return -1;
597     }
598
599     /* figure out all of the entry offsets and lengths. if we aren't
600      * able to read a resource fork entry, bail. */
601     nentries = len / AD_ENTRY_LEN;
602     parse_entries(ad, buf, nentries);
603     if (!ad_getentryoff(ad, ADEID_RFORK)
604         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
605         ) {
606         errno = EIO;
607         LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
608         return -1;
609     }
610
611     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
612         errno = EIO;
613         LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
614         return -1;
615     }
616
617     if (hst == NULL) {
618         hst = &st;
619         if (fstat(ad->ad_md->adf_fd, &st) < 0) {
620             return 1; /* fail silently */
621         }
622     }
623     ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
624
625     /* fix up broken dates */
626     if (ad->ad_version == AD_VERSION1) {
627         u_int32_t aint;
628
629         /* check to see if the ad date is wrong. just see if we have
630          * a modification date in the future. */
631         if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
632             (aint > TIMEWARP_DELTA + hst->st_mtime)) {
633             ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
634             ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
635             ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
636             ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
637             ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
638         }
639     }
640
641     return 0;
642 }
643
644 /* ---------------------------
645    SFM structure
646 */
647 #if 0
648 typedef struct {
649     byte    afpi_Signature[4];      /* Must be 0x00504641 */
650     byte    afpi_Version[4];        /* Must be 0x00010000 */
651     byte    afpi_Reserved1[4];
652     byte    afpi_BackupTime[4];     /* Backup time for the file/dir */
653     byte    finderinfo[32];         /* Finder info */
654     byte    afpi_ProDosInfo[6];     /* ProDos Info */
655     byte    afpi_Reserved2[6];
656 } sfm_info;
657 #endif
658
659 static int ad_header_sfm_read(struct adouble *ad, struct stat *hst)
660 {
661     char                *buf = ad->ad_data;
662     const struct entry  *eid;
663     ssize_t             header_len;
664     struct stat         st;
665
666     /* read the header */
667     if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
668         return -1;
669     }
670     if (header_len != AD_SFM_LEN) {
671         errno = EIO;
672         return -1;
673     }
674
675     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
676     memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version ));
677
678     /* FIXME in the great Microsoft tradition they aren't in network order */
679 #if 0
680     if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) {
681         static int          warning = 0;
682         if (!warning) {
683             LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
684             warning++;
685         }
686
687     } else {
688         ad->ad_magic = ntohl( ad->ad_magic );
689         ad->ad_version = ntohl( ad->ad_version );
690     }
691 #endif
692     if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) {
693         errno = EIO;
694         LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header.");
695         return -1;
696     }
697
698     /* reinit adouble table */
699     eid = entry_order_sfm;
700     while (eid->id) {
701         ad->ad_eid[eid->id].ade_off = eid->offset;
702         ad->ad_eid[eid->id].ade_len = eid->len;
703         eid++;
704     }
705
706     /* steal some prodos for attribute */
707     {
708
709         u_int16_t attribute;
710         memcpy(&attribute, buf + 48 +4, sizeof(attribute));
711         ad_setattr(ad, attribute );
712     }
713
714     if (ad->ad_resource_fork.adf_fd != -1) {
715         /* we have a resource fork use it rather than the metadata */
716         if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) {
717             /* XXX set to zero ?
718                ad->ad_rlen =  0;
719             */
720             return 1;
721         }
722         ad->ad_rlen = st.st_size;
723         hst = &st;
724     }
725     else if (hst == NULL) {
726         hst = &st;
727         if (fstat(ad->ad_md->adf_fd, &st) < 0) {
728             return 1; /* fail silently */
729         }
730     }
731
732     /* fix up broken dates */
733     if (ad->ad_version == AD_VERSION1) {
734         u_int32_t aint;
735
736         /* check to see if the ad date is wrong. just see if we have
737          * a modification date in the future. */
738         if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
739             (aint > TIMEWARP_DELTA + hst->st_mtime)) {
740             ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
741             ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
742             ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
743             ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
744             ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
745         }
746     }
747
748     return 0;
749 }
750
751 /* ---------------------------------------
752  * Put the .AppleDouble where it needs to be:
753  *
754  *      /   a/.AppleDouble/b
755  *  a/b
756  *      \   b/.AppleDouble/.Parent
757  *
758  * FIXME: should do something for pathname > MAXPATHLEN
759  */
760 char *
761 ad_path( path, adflags )
762     const char  *path;
763     int     adflags;
764 {
765     static char pathbuf[ MAXPATHLEN + 1];
766     char    c, *slash, buf[MAXPATHLEN + 1];
767     size_t      l;
768
769     l = strlcpy(buf, path, MAXPATHLEN +1);
770     if ( adflags & ADFLAGS_DIR ) {
771         strcpy( pathbuf, buf);
772         if ( *buf != '\0' && l < MAXPATHLEN) {
773             pathbuf[l++] = '/';
774             pathbuf[l] = 0;
775         }
776         slash = ".Parent";
777     } else {
778         if (NULL != ( slash = strrchr( buf, '/' )) ) {
779             c = *++slash;
780             *slash = '\0';
781             strcpy( pathbuf, buf);
782             *slash = c;
783         } else {
784             pathbuf[ 0 ] = '\0';
785             slash = buf;
786         }
787     }
788     strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
789     strlcat( pathbuf, slash, MAXPATHLEN +1);
790
791     return( pathbuf );
792 }
793
794 /* -------------------- */
795 static int ad_mkrf(char *path)
796 {
797     char *slash;
798     /*
799      * Probably .AppleDouble doesn't exist, try to mkdir it.
800      */
801     if (NULL == ( slash = strrchr( path, '/' )) ) {
802         return -1;
803     }
804     *slash = '\0';
805     errno = 0;
806     if ( ad_mkdir( path, 0777 ) < 0 ) {
807         return -1;
808     }
809     *slash = '/';
810     return 0;
811 }
812
813 /* ---------------------------------------
814  * Put the resource fork where it needs to be:
815  * ._name
816  */
817 char *
818 ad_path_osx(const char *path, int adflags _U_)
819 {
820     static char pathbuf[ MAXPATHLEN + 1];
821     char    c, *slash, buf[MAXPATHLEN + 1];
822
823     if (!strcmp(path,".")) {
824         /* fixme */
825         getcwd(buf, MAXPATHLEN);
826     }
827     else {
828         strlcpy(buf, path, MAXPATHLEN +1);
829     }
830     if (NULL != ( slash = strrchr( buf, '/' )) ) {
831         c = *++slash;
832         *slash = '\0';
833         strlcpy( pathbuf, buf, MAXPATHLEN +1);
834         *slash = c;
835     } else {
836         pathbuf[ 0 ] = '\0';
837         slash = buf;
838     }
839     strlcat( pathbuf, "._", MAXPATHLEN  +1);
840     strlcat( pathbuf, slash, MAXPATHLEN +1);
841     return pathbuf;
842 }
843 /* -------------------- */
844 static int ad_mkrf_osx(char *path _U_)
845 {
846     return 0;
847 }
848
849 /* ---------------------------------------
850  * Put the .AppleDouble where it needs to be:
851  *
852  *      /   a/.AppleDouble/b/AFP_AfpInfo
853  *  a/b
854  *      \   b/.AppleDouble/.Parent/AFP_AfpInfo
855  *
856  */
857 char *
858 ad_path_sfm( path, adflags )
859     const char  *path;
860     int     adflags;
861 {
862     static char pathbuf[ MAXPATHLEN + 1];
863     char    c, *slash, buf[MAXPATHLEN + 1];
864     size_t      l;
865
866     l = strlcpy(buf, path, MAXPATHLEN +1);
867     if ( adflags & ADFLAGS_DIR ) {
868         strcpy( pathbuf, buf);
869         if ( *buf != '\0' && l < MAXPATHLEN) {
870             pathbuf[l++] = '/';
871             pathbuf[l] = 0;
872         }
873         slash = ".Parent";
874     } else {
875         if (NULL != ( slash = strrchr( buf, '/' )) ) {
876             c = *++slash;
877             *slash = '\0';
878             strcpy( pathbuf, buf);
879             *slash = c;
880         } else {
881             pathbuf[ 0 ] = '\0';
882             slash = buf;
883         }
884     }
885     strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
886     strlcat( pathbuf, slash, MAXPATHLEN +1);
887
888     if ((adflags == ADFLAGS_RF)) {
889         strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
890     }
891     else {
892         strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
893     }
894     return( pathbuf );
895 }
896
897 /* -------------------- */
898 static int ad_mkrf_sfm(char *path)
899 {
900     char *slash;
901     /*
902      * Probably .AppleDouble doesn't exist, try to mkdir it.
903      */
904     if (NULL == ( slash = strrchr( path, '/' )) ) {
905         return -1;
906     }
907     *slash = 0;
908     errno = 0;
909     if ( ad_mkdir( path, 0777 ) < 0 ) {
910         if ( errno == ENOENT ) {
911             char *slash1;
912
913             if (NULL == ( slash1 = strrchr( path, '/' )) )
914                 return -1;
915             errno = 0;
916             *slash1 = 0;
917             if ( ad_mkdir( path, 0777 ) < 0 )
918                 return -1;
919             *slash1 = '/';
920             if ( ad_mkdir( path, 0777 ) < 0 )
921                 return -1;
922         }
923         else
924             return -1;
925     }
926     *slash = '/';
927     return 0;
928 }
929
930 /* -------------------------
931  * Support inherited protection modes for AppleDouble files.  The supplied
932  * mode is ANDed with the parent directory's mask value in lieu of "umask",
933  * and that value is returned.
934  */
935
936 #define DEFMASK 07700   /* be conservative */
937
938 char
939 *ad_dir(path)
940     const char      *path;
941 {
942     static char     modebuf[ MAXPATHLEN + 1];
943     char        *slash;
944     size_t              len;
945
946     if ( (len = strlen( path )) >= MAXPATHLEN ) {
947         errno = ENAMETOOLONG;
948         return NULL;  /* can't do it */
949     }
950
951     /*
952      * For a path with directories in it, remove the final component
953      * (path or subdirectory name) to get the name we want to stat.
954      * For a path which is just a filename, use "." instead.
955      */
956     strcpy( modebuf, path );
957     slash = strrchr( modebuf, '/' );
958     /* is last char a '/' */
959     if (slash && slash[1] == 0) {
960         while (modebuf < slash && slash[-1] == '/') {
961             --slash;
962         }
963         if (modebuf < slash) {
964             *slash = '\0';      /* remove pathname component */
965             slash = strrchr( modebuf, '/' );
966         }
967     }
968     if (slash) {
969         *slash = '\0';      /* remove pathname component */
970     } else {
971         modebuf[0] = '.';   /* use current directory */
972         modebuf[1] = '\0';
973     }
974     return modebuf;
975 }
976
977 /* ---------------- */
978 static uid_t default_uid = -1;
979
980 int ad_setfuid(const uid_t id)
981 {
982     default_uid = id;
983     return 0;
984 }
985
986 /* ---------------- */
987 uid_t ad_getfuid(void)
988 {
989     return default_uid;
990 }
991
992 /* ----------------
993    return inode of path parent directory
994 */
995 int ad_stat(const char *path, struct stat *stbuf)
996 {
997     char                *p;
998
999     p = ad_dir(path);
1000     if (!p) {
1001         return -1;
1002     }
1003
1004     return stat( p, stbuf );
1005 }
1006
1007 /* ----------------
1008    if we are root change path user/ group
1009    It can be a native function for BSD cf. FAQ.Q10
1010    path:  pathname to chown
1011    stbuf: parent directory inode
1012
1013    use fstat and fchown or lchown with linux?
1014 */
1015 #define EMULATE_SUIDDIR
1016
1017 static int ad_chown(const char *path, struct stat *stbuf)
1018 {
1019     int ret = 0;
1020 #ifdef EMULATE_SUIDDIR
1021     uid_t id;
1022
1023     if (default_uid != (uid_t)-1) {
1024         /* we are root (admin) */
1025         id = (default_uid)?default_uid:stbuf->st_uid;
1026         ret = chown( path, id, stbuf->st_gid );
1027     }
1028 #endif
1029     return ret;
1030 }
1031
1032 /* ----------------
1033    return access right and inode of path parent directory
1034 */
1035 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
1036 {
1037     if (*mode == 0) {
1038         return -1;
1039     }
1040     if (ad_stat(path, stbuf) != 0) {
1041         *mode &= DEFMASK;
1042         return -1;
1043     }
1044     *mode &= stbuf->st_mode;
1045     return 0;
1046 }
1047
1048 /* ----------------
1049    return access right of path parent directory
1050 */
1051 int
1052 ad_mode( path, mode )
1053     const char      *path;
1054     int         mode;
1055 {
1056     struct stat     stbuf;
1057     ad_mode_st(path, &mode, &stbuf);
1058     return mode;
1059 }
1060
1061 /*
1062  * Use mkdir() with mode bits taken from ad_mode().
1063  */
1064 int
1065 ad_mkdir( path, mode )
1066     const char      *path;
1067     int         mode;
1068 {
1069     int ret;
1070     int st_invalid;
1071     struct stat stbuf;
1072
1073 #ifdef DEBUG
1074     LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
1075 #endif /* DEBUG */
1076
1077     st_invalid = ad_mode_st(path, &mode, &stbuf);
1078     ret = mkdir( path, mode );
1079     if (ret || st_invalid)
1080         return ret;
1081     ad_chown(path, &stbuf);
1082
1083     return ret;
1084 }
1085
1086 /* ----------------- */
1087 static int ad_error(struct adouble *ad, int adflags)
1088 {
1089     int err = errno;
1090     if ((adflags & ADFLAGS_NOHF)) {
1091         /* FIXME double check : set header offset ?*/
1092         return 0;
1093     }
1094     if ((adflags & ADFLAGS_DF)) {
1095         ad_close( ad, ADFLAGS_DF );
1096         err = errno;
1097     }
1098     return -1 ;
1099 }
1100
1101 static int new_rfork(const char *path, struct adouble *ad, int adflags);
1102
1103 #ifdef  HAVE_PREAD
1104 #define AD_SET(a)
1105 #else
1106 #define AD_SET(a) a = 0
1107 #endif
1108
1109 /* --------------------------- */
1110 static int ad_check_size(struct adouble *ad _U_, struct stat *st)
1111 {
1112     if (st->st_size > 0 && st->st_size < AD_DATASZ1)
1113         return 1;
1114     return 0;
1115 }
1116
1117 /* --------------------------- */
1118 static int ad_check_size_sfm(struct adouble *ad _U_, struct stat *st)
1119 {
1120     if (st->st_size > 0 && st->st_size < AD_SFM_LEN)
1121         return 1;
1122     return 0;
1123 }
1124
1125 /* --------------------------- */
1126 static int ad_header_upgrade(struct adouble *ad, char *name)
1127 {
1128 #if AD_VERSION == AD_VERSION2
1129     int ret;
1130     if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
1131         return ret;
1132     }
1133 #endif
1134     return 0;
1135 }
1136
1137 /* --------------------------- */
1138 static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_)
1139 {
1140     return 0;
1141 }
1142
1143 /* --------------------------- */
1144 static struct adouble_fops ad_osx = {
1145     &ad_path_osx,
1146     &ad_mkrf_osx,
1147     &ad_rebuild_adouble_header,
1148     &ad_check_size,
1149
1150     &ad_header_read,
1151     &ad_header_upgrade,
1152 };
1153
1154 static struct adouble_fops ad_sfm = {
1155     &ad_path_sfm,
1156     &ad_mkrf_sfm,
1157     &ad_rebuild_sfm_header,
1158     &ad_check_size_sfm,
1159
1160     &ad_header_sfm_read,
1161     &ad_header_upgrade_none,
1162 };
1163
1164 static struct adouble_fops ad_adouble = {
1165     &ad_path,
1166     &ad_mkrf,
1167     &ad_rebuild_adouble_header,
1168     &ad_check_size,
1169
1170     &ad_header_read,
1171     &ad_header_upgrade,
1172 };
1173
1174
1175 void ad_init(struct adouble *ad, int flags, int options)
1176 {
1177     memset( ad, 0, sizeof( struct adouble ) );
1178     ad->ad_flags = flags;
1179     if (flags == AD_VERSION2_OSX) {
1180         ad->ad_ops = &ad_osx;
1181     }
1182     else if (flags == AD_VERSION1_SFM) {
1183         ad->ad_ops = &ad_sfm;
1184     }
1185     else {
1186         ad->ad_ops = &ad_adouble;
1187     }
1188     ad->ad_options = options;
1189 }
1190
1191 /* -------------------
1192  * It's not possible to open the header file O_RDONLY -- the read
1193  * will fail and return an error. this refcounts things now.
1194  */
1195 int ad_open( path, adflags, oflags, mode, ad )
1196     const char      *path;
1197     int         adflags, oflags, mode;
1198     struct adouble  *ad;
1199 {
1200     struct stat         st_dir;
1201     struct stat         st_meta;
1202     struct stat         *pst = NULL;
1203     char        *ad_p;
1204     int         hoflags, admode;
1205     int                 st_invalid = -1;
1206     int                 open_df = 0;
1207
1208     if (ad->ad_inited != AD_INITED) {
1209         ad_data_fileno(ad) = -1;
1210         ad_reso_fileno(ad) = -1;
1211         adf_lock_init(&ad->ad_data_fork);
1212         adf_lock_init(&ad->ad_resource_fork);
1213         if (ad->ad_flags != AD_VERSION1_SFM) {
1214             ad->ad_md = &ad->ad_resource_fork;
1215         }
1216         else {
1217             adf_lock_init(&ad->ad_metadata_fork);
1218             ad->ad_md = &ad->ad_metadata_fork;
1219             ad_meta_fileno(ad) = -1;
1220         }
1221         ad->ad_inited = AD_INITED;
1222         ad->ad_refcount = 1;
1223         ad->ad_open_forks = 0;
1224         ad->ad_adflags = adflags;
1225     }
1226     else {
1227         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1228         /* XXX not true if we have a meta data fork ? */
1229         if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
1230             ad->ad_open_forks |= ATTRBIT_ROPEN;
1231     }
1232
1233     if ((adflags & ADFLAGS_DF)) {
1234         if (ad_data_fileno(ad) == -1) {
1235             hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1236             admode = mode;
1237             if ((oflags & O_CREAT)) {
1238                 st_invalid = ad_mode_st(path, &admode, &st_dir);
1239                 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1240                     admode = mode;
1241                 }
1242             }
1243             ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
1244             if (ad->ad_data_fork.adf_fd < 0 ) {
1245                 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1246                     hoflags = oflags;
1247                     ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
1248                 }
1249             }
1250             if ( ad->ad_data_fork.adf_fd < 0)
1251                 return -1;
1252
1253             AD_SET(ad->ad_data_fork.adf_off);
1254             ad->ad_data_fork.adf_flags = hoflags;
1255             if (!st_invalid) {
1256                 /* just created, set owner if admin (root) */
1257                 ad_chown(path, &st_dir);
1258             }
1259         }
1260         else {
1261             /* the file is already open... but */
1262             if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
1263                 !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
1264             {
1265                 errno = EACCES;
1266                 return -1;
1267             }
1268             /* FIXME
1269              * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
1270              * already open. Should we check for it? ie
1271              * O_EXCL --> error
1272              * O_TRUNC --> truncate the fork.
1273              * idem for ressource fork.
1274              */
1275         }
1276         open_df = ADFLAGS_DF;
1277         ad->ad_data_fork.adf_refcount++;
1278     }
1279
1280     if (!(adflags & ADFLAGS_HF))
1281         return 0;
1282
1283     /* ****************************************** */
1284
1285     if (ad_meta_fileno(ad) != -1) { /* the file is already open */
1286         if ((oflags & ( O_RDWR | O_WRONLY)) &&
1287             !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
1288             if (open_df) {
1289                 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
1290                 ad_close( ad, open_df );
1291             }
1292             errno = EACCES;
1293             return -1;
1294         }
1295         ad_refresh(ad);
1296         ad->ad_md->adf_refcount++;
1297         goto sfm;
1298     }
1299
1300     ad_p = ad->ad_ops->ad_path( path, adflags );
1301
1302     hoflags = oflags & ~O_CREAT;
1303     if (!(adflags & ADFLAGS_RDONLY)) {
1304         hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1305     }
1306     ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1307     if (ad->ad_md->adf_fd < 0 ) {
1308         if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1309             hoflags = oflags & ~O_CREAT;
1310             ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1311         }
1312     }
1313
1314     if ( ad->ad_md->adf_fd < 0 ) {
1315         if (errno == ENOENT && (oflags & O_CREAT) ) {
1316             /*
1317              * We're expecting to create a new adouble header file,
1318              * here.
1319              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
1320              */
1321             admode = mode;
1322             errno = 0;
1323             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1324             if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1325                 admode = mode;
1326             }
1327             admode = ad_hf_mode(admode);
1328             if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
1329                 if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
1330                     return ad_error(ad, adflags);
1331                 }
1332                 admode = mode;
1333                 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1334                 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1335                     admode = mode;
1336                 }
1337                 admode = ad_hf_mode(admode);
1338             }
1339             /* retry with O_CREAT */
1340             ad->ad_md->adf_fd = open( ad_p, oflags,admode );
1341             if ( ad->ad_md->adf_fd < 0 ) {
1342                 return ad_error(ad, adflags);
1343             }
1344             ad->ad_md->adf_flags = oflags;
1345             /* just created, set owner if admin owner (root) */
1346             if (!st_invalid) {
1347                 ad_chown(ad_p, &st_dir);
1348             }
1349         }
1350         else {
1351             return ad_error(ad, adflags);
1352         }
1353     } else if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
1354         /* for 0 length files, treat them as new. */
1355         ad->ad_md->adf_flags = hoflags| O_TRUNC;
1356     } else {
1357         ad->ad_md->adf_flags = hoflags;
1358     }
1359     AD_SET(ad->ad_md->adf_off);
1360
1361     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1362     ad->ad_md->adf_refcount++;
1363     if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
1364         /*
1365          * This is a new adouble header file. Initialize the structure,
1366          * instead of reading it.
1367          */
1368         if (new_rfork(path, ad, adflags) < 0) {
1369             int err = errno;
1370             /* the file is already deleted, perm, whatever, so return an error*/
1371             ad_close(ad, adflags);
1372             errno = err;
1373             return -1;
1374         }
1375         ad_flush(ad);
1376     } else {
1377         /* Read the adouble header in and parse it.*/
1378         if (ad->ad_ops->ad_header_read( ad , pst) < 0
1379             || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0)
1380         {
1381             int err = errno;
1382
1383             ad_close( ad, adflags );
1384             errno = err;
1385             return -1;
1386         }
1387     }
1388
1389     /* ****************************************** */
1390     /* open the resource fork if SFM */
1391 sfm:
1392     if (ad->ad_flags != AD_VERSION1_SFM) {
1393         return 0;
1394     }
1395
1396     if ((adflags & ADFLAGS_DIR)) {
1397         /* no resource fork for directories / volumes XXX it's false! */
1398         return 0;
1399     }
1400
1401     /* untrue yet but ad_close will decremente it*/
1402     ad->ad_resource_fork.adf_refcount++;
1403
1404     if (ad_reso_fileno(ad) != -1) { /* the file is already open */
1405         if ((oflags & ( O_RDWR | O_WRONLY)) &&
1406             !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1407
1408             ad_close( ad, open_df | ADFLAGS_HF);
1409             errno = EACCES;
1410             return -1;
1411         }
1412         return 0;
1413     }
1414
1415     ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
1416
1417     hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1418     ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
1419     admode = mode;
1420     st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1421
1422     if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1423         admode = mode;
1424     }
1425
1426     if (ad->ad_resource_fork.adf_fd < 0 ) {
1427         if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1428             hoflags = oflags;
1429             ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
1430         }
1431     }
1432
1433     if ( ad->ad_resource_fork.adf_fd < 0) {
1434         int err = errno;
1435
1436         ad_close( ad, adflags );
1437         errno = err;
1438         return -1;
1439     }
1440
1441     AD_SET(ad->ad_resource_fork.adf_off);
1442     ad->ad_resource_fork.adf_flags = hoflags;
1443     if ((oflags & O_CREAT) && !st_invalid) {
1444         /* just created, set owner if admin (root) */
1445         ad_chown(ad_p, &st_dir);
1446     }
1447     else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
1448         ad->ad_rlen = st_meta.st_size;
1449     }
1450     return 0 ;
1451 }
1452
1453 /* -----------------------------------
1454  * return only metadata but try very hard
1455  */
1456 int ad_metadata(const char *name, int flags, struct adouble *adp)
1457 {
1458     uid_t uid;
1459     int   ret, err;
1460     int   dir = flags & ADFLAGS_DIR;
1461
1462     /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */
1463     /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */
1464     if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) {
1465         uid = geteuid();
1466         if (seteuid(0)) {
1467             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1468             errno = EACCES;
1469             return -1;
1470         }
1471         /* we are root open read only */
1472         ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
1473         err = errno;
1474         if ( seteuid(uid) < 0) {
1475             LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1476             exit(EXITERR_SYS);
1477         }
1478         errno = err;
1479     }
1480
1481     if (!ret && (ADFLAGS_OPENFORKS & flags)) {
1482         u_int16_t attrbits = adp->ad_open_forks;
1483
1484         /*
1485           we need to check if the file is open by another process.
1486           it's slow so we only do it if we have to:
1487           - it's requested.
1488           - we don't already have the answer!
1489         */
1490
1491         if (!(attrbits & ATTRBIT_ROPEN)) {
1492             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_ROPEN : 0;
1493             attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_ROPEN : 0;
1494         }
1495
1496         if (!(attrbits & ATTRBIT_DOPEN)) {
1497             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_DOPEN : 0;
1498             attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_DOPEN : 0;
1499         }
1500         adp->ad_open_forks = attrbits;
1501     }
1502     return ret;
1503 }
1504
1505 /* ----------------------------------- */
1506 static int new_rfork(const char *path, struct adouble *ad, int adflags)
1507 {
1508     const struct entry  *eid;
1509     u_int16_t           ashort;
1510     struct stat         st;
1511
1512     ad->ad_magic = AD_MAGIC;
1513     ad->ad_version = ad->ad_flags & 0x0f0000;
1514     if (!ad->ad_version) {
1515         ad->ad_version = AD_VERSION;
1516     }
1517
1518     memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
1519     memset(ad->ad_data, 0, sizeof(ad->ad_data));
1520
1521 #if AD_VERSION == AD_VERSION2
1522     if (ad->ad_flags == AD_VERSION2)
1523         eid = entry_order2;
1524     else if (ad->ad_flags == AD_VERSION2_OSX)
1525         eid = entry_order_osx;
1526     else  if (ad->ad_flags == AD_VERSION1_SFM) {
1527         ad->ad_magic = SFM_MAGIC;
1528         eid = entry_order_sfm;
1529     }
1530     else
1531 #endif
1532         eid = entry_order1;
1533
1534     while (eid->id) {
1535         ad->ad_eid[eid->id].ade_off = eid->offset;
1536         ad->ad_eid[eid->id].ade_len = eid->len;
1537         eid++;
1538     }
1539
1540     /* put something sane in the directory finderinfo */
1541     if ((adflags & ADFLAGS_DIR)) {
1542         /* set default view */
1543         ashort = htons(FINDERINFO_CLOSEDVIEW);
1544         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF,
1545                &ashort, sizeof(ashort));
1546     } else {
1547         /* set default creator/type fields */
1548         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
1549         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
1550     }
1551
1552     /* make things invisible */
1553     if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) &&
1554         (*path == '.') && strcmp(path, ".") && strcmp(path, ".."))
1555     {
1556         ashort = htons(ATTRBIT_INVISIBLE);
1557         ad_setattr(ad, ashort);
1558         ashort = htons(FINDERINFO_INVISIBLE);
1559         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1560     }
1561
1562     if (stat(path, &st) < 0) {
1563         return -1;
1564     }
1565
1566     /* put something sane in the date fields */
1567     ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
1568     ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
1569     ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
1570     ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
1571     return 0;
1572 }
1573
1574 /* to do this with mmap, we need the hfs fs to understand how to mmap
1575    header files. */
1576 int ad_refresh(struct adouble *ad)
1577 {
1578
1579     if (ad_meta_fileno(ad) < 0)
1580         return -1;
1581
1582     return ad->ad_ops->ad_header_read(ad, NULL);
1583 }