diff options
author | Martin Matuska <martin@matuska.org> | 2019-05-26 01:48:39 +0200 |
---|---|---|
committer | Martin Matuska <martin@matuska.org> | 2019-05-26 08:18:49 +0200 |
commit | b66555921c0457645be2f75aaa876d7cf12af5c6 (patch) | |
tree | f20b63a22b50acf91041658768c31de00cc88d70 /libarchive/archive_write_disk_posix.c | |
parent | b837c72c423b744a2e6c554742877173406dbfa0 (diff) | |
download | libarchive-b66555921c0457645be2f75aaa876d7cf12af5c6.tar.gz |
archive_write_disk_posix: check_symlinks_fsobj() without chdir()
only on platforms with openat(), fstatat() and unlinkat() support
Diffstat (limited to 'libarchive/archive_write_disk_posix.c')
-rw-r--r-- | libarchive/archive_write_disk_posix.c | 116 |
1 files changed, 102 insertions, 14 deletions
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 0583fbd1..02745d17 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -165,6 +165,10 @@ __FBSDID("$FreeBSD$"); #define O_NOFOLLOW 0 #endif +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; @@ -349,6 +353,8 @@ struct archive_write_disk { #define HFS_BLOCKS(s) ((s) >> 12) + +static int la_opendirat(int, const char *); static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, @@ -402,6 +408,37 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); static int +la_opendirat(int fd, const char *path) { + const int flags = O_CLOEXEC +#if defined(O_BINARY) + | O_BINARY +#endif +#if defined(O_DIRECTORY) + | O_DIRECTORY +#endif +#if defined(O_PATH) + | O_PATH +#elif defined(O_SEARCH) + | O_SEARCH +#elif defined(O_EXEC) + | O_EXEC +#else + | O_RDONLY +#endif + ; + +#if !defined(HAVE_OPENAT) + if (fd != AT_FDCWD) { + errno = ENOTSUP; + return (-1); + } else + return (open(fd, path, flags)); +#else + return (openat(fd, path, flags)); +#endif +} + +static int lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { @@ -2565,7 +2602,8 @@ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { -#if !defined(HAVE_LSTAT) +#if !defined(HAVE_LSTAT) && \ + !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ @@ -2580,7 +2618,10 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, char c; int r; struct stat st; - int restore_pwd; + int chdir_fd; +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + int fd; +#endif /* Nothing to do here if name is empty */ if(path[0] == '\0') @@ -2601,9 +2642,9 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * c holds what used to be in *tail * last is 1 if this is the last tail */ - restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); - __archive_ensure_cloexec_flag(restore_pwd); - if (restore_pwd < 0) { + chdir_fd = la_opendirat(AT_FDCWD, "."); + __archive_ensure_cloexec_flag(chdir_fd); + if (chdir_fd < 0) { fsobj_error(a_eno, a_estr, errno, "Could not open ", path); return (ARCHIVE_FATAL); @@ -2636,7 +2677,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); +#else r = lstat(head, &st); +#endif if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ @@ -2662,7 +2707,19 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } } else if (S_ISDIR(st.st_mode)) { if (!last) { - if (chdir(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + fd = la_opendirat(chdir_fd, head); + if (fd < 0) + r = -1; + else { + r = 0; + close(chdir_fd); + chdir_fd = fd; + } +#else + r = chdir(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); @@ -2679,7 +2736,12 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * so we can overwrite it with the * item being extracted. */ - if (unlink(head)) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = unlinkat(chdir_fd, head, 0); +#else + r = unlink(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not remove symlink ", @@ -2709,7 +2771,12 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, break; } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (unlink(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = unlinkat(chdir_fd, head, 0); +#else + r = unlink(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot remove intervening " @@ -2727,7 +2794,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * This is needed to extract hardlinks over * symlinks. */ +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + r = fstatat(chdir_fd, head, &st, 0); +#else r = la_stat(head, &st); +#endif if (r != 0) { tail[0] = c; if (errno == ENOENT) { @@ -2740,7 +2811,19 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, break; } } else if (S_ISDIR(st.st_mode)) { - if (chdir(head) != 0) { +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + fd = la_opendirat(chdir_fd, head); + if (fd < 0) + r = -1; + else { + r = 0; + close(chdir_fd); + chdir_fd = fd; + } +#else + r = chdir(head); +#endif + if (r != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, @@ -2776,16 +2859,21 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } /* Catches loop exits via break */ tail[0] = c; -#ifdef HAVE_FCHDIR +#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) + /* If we operate with openat(), fstatat() and unlinkat() there was + * no chdir(), so just close the fd */ + if (chdir_fd >= 0) + close(chdir_fd); +#elif HAVE_FCHDIR /* If we changed directory above, restore it here. */ - if (restore_pwd >= 0) { - r = fchdir(restore_pwd); + if (chdir_fd >= 0) { + r = fchdir(chdir_fd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } - close(restore_pwd); - restore_pwd = -1; + close(chdir_fd); + chdir_fd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } |