]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
archive_write_disk_posix: check_symlinks_fsobj() without chdir()
authorMartin Matuska <martin@matuska.org>
Sat, 25 May 2019 23:48:39 +0000 (01:48 +0200)
committerMartin Matuska <martin@matuska.org>
Sun, 26 May 2019 06:18:49 +0000 (08:18 +0200)
only on platforms with openat(), fstatat() and unlinkat() support

CMakeLists.txt
configure.ac
libarchive/archive_write_disk_posix.c

index 0d6b8f14062b807f7d95e3d8d77b46957d86f57f..f8f0c7553123ba36d2a4237282467f35bee82b0f 100644 (file)
@@ -1350,6 +1350,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR)
 CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK)
 CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM)
 CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET)
+CHECK_FUNCTION_EXISTS_GLIBC(unlinkat HAVE_TZSET)
 CHECK_FUNCTION_EXISTS_GLIBC(unsetenv HAVE_UNSETENV)
 CHECK_FUNCTION_EXISTS_GLIBC(utime HAVE_UTIME)
 CHECK_FUNCTION_EXISTS_GLIBC(utimes HAVE_UTIMES)
index 64f77a6736820b5607eac00493398cfa69e51a13..d5339d37540a025836967dcd4afcd4f4112d91b0 100644 (file)
@@ -636,7 +636,7 @@ AC_CHECK_FUNCS([nl_langinfo openat pipe poll posix_spawnp readlink readlinkat])
 AC_CHECK_FUNCS([readpassphrase])
 AC_CHECK_FUNCS([select setenv setlocale sigaction statfs statvfs])
 AC_CHECK_FUNCS([strchr strdup strerror strncpy_s strrchr symlink timegm])
-AC_CHECK_FUNCS([tzset unsetenv utime utimensat utimes vfork])
+AC_CHECK_FUNCS([tzset unlinkat unsetenv utime utimensat utimes vfork])
 AC_CHECK_FUNCS([wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove])
 AC_CHECK_FUNCS([_ctime64_s _fseeki64])
 AC_CHECK_FUNCS([_get_timezone _localtime64_s _mkgmtime64])
index 0583fbd138d3f5306e63439b9a583ca1aecfc9f3..02745d1782d8e8f8a3f0636253387f56ad8b8080 100644 (file)
@@ -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 *,
@@ -401,6 +407,37 @@ static ssize_t     _archive_write_disk_data(struct archive *, const void *,
 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)
 {
@@ -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);
                }