commit 5096d11454f1643666ae41237a723d5e5ecdab06 Author: Doran Moppert Date: Fri Aug 12 14:17:55 2016 +0930 backport 3c378bb - (jamtart-patches) Fix for hardlinks with .. in target path diff --git a/libarchive/archive_write_disk.c b/libarchive/archive_write_disk.c index 2acf847..ac525e2 100644 --- a/libarchive/archive_write_disk.c +++ b/libarchive/archive_write_disk.c @@ -218,13 +218,14 @@ struct archive_write_disk { #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 -static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags); +static int check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #ifdef HAVE_FCHDIR static void edit_deep_directories(struct archive_write_disk *ad); #endif +static int cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); @@ -1082,7 +1083,7 @@ create_filesystem_object(struct archive_write_disk *a) const char *linkname; mode_t final_mode, mode; int r; - /* these for check_path_for_symlinks */ + /* these for check_symlinks_fsobj */ char *linkname_copy; /* non-const copy of linkname */ struct archive_string error_string; int error_number; @@ -1099,13 +1100,22 @@ create_filesystem_object(struct archive_write_disk *a) if (linkname_copy == NULL) { return (EPERM); } - r = check_path_for_symlinks(linkname_copy, &error_number, &error_string, a->flags); - free(linkname_copy); + /* TODO: consider using the cleaned-up path as the link target? */ + r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, error_number, "%s", error_string.s); + free(linkname_copy); + /* EPERM is more appropriate than error_number for our callers */ + return (EPERM); + } + r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); /* EPERM is more appropriate than error_number for our callers */ + free(linkname_copy); return (EPERM); } + free(linkname_copy); r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries @@ -1443,7 +1453,8 @@ current_fixup(struct archive_write_disk *a, const char *pathname) * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ -static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags) +static int +check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) { #if !defined(HAVE_LSTAT) /* Platform doesn't have lstat, so we can't look for symlinks. */ @@ -1635,7 +1646,7 @@ check_symlinks(struct archive_write_disk *a) int error_number; int rc; archive_string_init(&error_string); - rc = check_path_for_symlinks(a->name, &error_number, &error_string, a->flags); + rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } @@ -1703,15 +1714,17 @@ cleanup_pathname_win(struct archive_write_disk *a) * set) any '..' in the path. */ static int -cleanup_pathname(struct archive_write_disk *a) +cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) { char *dest, *src; char separator = '\0'; - dest = src = a->name; + dest = src = path; if (*src == '\0') { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid empty pathname"); + if (error_number) *error_number = ARCHIVE_ERRNO_MISC; + if (error_string) + archive_string_sprintf(error_string, + "Invalid empty pathname"); return (ARCHIVE_FAILED); } @@ -1742,10 +1755,11 @@ cleanup_pathname(struct archive_write_disk *a) } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ - if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Path contains '..'"); + if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { + if (error_number) *error_number = ARCHIVE_ERRNO_MISC; + if (error_string) + archive_string_sprintf(error_string, + "Path contains '..'"); return (ARCHIVE_FAILED); } } @@ -1776,7 +1790,7 @@ cleanup_pathname(struct archive_write_disk *a) * We've just copied zero or more path elements, not including the * final '/'. */ - if (dest == a->name) { + if (dest == path) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. @@ -1791,6 +1805,21 @@ cleanup_pathname(struct archive_write_disk *a) return (ARCHIVE_OK); } +static int +cleanup_pathname(struct archive_write_disk *a) +{ + struct archive_string error_string; + int error_number; + int rc; + archive_string_init(&error_string); + rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); + if (rc != ARCHIVE_OK) { + archive_set_error(&a->archive, error_number, "%s", error_string.s); + } + archive_string_free(&error_string); + return rc; +} + /* * Create the parent directory of the specified path, assuming path * is already in mutable storage.