diff options
Diffstat (limited to 'libarchive/archive_write_disk_posix.c')
-rw-r--r-- | libarchive/archive_write_disk_posix.c | 62 |
1 files changed, 43 insertions, 19 deletions
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index fcd733aff51e..aadc58718f9b 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -2462,6 +2462,7 @@ _archive_write_disk_close(struct archive *_a) struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; struct stat st; + char *c; int fd, ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -2475,24 +2476,49 @@ _archive_write_disk_close(struct archive *_a) while (p != NULL) { fd = -1; a->pst = NULL; /* Mark stat cache as out-of-date. */ - if (p->fixup & - (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { - fd = open(p->name, - O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); + + /* We must strip trailing slashes from the path to avoid + dereferencing symbolic links to directories */ + c = p->name; + while (*c != '\0') + c++; + while (c != p->name && *(c - 1) == '/') { + c--; + *c = '\0'; + } + + if (p->fixup == 0) + goto skip_fixup_entry; + else { + fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY +#if defined(O_DIRECTORY) + | O_DIRECTORY +#endif + | O_CLOEXEC); + /* + ` * If we don't support O_DIRECTORY, + * or open() has failed, we must stat() + * to verify that we are opening a directory + */ +#if defined(O_DIRECTORY) if (fd == -1) { - /* If we cannot lstat, skip entry */ - if (lstat(p->name, &st) != 0) + if (lstat(p->name, &st) != 0 || + !S_ISDIR(st.st_mode)) { goto skip_fixup_entry; - /* - * If we deal with a symbolic link, mark - * it in the fixup mode to ensure no - * modifications are made to its target. - */ - if (S_ISLNK(st.st_mode)) { - p->mode &= ~S_IFMT; - p->mode |= S_IFLNK; } } +#else +#if HAVE_FSTAT + if (fd > 0 && ( + fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) { + goto skip_fixup_entry; + } else +#endif + if (lstat(p->name, &st) != 0 || + !S_ISDIR(st.st_mode)) { + goto skip_fixup_entry; + } +#endif } if (p->fixup & TODO_TIMES) { set_times(a, fd, p->mode, p->name, @@ -2504,14 +2530,13 @@ _archive_write_disk_close(struct archive *_a) if (p->fixup & TODO_MODE_BASE) { #ifdef HAVE_FCHMOD if (fd >= 0) - fchmod(fd, p->mode); + fchmod(fd, p->mode & 07777); else #endif #ifdef HAVE_LCHMOD - lchmod(p->name, p->mode); + lchmod(p->name, p->mode & 07777); #else - if (!S_ISLNK(p->mode)) - chmod(p->name, p->mode); + chmod(p->name, p->mode & 07777); #endif } if (p->fixup & TODO_ACLS) @@ -2664,7 +2689,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname) fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; - fe->mode = 0; fe->name = strdup(pathname); return (fe); } |