diff options
author | Tim Kientzle <kientzle@FreeBSD.org> | 2005-03-13 02:53:42 +0000 |
---|---|---|
committer | Tim Kientzle <kientzle@FreeBSD.org> | 2005-03-13 02:53:42 +0000 |
commit | 236d2801ad7127360de1bb809fe79b5b5f21361b (patch) | |
tree | 4c9ecd42c1869019e457d6081215cc5c8a55edeb | |
parent | 40be948e7a024ae4886eab8e00d8ca8e60c8c0bf (diff) | |
download | src-236d2801ad7127360de1bb809fe79b5b5f21361b.tar.gz src-236d2801ad7127360de1bb809fe79b5b5f21361b.zip |
Support extracting entries with pathnames longer than PATH_MAX. In
testing, I've archived and restored dir trees with ~1MB pathnames.
Most formats, of course, have much smaller limits.
Notes
Notes:
svn path=/head/; revision=143484
-rw-r--r-- | lib/libarchive/Makefile | 2 | ||||
-rw-r--r-- | lib/libarchive/archive_entry.c | 6 | ||||
-rw-r--r-- | lib/libarchive/archive_entry.h | 1 | ||||
-rw-r--r-- | lib/libarchive/archive_read_extract.c | 122 |
4 files changed, 104 insertions, 27 deletions
diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 347f445e12de..8ea2237fc6fe 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -7,7 +7,7 @@ LIB= archive -VERSION= 1.02.006 +VERSION= 1.02.013 ARCHIVE_API_FEATURE= 2 ARCHIVE_API_VERSION= 1 SHLIB_MAJOR= ${ARCHIVE_API_VERSION} diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index 8c52aa549781..f0f0042f4e87 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -598,6 +598,12 @@ archive_entry_set_pathname(struct archive_entry *entry, const char *name) } void +archive_entry_copy_pathname(struct archive_entry *entry, const char *name) +{ + aes_copy_mbs(&entry->ae_pathname, name); +} + +void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_pathname, name); diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index e5bdaf645a3c..2f67c8afa0e0 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -112,6 +112,7 @@ void archive_entry_set_link(struct archive_entry *, const char *); void archive_entry_set_mode(struct archive_entry *, mode_t); void archive_entry_set_mtime(struct archive_entry *, time_t, long); void archive_entry_set_pathname(struct archive_entry *, const char *); +void archive_entry_copy_pathname(struct archive_entry *, const char *); void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); void archive_entry_set_rdevminor(struct archive_entry *, dev_t); diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index 6a19dc640352..041a19b1df20 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -125,11 +125,11 @@ static int extract_symlink(struct archive *, struct archive_entry *, int); static unsigned int hash(const char *); static gid_t lookup_gid(struct archive *, const char *uname, gid_t); static uid_t lookup_uid(struct archive *, const char *uname, uid_t); +static int create_dir(struct archive *, const char *, int flags); +static int create_dir_mutable(struct archive *, char *, int flags); +static int create_dir_recursive(struct archive *, char *, int flags); static int create_parent_dir(struct archive *, const char *, int flags); -static int create_parent_dir_internal(struct archive *, char *, - int flags); -static int create_parent_dir_recursive(struct archive *, char *, - int flags); +static int create_parent_dir_mutable(struct archive *, char *, int flags); static int restore_metadata(struct archive *, struct archive_entry *, int flags); #ifdef HAVE_POSIX_ACL @@ -164,6 +164,7 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags) struct extract *extract; int ret; int restore_pwd; + char *original_filename; if (a->extract == NULL) { a->extract = malloc(sizeof(*a->extract)); @@ -180,16 +181,53 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags) extract->pst = NULL; extract->current_fixup = NULL; restore_pwd = -1; + original_filename = NULL; /* - * TODO: If pathname is longer than PATH_MAX, record starting - * directory and move to a suitable intermediate dir, which - * might require creating them! + * If pathname is longer than PATH_MAX, record starting directory + * and move to a suitable intermediate dir. */ if (strlen(archive_entry_pathname(entry)) > PATH_MAX) { + /* + * Yes, the copy here is necessary because we edit + * the pathname in-place to create intermediate dirnames. + */ + original_filename = strdup(archive_entry_pathname(entry)); + char *intdir, *tail; + restore_pwd = open(".", O_RDONLY); - /* XXX chdir() to a suitable intermediate dir XXX */ - /* XXX Update pathname in 'entry' XXX */ + /* + * "intdir" points to the initial dir section we're going + * to remove, "tail" points to the remainder of the path. + */ + intdir = tail = original_filename; + while (strlen(tail) > PATH_MAX) { + intdir = tail; + tail = intdir + PATH_MAX - 8; + while (tail > intdir && *tail != '/') + tail--; + if (tail <= intdir) { + close(restore_pwd); + archive_set_error(a, EPERM, + "Path element too long"); + return (ARCHIVE_WARN); + } + *tail = '\0'; /* Terminate dir portion */ + if (create_dir(a, intdir, flags) != ARCHIVE_OK) { + fchdir(restore_pwd); + close(restore_pwd); + return (ARCHIVE_WARN); + } + if (chdir(intdir) != 0) { + archive_set_error(a, errno, "Couldn't chdir"); + fchdir(restore_pwd); + close(restore_pwd); + return (ARCHIVE_WARN); + } + *tail = '/'; /* Restore the / we removed. */ + tail++; + } + archive_entry_set_pathname(entry, tail); } if (stat(archive_entry_pathname(entry), &extract->st) == 0) @@ -229,8 +267,11 @@ archive_read_extract(struct archive *a, struct archive_entry *entry, int flags) } /* If we changed directory above, restore it here. */ - if (restore_pwd >= 0) + if (restore_pwd >= 0 && original_filename != NULL) { fchdir(restore_pwd); + archive_entry_copy_pathname(entry, original_filename); + free(original_filename); + } return (ret); } @@ -364,6 +405,9 @@ sort_dir_list(struct fixup_entry *p) /* * Returns a new, initialized fixup entry. + * + * TODO: Reduce the memory requirements for this list by using a tree + * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive *a, const char *pathname) @@ -496,7 +540,7 @@ extract_dir(struct archive *a, struct archive_entry *entry, int flags) unlink(path); } else { /* Doesn't already exist; try building the parent path. */ - if (create_parent_dir_internal(a, path, flags) != ARCHIVE_OK) + if (create_parent_dir_mutable(a, path, flags) != ARCHIVE_OK) return (ARCHIVE_WARN); } @@ -533,27 +577,36 @@ success: static int create_parent_dir(struct archive *a, const char *path, int flags) { - struct extract *extract; int r; - extract = a->extract; - /* Copy path to mutable storage. */ - archive_strcpy(&(extract->create_parent_dir), path); + archive_strcpy(&(a->extract->create_parent_dir), path); + r = create_parent_dir_mutable(a, a->extract->create_parent_dir.s, flags); + return (r); +} - r = create_parent_dir_internal(a, extract->create_parent_dir.s, flags); +/* + * Like create_parent_dir, but creates the dir actually requested, not + * the parent. + */ +static int +create_dir(struct archive *a, const char *path, int flags) +{ + int r; + /* Copy path to mutable storage. */ + archive_strcpy(&(a->extract->create_parent_dir), path); + r = create_dir_mutable(a, a->extract->create_parent_dir.s, flags); return (r); } /* - * Handle remaining setup for create_parent_dir_recursive(), assuming - * path is already in mutable storage. + * Create the parent directory of the specified path, assuming path + * is already in mutable storage. */ static int -create_parent_dir_internal(struct archive *a, char *path, int flags) +create_parent_dir_mutable(struct archive *a, char *path, int flags) { char *slash; - mode_t old_umask; int r; /* Remove tail element to obtain parent name. */ @@ -561,10 +614,24 @@ create_parent_dir_internal(struct archive *a, char *path, int flags) if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; + r = create_dir_mutable(a, path, flags); + *slash = '/'; + return (r); +} + +/* + * Create the specified dir, assuming path is already in + * mutable storage. + */ +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_parent_dir_recursive(a, path, flags); + r = create_dir_recursive(a, path, flags); umask(old_umask); - *slash = '/'; return (r); } @@ -575,7 +642,7 @@ create_parent_dir_internal(struct archive *a, char *path, int flags) * Otherwise, returns ARCHIVE_WARN. */ static int -create_parent_dir_recursive(struct archive *a, char *path, int flags) +create_dir_recursive(struct archive *a, char *path, int flags) { struct stat st; struct extract *extract; @@ -600,7 +667,7 @@ create_parent_dir_recursive(struct archive *a, char *path, int flags) /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; - r = create_parent_dir_recursive(a, path, flags); + r = create_dir_recursive(a, path, flags); *slash = '/'; return (r); } @@ -632,7 +699,7 @@ create_parent_dir_recursive(struct archive *a, char *path, int flags) return (ARCHIVE_WARN); } else if (slash != NULL) { *slash = '\0'; - r = create_parent_dir_recursive(a, path, flags); + r = create_dir_recursive(a, path, flags); *slash = '/'; if (r != ARCHIVE_OK) return (r); @@ -1011,9 +1078,12 @@ set_perm(struct archive *a, struct archive_entry *entry, int mode, int flags) le = current_fixup(a, archive_entry_pathname(entry)); le->fixup |= FIXUP_FFLAGS; 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, archive_entry_pathname(entry), - archive_entry_mode(entry), set, clear); + mode, set, clear); if (r != ARCHIVE_OK) return (r); } |