Performance: If the dir mode requested is "reasonable", then just

restore it directly and skip chmod() during the post-extract fixup.
In particular, bsdtar -xm now completely skips the post-extract fixup
for directories, which produces a noticable speedup in that case.
This commit is contained in:
Tim Kientzle 2006-10-04 02:08:04 +00:00
parent f68412f927
commit 011a0a0432
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=162996

View File

@ -85,7 +85,8 @@ struct bucket {
struct extract {
mode_t umask;
mode_t default_dir_mode;
mode_t default_dir_mode_initial;
mode_t default_dir_mode_final;
struct archive_string create_parent_dir;
struct fixup_entry *fixup_list;
struct fixup_entry *current_fixup;
@ -108,9 +109,11 @@ struct extract {
* Mode to use for newly-created dirs during extraction; the correct
* mode will be set at the end of the extraction.
*/
#define SECURE_DIR_MODE 0700
#define MINIMUM_DIR_MODE 0700
#define MAXIMUM_DIR_MODE 0775
static int archive_extract_cleanup(struct archive *);
static int create_extract(struct archive *a);
static int extract_block_device(struct archive *,
struct archive_entry *, int);
static int extract_char_device(struct archive *,
@ -169,17 +172,11 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
char *original_filename;
if (a->extract == NULL) {
a->extract = malloc(sizeof(*a->extract));
if (a->extract == NULL) {
archive_set_error(a, ENOMEM, "Can't extract");
return (ARCHIVE_FATAL);
}
a->cleanup_archive_extract = archive_extract_cleanup;
memset(a->extract, 0, sizeof(*a->extract));
ret = create_extract(a);
if (ret)
return (ret);
}
extract = a->extract;
umask(extract->umask = umask(0)); /* Read the current umask. */
extract->default_dir_mode = DEFAULT_DIR_MODE & ~extract->umask;
extract->pst = NULL;
extract->current_fixup = NULL;
restore_pwd = -1;
@ -293,6 +290,35 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
return (ret);
}
static int
create_extract(struct archive *a)
{
struct extract *extract;
extract = malloc(sizeof(*extract));
if (extract == NULL) {
archive_set_error(a, ENOMEM, "Can't extract");
return (ARCHIVE_FATAL);
}
a->cleanup_archive_extract = archive_extract_cleanup;
memset(extract, 0, sizeof(*extract));
umask(extract->umask = umask(0)); /* Read the current umask. */
/* Final permission for default dirs. */
extract->default_dir_mode_final
= DEFAULT_DIR_MODE & ~extract->umask;
/* Temporary permission for default dirs during extract. */
extract->default_dir_mode_initial
= extract->default_dir_mode_final;
extract->default_dir_mode_initial |= MINIMUM_DIR_MODE;
extract->default_dir_mode_initial &= MAXIMUM_DIR_MODE;
/* If the two permissions above are different, then
* the "final" permissions will be applied in the
* post-extract fixup pass. */
a->extract = extract;
return (ARCHIVE_OK);
}
/*
* Cleanup function for archive_extract. Mostly, this involves processing
* the fixup list, which is used to address a number of problems:
@ -508,6 +534,7 @@ extract_dir(struct archive *a, struct archive_entry *entry, int flags)
struct extract *extract;
struct fixup_entry *fe;
char *path, *p;
mode_t restore_mode, final_mode;
extract = a->extract;
extract->pst = NULL; /* Invalidate cached stat data. */
@ -554,7 +581,16 @@ extract_dir(struct archive *a, struct archive_entry *entry, int flags)
break;
}
if (mkdir(path, SECURE_DIR_MODE) == 0)
final_mode = archive_entry_mode(entry) &
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
final_mode &= ~extract->umask;
/* Constrain the permissions in effect during the restore. */
restore_mode = final_mode;
restore_mode |= MINIMUM_DIR_MODE;
restore_mode &= MAXIMUM_DIR_MODE;
if (mkdir(path, restore_mode) == 0)
goto success;
if (extract->pst == NULL && stat(path, &extract->st) == 0)
@ -575,27 +611,26 @@ extract_dir(struct archive *a, struct archive_entry *entry, int flags)
}
/* One final attempt to create the dir. */
if (mkdir(path, SECURE_DIR_MODE) != 0) {
if (mkdir(path, restore_mode) != 0) {
archive_set_error(a, errno, "Can't create directory");
return (ARCHIVE_WARN);
}
success:
/* Add this dir to the fixup list. */
fe = current_fixup(a, path);
fe->fixup |= FIXUP_MODE;
fe->mode = archive_entry_mode(entry);
if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
fe->mode &= ~extract->umask;
if (final_mode != restore_mode) {
fe = current_fixup(a, path);
fe->fixup |= FIXUP_MODE;
fe->mode = final_mode;
}
if (flags & ARCHIVE_EXTRACT_TIME) {
fe = current_fixup(a, path);
fe->fixup |= FIXUP_TIMES;
fe->mtime = archive_entry_mtime(entry);
fe->mtime_nanos = archive_entry_mtime_nsec(entry);
fe->atime = archive_entry_atime(entry);
fe->atime_nanos = archive_entry_atime_nsec(entry);
}
/* For now, set the mode to SECURE_DIR_MODE. */
archive_entry_set_mode(entry, SECURE_DIR_MODE);
return (restore_metadata(a, -1, entry, flags));
}
@ -656,12 +691,9 @@ create_parent_dir_mutable(struct archive *a, char *path, int flags)
static int
create_dir_mutable(struct archive *a, char *path, int flags)
{
mode_t old_umask;
int r;
old_umask = umask(~SECURE_DIR_MODE);
r = create_dir_recursive(a, path, flags);
umask(old_umask);
return (r);
}
@ -735,10 +767,13 @@ create_dir_recursive(struct archive *a, char *path, int flags)
return (r);
}
if (mkdir(path, SECURE_DIR_MODE) == 0) {
le = new_fixup(a, path);
le->fixup |= FIXUP_MODE;
le->mode = extract->default_dir_mode;
if (mkdir(path, extract->default_dir_mode_initial) == 0) {
if (extract->default_dir_mode_initial
!= extract->default_dir_mode_final) {
le = new_fixup(a, path);
le->fixup |= FIXUP_MODE;
le->mode = extract->default_dir_mode_final;
}
return (ARCHIVE_OK);
}
@ -1052,12 +1087,26 @@ set_perm(struct archive *a, int fd, struct archive_entry *entry,
mode &= ~ S_ISGID;
}
/*
* Ensure we change permissions on the object we extracted,
* and not any incidental symlink that might have gotten in
* the way.
*/
if (!S_ISLNK(archive_entry_mode(entry))) {
if (S_ISLNK(archive_entry_mode(entry))) {
#ifdef HAVE_LCHMOD
/*
* If this is a symlink, use lchmod(). If the
* platform doesn't support lchmod(), just skip it as
* permissions on symlinks are actually ignored on
* most platforms.
*/
if (lchmod(name, mode) != 0) {
archive_set_error(a, errno, "Can't set permissions");
return (ARCHIVE_WARN);
}
#endif
} else if (!S_ISDIR(archive_entry_mode(entry))) {
/*
* If it's not a symlink and not a dir, then use
* fchmod() or chmod(), depending on whether we have
* an fd. Dirs get their perms set during the
* post-extract fixup, which is handled elsewhere.
*/
#ifdef HAVE_FCHMOD
if (fd >= 0) {
if (fchmod(fd, mode) != 0) {
@ -1067,22 +1116,13 @@ set_perm(struct archive *a, int fd, struct archive_entry *entry,
}
} else
#endif
if (chmod(name, mode) != 0) {
archive_set_error(a, errno, "Can't set permissions");
return (ARCHIVE_WARN);
}
#ifdef HAVE_LCHMOD
} else {
/*
* If lchmod() isn't supported, it's no big deal.
* Permissions on symlinks are actually ignored on
* most platforms.
*/
if (lchmod(name, mode) != 0) {
archive_set_error(a, errno, "Can't set permissions");
return (ARCHIVE_WARN);
}
#endif
/* If this platform lacks fchmod(), then
* we'll just use chmod(). */
if (chmod(name, mode) != 0) {
archive_set_error(a, errno,
"Can't set permissions");
return (ARCHIVE_WARN);
}
}
if (flags & ARCHIVE_EXTRACT_ACL) {