--- /usr/src/lib/libarchive/archive.h.in Sun Jun 5 00:24:04 2005 +++ archive.h.in Fri Jul 14 10:57:58 2006 @@ -243,6 +243,8 @@ #define ARCHIVE_EXTRACT_UNLINK (16) /* Default: don't unlink existing files */ #define ARCHIVE_EXTRACT_ACL (32) /* Default: don't restore ACLs */ #define ARCHIVE_EXTRACT_FFLAGS (64) /* Default: don't restore fflags */ +#define ARCHIVE_EXTRACT_EXACT_STAT (128) /* set fflags exactly as they in archive */ + /* (don't merge), extract perm, acl and time for exist dirs */ int archive_read_extract(struct archive *, struct archive_entry *, int flags); --- /usr/src/lib/libarchive/archive_read_extract.c Wed Sep 21 05:12:45 2005 +++ archive_read_extract.c Fri Jul 14 11:58:00 2006 @@ -73,6 +73,7 @@ #define FIXUP_MODE 1 #define FIXUP_TIMES 2 #define FIXUP_FFLAGS 4 +#define FIXUP_EXACT_STAT 8 struct bucket { char *name; @@ -135,7 +136,7 @@ #endif static int set_acls(struct archive *, int fd, struct archive_entry *); static int set_fflags(struct archive *, int fd, const char *name, mode_t, - unsigned long fflags_set, unsigned long fflags_clear); + unsigned long fflags_set, unsigned long fflags_clear, int exact_stat); static int set_ownership(struct archive *, int fd, struct archive_entry *, int flags); static int set_perm(struct archive *, int fd, struct archive_entry *, @@ -338,7 +339,8 @@ chmod(p->name, p->mode); if (p->fixup & FIXUP_FFLAGS) - set_fflags(a, -1, p->name, p->mode, p->fflags_set, 0); + set_fflags(a, -1, p->name, p->mode, p->fflags_set, 0, + (p->fixup & FIXUP_EXACT_STAT)); next = p->next; free(p->name); @@ -558,8 +560,12 @@ if (extract->pst != NULL) { extract->pst = &extract->st; /* If dir already exists, don't reset permissions. */ - if (S_ISDIR(extract->pst->st_mode)) - return (ARCHIVE_OK); + if (S_ISDIR(extract->pst->st_mode)) { + if (flags & ARCHIVE_EXTRACT_EXACT_STAT) + goto success; + else + return (ARCHIVE_OK); + } /* It exists but isn't a dir. */ if ((flags & ARCHIVE_EXTRACT_UNLINK)) unlink(path); @@ -1132,13 +1138,14 @@ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, archive_entry_pathname(entry)); le->fixup |= FIXUP_FFLAGS; + le->fixup |= (flags & ARCHIVE_EXTRACT_EXACT_STAT); le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & FIXUP_MODE) == 0) le->mode = mode; } else { r = set_fflags(a, fd, archive_entry_pathname(entry), - mode, set, clear); + mode, set, clear, (flags & ARCHIVE_EXTRACT_EXACT_STAT)); if (r != ARCHIVE_OK) return (r); } @@ -1150,35 +1157,42 @@ #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux) static int set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) + unsigned long set, unsigned long clear, int exact_stat) { + (void)mode; /* UNUSED */ + struct extract *extract; extract = a->extract; - if (set == 0 && clear == 0) - return (ARCHIVE_OK); - (void)mode; /* UNUSED */ - /* - * XXX Is the stat here really necessary? Or can I just use - * the 'set' flags directly? In particular, I'm not sure - * about the correct approach if we're overwriting an existing - * file that already has flags on it. XXX - */ - if (extract->pst != NULL) { - /* Already have stat() data available. */ - } else if (fd >= 0 && fstat(fd, &extract->st) == 0) - extract->pst = &extract->st; - else if (stat(name, &extract->st) == 0) - extract->pst = &extract->st; + if (exact_stat) + extract->st.st_flags = set; else { - archive_set_error(a, errno, - "Couldn't stat file"); - return (ARCHIVE_WARN); + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + + /* + * XXX Is the stat here really necessary? Or can I just use + * the 'set' flags directly? In particular, I'm not sure + * about the correct approach if we're overwriting an existing + * file that already has flags on it. XXX + */ + if (extract->pst != NULL) { + /* Already have stat() data available. */ + } else if (fd >= 0 && fstat(fd, &extract->st) == 0) + extract->pst = &extract->st; + else if (stat(name, &extract->st) == 0) + extract->pst = &extract->st; + else { + archive_set_error(a, errno, + "Couldn't stat file"); + return (ARCHIVE_WARN); + } + + extract->st.st_flags &= ~clear; + extract->st.st_flags |= set; } - extract->st.st_flags &= ~clear; - extract->st.st_flags |= set; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, extract->st.st_flags) == 0) @@ -1209,7 +1223,7 @@ */ static int set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) + unsigned long set, unsigned long clear, int exact_stat) { struct extract *extract; int ret; @@ -1218,7 +1232,7 @@ unsigned long newflags, oldflags; extract = a->extract; - if (set == 0 && clear == 0) + if (exact_stat == 0 && set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) @@ -1275,7 +1289,7 @@ */ static int set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) + unsigned long set, unsigned long clear, int exact_stat) { (void)a; (void)fd; --- /usr/src/usr.bin/tar/bsdtar.c Sun Nov 6 23:27:21 2005 +++ bsdtar.c Fri Jul 14 11:24:50 2006 @@ -110,6 +110,7 @@ /* Fake short equivalents for long options that otherwise lack them. */ enum { OPTION_CHECK_LINKS=1, + OPTION_EXACT_STAT, OPTION_EXCLUDE, OPTION_FAST_READ, OPTION_FORMAT, @@ -145,6 +146,7 @@ { "create", no_argument, NULL, 'c' }, { "dereference", no_argument, NULL, 'L' }, { "directory", required_argument, NULL, 'C' }, + { "exact-stat", no_argument, NULL, OPTION_EXACT_STAT }, { "exclude", required_argument, NULL, OPTION_EXCLUDE }, { "exclude-from", required_argument, NULL, 'X' }, { "extract", no_argument, NULL, 'x' }, @@ -266,6 +268,9 @@ break; case OPTION_CHECK_LINKS: /* GNU tar */ bsdtar->option_warn_links = 1; + break; + case OPTION_EXACT_STAT: + bsdtar->extract_flags |= ARCHIVE_EXTRACT_EXACT_STAT; break; case OPTION_EXCLUDE: /* GNU tar */ if (exclude(bsdtar, optarg))