2 * $Id: ad_open.c,v 1.66 2010-01-06 11:08:53 franklahm Exp $
4 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
5 * Copyright (c) 1990,1991 Regents of The University of Michigan.
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.
18 * Research Systems Unix Group
19 * The University of Michigan
21 * 535 W. William Street
24 * netatalk@itd.umich.edu
30 * Part of Netatalk's AppleDouble implementatation
31 * @note We don't use inlines because a good compiler should be
32 * able to optimize all the static funcs below.
33 * @sa include/atalk/adouble.h
38 #endif /* HAVE_CONFIG_H */
42 #include <atalk/adouble.h>
43 #include <sys/param.h>
44 #include <atalk/logger.h>
46 #include <atalk/util.h>
49 #include "ad_private.h"
53 #define MAX(a, b) ((a) < (b) ? (b) : (a))
57 * AppleDouble entry default offsets.
58 * The layout looks like this:
60 * this is the v1 layout:
62 * | NAME | COMMENT | FILEI | FINDERI | RFORK |
64 * we need to change it to look like this:
67 * field length (in bytes)
70 * FILEDATESI 16 replaces FILEI
74 * SHORTNAME 12 8.3 new
77 * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
78 * and add in the new fields.
80 * NOTE: the HFS module will need similar modifications to interact with
84 #define ADEDOFF_MAGIC (0)
85 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
86 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
87 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
89 /* initial lengths of some of the fields */
90 #define ADEDLEN_INIT 0
92 /* make sure we don't redefine ADEDOFF_FILEI */
95 #endif /* ADEDOFF_FILEI */
97 #define ADEDOFF_NAME_V1 (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
98 #define ADEDOFF_COMMENT_V1 (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
99 #define ADEDOFF_FILEI (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
100 #define ADEDOFF_FINDERI_V1 (ADEDOFF_FILEI + ADEDLEN_FILEI)
101 #define ADEDOFF_RFORK_V1 (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
103 /* i stick things in a slightly different order than their eid order in
104 * case i ever want to separate RootInfo behaviour from the rest of the
106 #define ADEDOFF_NAME_V2 (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
107 #define ADEDOFF_COMMENT_V2 (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
108 #define ADEDOFF_FILEDATESI (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
109 #define ADEDOFF_FINDERI_V2 (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
110 #define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
111 #define ADEDOFF_AFPFILEI (ADEDOFF_DID + ADEDLEN_DID)
112 #define ADEDOFF_SHORTNAME (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
113 #define ADEDOFF_PRODOSFILEI (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
114 #define ADEDOFF_PRIVDEV (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
115 #define ADEDOFF_PRIVINO (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
116 #define ADEDOFF_PRIVSYN (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
117 #define ADEDOFF_PRIVID (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
119 #define ADEDOFF_RFORK_V2 (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
121 #define ADEID_NUM_OSX 2
122 #define ADEDOFF_FINDERI_OSX (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN)
123 #define ADEDOFF_RFORK_OSX (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI)
125 /* we keep local copies of a bunch of stuff so that we can initialize things
128 /* this is to prevent changing timezones from causing problems with
129 localtime volumes. the screw-up is 30 years. we use a delta of 5
131 #define TIMEWARP_DELTA 157680000
135 u_int32_t id, offset, len;
138 static const struct entry entry_order1[ADEID_NUM_V1 +1] = {
139 {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT}, /* 3 */
140 {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT}, /* 4 */
141 {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI}, /* 7 */
142 {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI}, /* 9 */
143 {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT}, /* 2 */
147 #if AD_VERSION == AD_VERSION1
148 #define DISK_EID(ad, a) (a)
150 #else /* AD_VERSION == AD_VERSION2 */
152 static u_int32_t get_eid(struct adouble *ad, u_int32_t eid)
156 if (ad->ad_version == AD_VERSION1)
159 return ADEID_PRIVDEV;
161 return ADEID_PRIVINO;
163 return ADEID_PRIVSYN;
170 #define DISK_EID(ad, a) get_eid(ad, a)
172 static const struct entry entry_order2[ADEID_NUM_V2 +1] = {
173 {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
174 {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
175 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
176 {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
177 {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
178 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
179 {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
180 {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
181 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT},
182 {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT},
183 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT},
184 {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT},
185 {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
190 /* OS X adouble finder info and resource fork only
192 static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = {
193 {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
194 {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
199 #define ADEID_NUM_SFM 3
200 static const struct entry entry_order_sfm[ADEID_NUM_SFM +1] = {
201 {ADEID_FINDERI, 16, ADEDLEN_FINDERI}, /* 9 */
202 {ADEID_SFMRESERVE2, 16+32, 6}, /* 21 */
203 {ADEID_FILEI, 60, ADEDLEN_FILEI}, /* 7 */
208 #endif /* AD_VERSION == AD_VERSION2 */
210 #if AD_VERSION == AD_VERSION2
212 /* update a version 2 adouble resource fork with our private entries */
213 static int ad_update(struct adouble *ad, const char *path)
216 u_int16_t nentries = 0;
217 off_t off, shiftdata=0;
218 const struct entry *eid;
219 static off_t entry_len[ADEID_MAX];
220 static char databuf[ADEID_MAX][256], *buf;
224 /* check to see if we should convert this header. */
225 if (!path || ad->ad_flags != AD_VERSION2)
228 LOG(log_maxdebug, logtype_default, "ad_update: checking whether '%s' needs an upgrade.", path);
230 if (!(ad->ad_md->adf_flags & O_RDWR)) {
231 /* we were unable to open the file read write the last time */
235 if (ad->ad_eid[ADEID_RFORK].ade_off) {
236 shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
239 memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries ));
240 nentries = ntohs( nentries );
242 if ( shiftdata == 0 && nentries == ADEID_NUM_V2)
245 memset(entry_len, 0, sizeof(entry_len));
246 memset(databuf, 0, sizeof(databuf));
248 /* bail if we can't get a lock */
249 if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
252 fd = ad->ad_md->adf_fd;
254 if (fstat(fd, &st)) {
258 if (st.st_size > 0x7fffffff) {
259 LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path);
264 off = ad->ad_eid[ADEID_RFORK].ade_off;
265 if (off > st.st_size) {
266 LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off);
271 if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
272 LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
277 if ((void *) (buf = (char *)
278 mmap(NULL, st.st_size + shiftdata,
279 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
284 /* last place for failure. */
285 if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
289 /* move the RFORK. this assumes that the RFORK is at the end */
291 memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
294 munmap(buf, st.st_size + shiftdata);
296 /* now, fix up our copy of the header */
297 memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
299 /* save the header entries */
302 if( ad->ad_eid[eid->id].ade_off != 0) {
303 if ( eid->id > 2 && ad->ad_eid[eid->id].ade_len < 256)
304 memcpy( databuf[eid->id], ad->ad_data +ad->ad_eid[eid->id].ade_off, ad->ad_eid[eid->id].ade_len);
305 entry_len[eid->id] = ad->ad_eid[eid->id].ade_len;
310 memset(ad->ad_data + AD_HEADER_LEN, 0, AD_DATASZ - AD_HEADER_LEN);
312 /* copy the saved entries to the new header */
315 if ( eid->id > 2 && entry_len[eid->id] > 0) {
316 memcpy(ad->ad_data+eid->offset, databuf[eid->id], entry_len[eid->id]);
318 ad->ad_eid[eid->id].ade_off = eid->offset;
319 ad->ad_eid[eid->id].ade_len = entry_len[eid->id];
323 /* rebuild the header and cleanup */
324 LOG(log_debug, logtype_default, "updated AD2 header %s", path);
329 ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
334 /* ------------------------------------------
335 FIXME work only if < 2GB
337 static int ad_convert(struct adouble *ad, const char *path)
344 /* use resource fork offset from file */
353 if (!(ad->ad_md->adf_flags & ( O_RDWR))) {
354 /* we were unable to open the file read write the last time */
358 /* check to see if we should convert this header. */
359 toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2;
360 toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1;
365 /* convert from v1 to v2. what does this mean?
366 * 1) change FILEI into FILEDATESI
367 * 2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
368 * 3) move FILEI attributes into AFPFILEI
369 * 4) initialize ACCESS field of FILEDATESI.
370 * 5) move the resource fork
373 /* bail if we can't get a lock */
374 if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
377 /* we reuse fd from the resource fork */
378 fd = ad->ad_md->adf_fd;
380 if (ad->ad_eid[ADEID_RFORK].ade_off) {
381 shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
384 shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */
387 if (fstat(fd, &st)) {
391 if (st.st_size > 0x7fffffff -shiftdata) {
392 LOG(log_debug, logtype_default, "ad_v1tov2: file too big.");
397 off = ad->ad_eid[ADEID_RFORK].ade_off;
399 if (off > st.st_size) {
400 LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off);
405 if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
406 LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
411 if ((void *) (buf = (char *)
412 mmap(NULL, st.st_size + shiftdata,
413 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
418 /* last place for failure. */
420 if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
424 /* move the RFORK. this assumes that the RFORK is at the end */
426 memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
429 munmap(buf, st.st_size + shiftdata);
431 /* now, fix up our copy of the header */
432 memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
434 /* replace FILEI with FILEDATESI */
435 ad_getattr(ad, &attr);
436 ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
437 ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
438 ad->ad_eid[ADEID_FILEI].ade_off = 0;
439 ad->ad_eid[ADEID_FILEI].ade_len = 0;
441 /* add in the new entries */
442 ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
443 ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
444 ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
445 ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
446 ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
447 ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
448 ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
449 ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
451 ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV;
452 ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT;
453 ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO;
454 ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT;
455 ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN;
456 ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT;
457 ad->ad_eid[ADEID_PRIVID].ade_off = ADEDOFF_PRIVID;
458 ad->ad_eid[ADEID_PRIVID].ade_len = ADEDLEN_INIT;
460 /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
461 ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
462 ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
463 ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
464 ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
466 /* switch to dest version */
467 ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1;
469 /* move our data buffer to make space for the new entries. */
470 memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1,
471 ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
473 /* now, fill in the space with appropriate stuff. we're
474 operating as a v2 file now. */
475 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
476 memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
477 memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
478 ad_setattr(ad, attr);
479 memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
480 memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
482 /* rebuild the header and cleanup */
487 ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
491 #endif /* AD_VERSION == AD_VERSION2 */
493 /* -------------------------------------
496 static void parse_entries(struct adouble *ad, char *buf,
499 u_int32_t eid, len, off;
502 /* now, read in the entry bits */
503 for (; nentries > 0; nentries-- ) {
504 memcpy(&eid, buf, sizeof( eid ));
505 eid = DISK_EID(ad, ntohl( eid ));
506 buf += sizeof( eid );
507 memcpy(&off, buf, sizeof( off ));
509 buf += sizeof( off );
510 memcpy(&len, buf, sizeof( len ));
512 buf += sizeof( len );
514 if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) &&
515 (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
516 ad->ad_eid[ eid ].ade_off = off;
517 ad->ad_eid[ eid ].ade_len = len;
518 } else if (!warning) {
520 LOG(log_debug, logtype_default, "ad_refresh: nentries %hd eid %d",
527 /* this reads enough of the header so that we can figure out all of
528 * the entry lengths and offsets. once that's done, we just read/mmap
529 * the rest of the header in.
531 * NOTE: we're assuming that the resource fork is kept at the end of
532 * the file. also, mmapping won't work for the hfs fs until it
533 * understands how to mmap header files. */
534 static int ad_header_read(struct adouble *ad, struct stat *hst)
536 char *buf = ad->ad_data;
540 static int warning = 0;
543 /* read the header */
544 if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
547 if (header_len < AD_HEADER_LEN) {
552 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
553 memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
555 /* tag broken v1 headers. just assume they're all right.
556 * we detect two cases: null magic/version
557 * byte swapped magic/version
558 * XXX: in the future, you'll need the v1compat flag.
559 * (ad->ad_flags & ADFLAGS_V1COMPAT) */
560 if (!ad->ad_magic && !ad->ad_version) {
562 LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
565 ad->ad_magic = AD_MAGIC;
566 ad->ad_version = AD_VERSION1;
568 } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
570 LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
575 ad->ad_magic = ntohl( ad->ad_magic );
576 ad->ad_version = ntohl( ad->ad_version );
579 if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
580 #if AD_VERSION == AD_VERSION2
581 && (ad->ad_version != AD_VERSION2)
582 #endif /* AD_VERSION == AD_VERSION2 */
584 LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
589 memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
590 memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
591 nentries = ntohs( nentries );
593 /* read in all the entry headers. if we have more than the
594 * maximum, just hope that the rfork is specified early on. */
595 len = nentries*AD_ENTRY_LEN;
597 if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
598 len = sizeof(ad->ad_data) - AD_HEADER_LEN;
600 buf += AD_HEADER_LEN;
601 if (len > header_len - AD_HEADER_LEN) {
602 LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
607 /* figure out all of the entry offsets and lengths. if we aren't
608 * able to read a resource fork entry, bail. */
609 nentries = len / AD_ENTRY_LEN;
610 parse_entries(ad, buf, nentries);
611 if (!ad_getentryoff(ad, ADEID_RFORK)
612 || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
614 LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
619 if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
620 LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
627 if (fstat(ad->ad_md->adf_fd, &st) < 0) {
628 return 1; /* fail silently */
631 ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
633 /* fix up broken dates */
634 if (ad->ad_version == AD_VERSION1) {
637 /* check to see if the ad date is wrong. just see if we have
638 * a modification date in the future. */
639 if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
640 (aint > TIMEWARP_DELTA + hst->st_mtime)) {
641 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
642 ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
643 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
644 ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
645 ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
652 /* ---------------------------
657 byte afpi_Signature[4]; /* Must be 0x00504641 */
658 byte afpi_Version[4]; /* Must be 0x00010000 */
659 byte afpi_Reserved1[4];
660 byte afpi_BackupTime[4]; /* Backup time for the file/dir */
661 byte finderinfo[32]; /* Finder info */
662 byte afpi_ProDosInfo[6]; /* ProDos Info */
663 byte afpi_Reserved2[6];
667 static int ad_header_sfm_read(struct adouble *ad, struct stat *hst)
669 char *buf = ad->ad_data;
670 const struct entry *eid;
674 /* read the header */
675 if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
678 if (header_len != AD_SFM_LEN) {
683 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
684 memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version ));
686 /* FIXME in the great Microsoft tradition they aren't in network order */
688 if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) {
689 static int warning = 0;
691 LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
696 ad->ad_magic = ntohl( ad->ad_magic );
697 ad->ad_version = ntohl( ad->ad_version );
700 if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) {
702 LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header.");
706 /* reinit adouble table */
707 eid = entry_order_sfm;
709 ad->ad_eid[eid->id].ade_off = eid->offset;
710 ad->ad_eid[eid->id].ade_len = eid->len;
714 /* steal some prodos for attribute */
718 memcpy(&attribute, buf + 48 +4, sizeof(attribute));
719 ad_setattr(ad, attribute );
722 if (ad->ad_resource_fork.adf_fd != -1) {
723 /* we have a resource fork use it rather than the metadata */
724 if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) {
730 ad->ad_rlen = st.st_size;
733 else if (hst == NULL) {
735 if (fstat(ad->ad_md->adf_fd, &st) < 0) {
736 return 1; /* fail silently */
740 /* fix up broken dates */
741 if (ad->ad_version == AD_VERSION1) {
744 /* check to see if the ad date is wrong. just see if we have
745 * a modification date in the future. */
746 if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
747 (aint > TIMEWARP_DELTA + hst->st_mtime)) {
748 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
749 ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
750 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
751 ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
752 ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
759 /* ---------------------------------------
760 * Put the .AppleDouble where it needs to be:
764 * \ b/.AppleDouble/.Parent
766 * FIXME: should do something for pathname > MAXPATHLEN
769 ad_path( const char *path, int adflags)
771 static char pathbuf[ MAXPATHLEN + 1];
775 if ( adflags & ADFLAGS_DIR ) {
776 l = strlcpy( pathbuf, path, sizeof(pathbuf));
778 if ( l && l < MAXPATHLEN) {
781 strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
783 if (NULL != ( slash = strrchr( path, '/' )) ) {
786 /* XXX we must return NULL here and test in the caller */
789 memcpy( pathbuf, path, l);
794 l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
795 strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
801 /* -------------------- */
802 static int ad_mkrf(char *path)
806 * Probably .AppleDouble doesn't exist, try to mkdir it.
808 if (NULL == ( slash = strrchr( path, '/' )) ) {
813 if ( ad_mkdir( path, 0777 ) < 0 ) {
820 /* ---------------------------------------
821 * Put the resource fork where it needs to be:
825 ad_path_osx(const char *path, int adflags _U_)
827 static char pathbuf[ MAXPATHLEN + 1];
828 char c, *slash, buf[MAXPATHLEN + 1];
830 if (!strcmp(path,".")) {
832 getcwd(buf, MAXPATHLEN);
835 strlcpy(buf, path, MAXPATHLEN +1);
837 if (NULL != ( slash = strrchr( buf, '/' )) ) {
840 strlcpy( pathbuf, buf, MAXPATHLEN +1);
846 strlcat( pathbuf, "._", MAXPATHLEN +1);
847 strlcat( pathbuf, slash, MAXPATHLEN +1);
850 /* -------------------- */
851 static int ad_mkrf_osx(char *path _U_)
856 /* ---------------------------------------
857 * Put the .AppleDouble where it needs to be:
859 * / a/.AppleDouble/b/AFP_AfpInfo
861 * \ b/.AppleDouble/.Parent/AFP_AfpInfo
865 ad_path_sfm( const char *path, int adflags)
867 static char pathbuf[ MAXPATHLEN + 1];
868 char c, *slash, buf[MAXPATHLEN + 1];
871 l = strlcpy(buf, path, MAXPATHLEN +1);
872 if ( adflags & ADFLAGS_DIR ) {
873 strcpy( pathbuf, buf);
874 if ( *buf != '\0' && l < MAXPATHLEN) {
880 if (NULL != ( slash = strrchr( buf, '/' )) ) {
883 strcpy( pathbuf, buf);
890 strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
891 strlcat( pathbuf, slash, MAXPATHLEN +1);
893 if ((adflags == ADFLAGS_RF)) {
894 strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
897 strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
902 /* -------------------- */
903 static int ad_mkrf_sfm(char *path)
907 * Probably .AppleDouble doesn't exist, try to mkdir it.
909 if (NULL == ( slash = strrchr( path, '/' )) ) {
914 if ( ad_mkdir( path, 0777 ) < 0 ) {
915 if ( errno == ENOENT ) {
918 if (NULL == ( slash1 = strrchr( path, '/' )) )
922 if ( ad_mkdir( path, 0777 ) < 0 )
925 if ( ad_mkdir( path, 0777 ) < 0 )
935 /* -------------------------
936 * Support inherited protection modes for AppleDouble files. The supplied
937 * mode is ANDed with the parent directory's mask value in lieu of "umask",
938 * and that value is returned.
941 #define DEFMASK 07700 /* be conservative */
944 *ad_dir(const char *path)
946 static char modebuf[ MAXPATHLEN + 1];
949 * For a path with directories in it, remove the final component
950 * (path or subdirectory name) to get the name we want to stat.
951 * For a path which is just a filename, use "." instead.
953 slash = strrchr( path, '/' );
958 if (len >= MAXPATHLEN) {
959 errno = ENAMETOOLONG;
960 return NULL; /* can't do it */
962 memcpy( modebuf, path, len );
964 /* is last char a '/' ? */
966 slash = modebuf+ len;
968 while (modebuf < slash && slash[-1] == '/') {
971 if (modebuf == slash) {
975 while (modebuf < slash && *slash != '/') {
978 if (modebuf == slash) {
981 *slash = '\0'; /* remove pathname component */
986 modebuf[0] = '.'; /* use current directory */
991 /* ---------------- */
992 static uid_t default_uid = -1;
994 int ad_setfuid(const uid_t id)
1000 /* ---------------- */
1001 uid_t ad_getfuid(void)
1007 return inode of path parent directory
1009 int ad_stat(const char *path, struct stat *stbuf)
1018 return stat( p, stbuf );
1022 if we are root change path user/ group
1023 It can be a native function for BSD cf. FAQ.Q10
1024 path: pathname to chown
1025 stbuf: parent directory inode
1027 use fstat and fchown or lchown with linux?
1029 #define EMULATE_SUIDDIR
1031 static int ad_chown(const char *path, struct stat *stbuf)
1034 #ifdef EMULATE_SUIDDIR
1037 if (default_uid != (uid_t)-1) {
1038 /* we are root (admin) */
1039 id = (default_uid)?default_uid:stbuf->st_uid;
1040 ret = chown( path, id, stbuf->st_gid );
1047 return access right and inode of path parent directory
1049 static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
1054 if (ad_stat(path, stbuf) != 0) {
1058 *mode &= stbuf->st_mode;
1063 return access right of path parent directory
1066 ad_mode( const char *path, int mode)
1069 ad_mode_st(path, &mode, &stbuf);
1074 * Use mkdir() with mode bits taken from ad_mode().
1077 ad_mkdir( const char *path, int mode)
1083 LOG(log_debug, logtype_default, "ad_mkdir: creating ad-directory '%s/%s' with mode %04o",
1084 getcwdpath(), path, mode);
1086 st_invalid = ad_mode_st(path, &mode, &stbuf);
1087 ret = mkdir( path, mode );
1088 if (ret || st_invalid)
1090 ad_chown(path, &stbuf);
1095 /* ----------------- */
1096 static int ad_error(struct adouble *ad, int adflags)
1099 if ((adflags & ADFLAGS_NOHF)) {
1100 /* FIXME double check : set header offset ?*/
1103 if ((adflags & ADFLAGS_DF)) {
1104 ad_close( ad, ADFLAGS_DF );
1110 static int new_rfork(const char *path, struct adouble *ad, int adflags);
1115 #define AD_SET(a) a = 0
1118 /* --------------------------- */
1119 static int ad_check_size(struct adouble *ad _U_, struct stat *st)
1121 if (st->st_size > 0 && st->st_size < AD_DATASZ1)
1126 /* --------------------------- */
1127 static int ad_check_size_sfm(struct adouble *ad _U_, struct stat *st)
1129 if (st->st_size > 0 && st->st_size < AD_SFM_LEN)
1134 /* --------------------------- */
1135 static int ad_header_upgrade(struct adouble *ad, char *name)
1137 #if AD_VERSION == AD_VERSION2
1139 if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
1146 /* --------------------------- */
1147 static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_)
1152 /* --------------------------- */
1153 static struct adouble_fops ad_osx = {
1156 &ad_rebuild_adouble_header,
1163 static struct adouble_fops ad_sfm = {
1166 &ad_rebuild_sfm_header,
1169 &ad_header_sfm_read,
1170 &ad_header_upgrade_none,
1173 static struct adouble_fops ad_adouble = {
1176 &ad_rebuild_adouble_header,
1184 void ad_init(struct adouble *ad, int flags, int options)
1187 ad->ad_flags = flags;
1188 if (flags == AD_VERSION2_OSX) {
1189 ad->ad_ops = &ad_osx;
1190 ad->ad_md = &ad->ad_resource_fork;
1192 else if (flags == AD_VERSION1_SFM) {
1193 ad->ad_ops = &ad_sfm;
1194 ad->ad_md = &ad->ad_metadata_fork;
1197 ad->ad_ops = &ad_adouble;
1198 ad->ad_md = &ad->ad_resource_fork;
1200 ad->ad_options = options;
1202 ad_data_fileno(ad) = -1;
1203 ad_reso_fileno(ad) = -1;
1204 ad_meta_fileno(ad) = -1;
1205 /* following can be read even if there's no
1208 memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1213 * Open data-, metadata(header)- or ressource fork
1215 * You must call ad_init() before ad_open, usually you'll just call it like this: \n
1218 * ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1221 * @param path Path to file or directory
1223 * @param adflags ADFLAGS_DF: open data file/fork\n
1224 * ADFLAGS_HF: open header (metadata) file\n
1225 * ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n
1226 * ADFLAGS_CREATE: indicate creation\n
1227 * ADFLAGS_NOHF: it's not an error if header file couldn't be created\n
1228 * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n
1229 * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n
1230 * ADFLAGS_RDONLY: open read only\n
1231 * ADFLAGS_OPENFORKS: check for open forks from other processes\n
1232 * ADFLAGS_MD: alias for ADFLAGS_HF\n
1233 * ADFLAGS_V1COMPAT: obsolete
1235 * @param oflags flags passed through to open syscall: \n
1236 * O_RDONLY: *** FIXME *** \n
1237 * O_RDWR: *** FIXME *** \n
1238 * O_CREAT: create fork\n
1239 * O_EXCL: fail if exists with O_CREAT
1241 * @param mode passed to open with O_CREAT
1243 * @param ad pointer to struct adouble
1245 * @returns 0 on success
1247 * @note It's not possible to open the header file O_RDONLY -- the read
1248 * will fail and return an error. this refcounts things now.\n
1249 * metadata(ressource)-fork only gets created with O_CREAT.
1251 int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble *ad)
1254 struct stat st_meta;
1255 struct stat *pst = NULL;
1257 int hoflags, admode;
1258 int st_invalid = -1;
1261 if (ad->ad_inited != AD_INITED) {
1262 ad->ad_inited = AD_INITED;
1263 ad->ad_refcount = 1;
1264 ad->ad_open_forks = 0;
1265 ad->ad_adflags = adflags;
1266 ad->ad_resource_fork.adf_refcount = 0;
1267 ad->ad_data_fork.adf_refcount = 0;
1270 ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
1271 /* XXX not true if we have a meta data fork ? */
1272 if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
1273 ad->ad_open_forks |= ATTRBIT_ROPEN;
1276 if ((adflags & ADFLAGS_DF)) {
1277 if (ad_data_fileno(ad) == -1) {
1278 hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1280 if ((oflags & O_CREAT)) {
1281 st_invalid = ad_mode_st(path, &admode, &st_dir);
1282 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1286 ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
1287 if (ad->ad_data_fork.adf_fd < 0 ) {
1288 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1290 ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
1293 if ( ad->ad_data_fork.adf_fd < 0)
1296 AD_SET(ad->ad_data_fork.adf_off);
1297 ad->ad_data_fork.adf_flags = hoflags;
1299 /* just created, set owner if admin (root) */
1300 ad_chown(path, &st_dir);
1302 adf_lock_init(&ad->ad_data_fork);
1305 /* the file is already open... but */
1306 if ((oflags & ( O_RDWR | O_WRONLY)) && /* we want write access */
1307 !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
1313 * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
1314 * already open. Should we check for it? ie
1316 * O_TRUNC --> truncate the fork.
1317 * idem for ressource fork.
1320 open_df = ADFLAGS_DF;
1321 ad->ad_data_fork.adf_refcount++;
1324 if (!(adflags & ADFLAGS_HF))
1327 /* ****************************************** */
1329 if (ad_meta_fileno(ad) != -1) { /* the file is already open */
1330 if ((oflags & ( O_RDWR | O_WRONLY)) &&
1331 !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
1333 /* don't call with ADFLAGS_HF because we didn't open ressource fork */
1334 ad_close( ad, open_df );
1340 ad->ad_md->adf_refcount++;
1344 memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
1346 ad_p = ad->ad_ops->ad_path( path, adflags );
1348 hoflags = oflags & ~(O_CREAT | O_EXCL);
1349 if (!(adflags & ADFLAGS_RDONLY)) {
1350 hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1352 ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1353 if (ad->ad_md->adf_fd < 0 ) {
1354 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1355 hoflags = oflags & ~(O_CREAT | O_EXCL);
1356 ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
1360 if ( ad->ad_md->adf_fd < 0 ) {
1361 if (errno == ENOENT && (oflags & O_CREAT) ) {
1363 * We're expecting to create a new adouble header file,
1365 * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
1367 LOG(log_debug, logtype_default, "ad_open: creating new adouble file: %s/%s", getcwdpath(), ad_p);
1370 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1371 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1374 admode = ad_hf_mode(admode);
1375 if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) {
1376 if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
1377 return ad_error(ad, adflags);
1380 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1381 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1384 admode = ad_hf_mode(admode);
1386 /* retry with O_CREAT */
1387 ad->ad_md->adf_fd = open( ad_p, oflags,admode );
1388 if ( ad->ad_md->adf_fd < 0 ) {
1389 return ad_error(ad, adflags);
1391 ad->ad_md->adf_flags = oflags;
1392 /* just created, set owner if admin owner (root) */
1394 ad_chown(ad_p, &st_dir);
1398 return ad_error(ad, adflags);
1401 ad->ad_md->adf_flags = hoflags;
1402 if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
1403 /* for 0 length files, treat them as new. */
1404 ad->ad_md->adf_flags |= O_TRUNC;
1407 /* we have valid data in st_meta stat structure, reused it
1413 AD_SET(ad->ad_md->adf_off);
1415 ad->ad_md->adf_refcount = 1;
1416 adf_lock_init(ad->ad_md);
1417 if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
1419 * This is a new adouble header file. Initialize the structure,
1420 * instead of reading it.
1422 if (new_rfork(path, ad, adflags) < 0) {
1424 /* the file is already deleted, perm, whatever, so return an error*/
1425 ad_close(ad, adflags);
1431 /* Read the adouble header in and parse it.*/
1432 if (ad->ad_ops->ad_header_read( ad , pst) < 0
1433 || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0)
1437 ad_close( ad, adflags );
1443 /* ****************************************** */
1444 /* open the resource fork if SFM */
1446 if (ad->ad_flags != AD_VERSION1_SFM) {
1450 if ((adflags & ADFLAGS_DIR)) {
1451 /* no resource fork for directories / volumes XXX it's false! */
1455 /* untrue yet but ad_close will decremente it*/
1456 ad->ad_resource_fork.adf_refcount++;
1458 if (ad_reso_fileno(ad) != -1) { /* the file is already open */
1459 if ((oflags & ( O_RDWR | O_WRONLY)) &&
1460 !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1462 ad_close( ad, open_df | ADFLAGS_HF);
1469 ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
1472 st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
1474 if ((ad->ad_options & ADVOL_UNIXPRIV)) {
1478 hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
1479 ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
1481 if (ad->ad_resource_fork.adf_fd < 0 ) {
1482 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
1484 ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
1488 if ( ad->ad_resource_fork.adf_fd < 0) {
1491 ad_close( ad, adflags );
1495 adf_lock_init(&ad->ad_resource_fork);
1496 AD_SET(ad->ad_resource_fork.adf_off);
1497 ad->ad_resource_fork.adf_flags = hoflags;
1498 if ((oflags & O_CREAT) && !st_invalid) {
1499 /* just created, set owner if admin (root) */
1500 ad_chown(ad_p, &st_dir);
1502 else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
1503 ad->ad_rlen = st_meta.st_size;
1509 * @brief open metadata, possibly as root
1511 * Return only metadata but try very hard ie at first try as user, then try as root.
1513 * @param name name of file/dir
1514 * @param flags ADFLAGS_DIR: name is a directory \n
1515 * ADFLAGS_CREATE: force creation of header file, but only as use, not as root
1516 * @param adp pointer to struct adouble
1518 * @note caller MUST pass ADFLAGS_DIR for directories
1520 int ad_metadata(const char *name, int flags, struct adouble *adp)
1523 int ret, err, dir, create;
1525 dir = flags & ADFLAGS_DIR;
1526 create = (flags & ADFLAGS_CREATE) ? O_CREAT : 0;
1528 if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | create, 0666, adp)) < 0 && errno == EACCES) {
1531 LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
1535 /* we are root open read only */
1536 ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
1538 if ( seteuid(uid) < 0) {
1539 LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
1545 if (!ret && (ADFLAGS_OPENFORKS & flags)) {
1547 we need to check if the file is open by another process.
1548 it's slow so we only do it if we have to:
1550 - we don't already have the answer!
1552 adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks);
1557 /* ----------------------------------- */
1558 static int new_rfork(const char *path, struct adouble *ad, int adflags)
1560 const struct entry *eid;
1564 ad->ad_magic = AD_MAGIC;
1565 ad->ad_version = ad->ad_flags & 0x0f0000;
1566 if (!ad->ad_version) {
1567 ad->ad_version = AD_VERSION;
1570 memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
1571 memset(ad->ad_data, 0, sizeof(ad->ad_data));
1573 #if AD_VERSION == AD_VERSION2
1574 if (ad->ad_flags == AD_VERSION2)
1576 else if (ad->ad_flags == AD_VERSION2_OSX)
1577 eid = entry_order_osx;
1578 else if (ad->ad_flags == AD_VERSION1_SFM) {
1579 ad->ad_magic = SFM_MAGIC;
1580 eid = entry_order_sfm;
1587 ad->ad_eid[eid->id].ade_off = eid->offset;
1588 ad->ad_eid[eid->id].ade_len = eid->len;
1592 /* put something sane in the directory finderinfo */
1593 if ((adflags & ADFLAGS_DIR)) {
1594 /* set default view */
1595 ashort = htons(FINDERINFO_CLOSEDVIEW);
1596 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF,
1597 &ashort, sizeof(ashort));
1599 /* set default creator/type fields */
1600 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
1601 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
1604 /* make things invisible */
1605 if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) &&
1606 (*path == '.') && strcmp(path, ".") && strcmp(path, ".."))
1608 ashort = htons(ATTRBIT_INVISIBLE);
1609 ad_setattr(ad, ashort);
1610 ashort = htons(FINDERINFO_INVISIBLE);
1611 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1614 if (stat(path, &st) < 0) {
1618 /* put something sane in the date fields */
1619 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
1620 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
1621 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
1622 ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
1626 /* to do this with mmap, we need the hfs fs to understand how to mmap
1628 int ad_refresh(struct adouble *ad)
1631 if (ad_meta_fileno(ad) < 0)
1634 return ad->ad_ops->ad_header_read(ad, NULL);