]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
On Windows, make sure libarchive does not store the Windows style path
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 21 Feb 2012 08:18:32 +0000 (17:18 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 21 Feb 2012 08:18:32 +0000 (17:18 +0900)
separator '\' character into cpio, pax, ustar, gtar and zip archives,
because it is not interoperable between Windows and POSIX platform,
so we should replace '\' character in the pathnames with '/'.

libarchive/archive_windows.c
libarchive/archive_windows.h
libarchive/archive_write_set_format_cpio.c
libarchive/archive_write_set_format_cpio_newc.c
libarchive/archive_write_set_format_gnutar.c
libarchive/archive_write_set_format_pax.c
libarchive/archive_write_set_format_ustar.c
libarchive/archive_write_set_format_zip.c

index 65bf4d36745c5b9564d6aba96d518d890f6dad3a..4da4f7622985fadc1d585222dc4cf3634653615a 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "archive_platform.h"
 #include "archive_private.h"
+#include "archive_entry.h"
 #include <ctype.h>
 #include <errno.h>
 #include <stddef.h>
@@ -690,6 +691,113 @@ __la_write(int fd, const void *buf, size_t nbytes)
        return (bytes_written);
 }
 
+/*
+ * Replace the Windows path separator '\' with '/'.
+ */
+static int
+replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
+{
+       wchar_t *w;
+       size_t path_length;
+
+       if (wp == NULL)
+               return(0);
+       if (wcschr(wp, L'\\') == NULL)
+               return(0);
+       path_length = wcslen(wp);
+       if (archive_wstring_ensure(ws, path_length) == NULL)
+               return(-1);
+       archive_wstrncpy(ws, wp, path_length);
+       for (w = ws->s; *w; w++) {
+               if (*w == L'\\')
+                       *w = L'/';
+       }
+       return(1);
+}
+
+static int
+fix_pathseparator(struct archive_entry *entry)
+{
+       struct archive_wstring ws;
+       const wchar_t *wp;
+       int ret = ARCHIVE_OK;
+
+       archive_string_init(&ws);
+       wp = archive_entry_pathname_w(entry);
+       switch (replace_pathseparator(&ws, wp)) {
+       case 0: /* Not replaced. */
+               break;
+       case 1: /* Replaced. */
+               archive_entry_copy_pathname_w(entry, ws.s);
+               break;
+       default:
+               ret = ARCHIVE_FAILED;
+       }
+       wp = archive_entry_hardlink_w(entry);
+       switch (replace_pathseparator(&ws, wp)) {
+       case 0: /* Not replaced. */
+               break;
+       case 1: /* Replaced. */
+               archive_entry_copy_hardlink_w(entry, ws.s);
+               break;
+       default:
+               ret = ARCHIVE_FAILED;
+       }
+       wp = archive_entry_symlink_w(entry);
+       switch (replace_pathseparator(&ws, wp)) {
+       case 0: /* Not replaced. */
+               break;
+       case 1: /* Replaced. */
+               archive_entry_copy_symlink_w(entry, ws.s);
+               break;
+       default:
+               ret = ARCHIVE_FAILED;
+       }
+       archive_wstring_free(&ws);
+       return(ret);
+}
+
+struct archive_entry *
+__la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
+{
+       struct archive_entry *entry_main;
+       const wchar_t *wp;
+       int has_backslash = 0;
+       int ret;
+
+       wp = archive_entry_pathname_w(entry);
+       if (wp != NULL && wcschr(wp, L'\\') != NULL)
+               has_backslash = 1;
+       if (!has_backslash) {
+               wp = archive_entry_hardlink_w(entry);
+               if (wp != NULL && wcschr(wp, L'\\') != NULL)
+                       has_backslash = 1;
+       }
+       if (!has_backslash) {
+               wp = archive_entry_symlink_w(entry);
+               if (wp != NULL && wcschr(wp, L'\\') != NULL)
+                       has_backslash = 1;
+       }
+       /*
+        * If there is no backslach chars, return the original.
+        */
+       if (!has_backslash)
+               return (entry);
+
+       /* Copy entry so we can modify it as needed. */
+       entry_main = archive_entry_clone(entry);
+       if (entry_main == NULL)
+               return (NULL);
+       /* Replace the Windows path-separator '\' with '/'. */
+       ret = fix_pathseparator(entry_main);
+       if (ret < ARCHIVE_WARN) {
+               archive_entry_free(entry_main);
+               return (NULL);
+       }
+       return (entry_main);
+}
+
+
 /*
  * The following function was modified from PostgreSQL sources and is
  * subject to the copyright below.
index 4a08f277a34016f8372940c28b460b982b351180..a8e54c593d078e30c638726c2b5181a30722c995 100644 (file)
@@ -265,6 +265,8 @@ extern wchar_t *__la_win_permissive_name(const char *name);
 extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname);
 extern void __la_dosmaperr(unsigned long e);
 #define la_dosmaperr(e) __la_dosmaperr(e)
+extern struct archive_entry *__la_win_entry_in_posix_pathseparator(
+    struct archive_entry *);
 
 #if defined(HAVE_WCRTOMB) && defined(__BORLANDC__)
 typedef int mbstate_t;
index e356703c4ce6a785651d6f572256f14179861bd1..352649365d6fb4bdcb3ee8ede80df386b0e30c2d 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -280,18 +281,37 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        int64_t ino;
        char h[76];
        struct archive_string_conv *sconv;
+       struct archive_entry *entry_main;
        size_t len;
 
        cpio = (struct cpio *)a->format_data;
        ret_final = ARCHIVE_OK;
        sconv = get_sconv(a);
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       entry_main = __la_win_entry_in_posix_pathseparator(entry);
+       if (entry_main == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate ustar data");
+               return(ARCHIVE_FATAL);
+       }
+       if (entry != entry_main)
+               entry = entry_main;
+       else
+               entry_main = NULL;
+#else
+       entry_main = NULL;
+#endif
+
        ret = archive_entry_pathname_l(entry, &path, &len, sconv);
        if (ret != 0) {
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Pathname");
-                       return (ARCHIVE_FATAL);
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Can't translate pathname '%s' to %s",
@@ -310,11 +330,13 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        if (ino < 0) {
                archive_set_error(&a->archive, ENOMEM,
                    "No memory for ino translation table");
-               return (ARCHIVE_FATAL);
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
        } else if (ino > 0777777) {
                archive_set_error(&a->archive, ERANGE,
                    "Too many files for this cpio format");
-               return (ARCHIVE_FATAL);
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
        }
        format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
 
@@ -341,7 +363,8 @@ write_header(struct archive_write *a, struct archive_entry *entry)
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Linkname");
-                       return (ARCHIVE_FATAL);
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Can't translate linkname '%s' to %s",
@@ -358,25 +381,35 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        if (ret) {
                archive_set_error(&a->archive, ERANGE,
                    "File is too large for cpio format.");
-               return (ARCHIVE_FAILED);
+               ret_final = ARCHIVE_FAILED;
+               goto exit_write_header;
        }
 
        ret = __archive_write_output(a, h, sizeof(h));
-       if (ret != ARCHIVE_OK)
-               return (ARCHIVE_FATAL);
+       if (ret != ARCHIVE_OK) {
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
+       }
 
        ret = __archive_write_output(a, path, pathlength);
-       if (ret != ARCHIVE_OK)
-               return (ARCHIVE_FATAL);
+       if (ret != ARCHIVE_OK) {
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
+       }
 
        cpio->entry_bytes_remaining = archive_entry_size(entry);
 
        /* Write the symlink now. */
        if (p != NULL  &&  *p != '\0') {
                ret = __archive_write_output(a, p, strlen(p));
-               if (ret != ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
+               if (ret != ARCHIVE_OK) {
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
+               }
        }
+exit_write_header:
+       if (entry_main)
+               archive_entry_free(entry_main);
        return (ret_final);
 }
 
index f16ac33e0a6bd2c33927641ebc56d5c60cbc689c..a9bfa808cc1a7ec90192c485718217fd076a69fc 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -222,6 +223,7 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        int pathlength, ret, ret_final;
        char h[c_header_size];
        struct archive_string_conv *sconv;
+       struct archive_entry *entry_main;
        size_t len;
        int pad;
 
@@ -229,12 +231,30 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        ret_final = ARCHIVE_OK;
        sconv = get_sconv(a);
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       entry_main = __la_win_entry_in_posix_pathseparator(entry);
+       if (entry_main == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate ustar data");
+               return(ARCHIVE_FATAL);
+       }
+       if (entry != entry_main)
+               entry = entry_main;
+       else
+               entry_main = NULL;
+#else
+       entry_main = NULL;
+#endif
+
        ret = archive_entry_pathname_l(entry, &path, &len, sconv);
        if (ret != 0) {
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Pathname");
-                       return (ARCHIVE_FATAL);
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Can't translate pathname '%s' to %s",
@@ -286,7 +306,8 @@ write_header(struct archive_write *a, struct archive_entry *entry)
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Likname");
-                       return (ARCHIVE_FATAL);
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Can't translate linkname '%s' to %s",
@@ -303,22 +324,29 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        if (ret) {
                archive_set_error(&a->archive, ERANGE,
                    "File is too large for this format.");
-               return (ARCHIVE_FAILED);
+               ret_final = ARCHIVE_FAILED;
+               goto exit_write_header;
        }
 
        ret = __archive_write_output(a, h, c_header_size);
-       if (ret != ARCHIVE_OK)
-               return (ARCHIVE_FATAL);
+       if (ret != ARCHIVE_OK) {
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
+       }
 
        /* Pad pathname to even length. */
        ret = __archive_write_output(a, path, pathlength);
-       if (ret != ARCHIVE_OK)
-               return (ARCHIVE_FATAL);
+       if (ret != ARCHIVE_OK) {
+               ret_final = ARCHIVE_FATAL;
+               goto exit_write_header;
+       }
        pad = PAD4(pathlength + c_header_size);
        if (pad) {
                ret = __archive_write_output(a, "\0\0\0", pad);
-               if (ret != ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
+               if (ret != ARCHIVE_OK) {
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
+               }
        }
 
        cpio->entry_bytes_remaining = archive_entry_size(entry);
@@ -327,13 +355,20 @@ write_header(struct archive_write *a, struct archive_entry *entry)
        /* Write the symlink now. */
        if (p != NULL  &&  *p != '\0') {
                ret = __archive_write_output(a, p, strlen(p));
-               if (ret != ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
+               if (ret != ARCHIVE_OK) {
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
+               }
                pad = PAD4(strlen(p));
                ret = __archive_write_output(a, "\0\0\0", pad);
-               if (ret != ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
+               if (ret != ARCHIVE_OK) {
+                       ret_final = ARCHIVE_FATAL;
+                       goto exit_write_header;
+               }
        }
+exit_write_header:
+       if (entry_main)
+               archive_entry_free(entry_main);
        return (ret_final);
 }
 
index dbb378c364f2b0820426a355d87583492988d45c..13942c132b185cd727efe60e4704a0ee79177673 100644 (file)
@@ -278,6 +278,7 @@ archive_write_gnutar_header(struct archive_write *a,
        int tartype;
        struct gnutar *gnutar;
        struct archive_string_conv *sconv;
+       struct archive_entry *entry_main;
 
        gnutar = (struct gnutar *)a->format_data;
 
@@ -301,33 +302,95 @@ archive_write_gnutar_header(struct archive_write *a,
 
        if (AE_IFDIR == archive_entry_filetype(entry)) {
                const char *p;
-               char *t;
+               size_t path_length;
                /*
                 * Ensure a trailing '/'.  Modify the entry so
                 * the client sees the change.
                 */
-               p = archive_entry_pathname(entry);
-               if (p[strlen(p) - 1] != '/') {
-                       t = (char *)malloc(strlen(p) + 2);
-                       if (t == NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+               const wchar_t *wp;
+
+               wp = archive_entry_pathname_w(entry);
+               if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+                       struct archive_wstring ws;
+
+                       archive_string_init(&ws);
+                       path_length = wcslen(wp);
+                       if (archive_wstring_ensure(&ws,
+                           path_length + 2) == NULL) {
                                archive_set_error(&a->archive, ENOMEM,
-                               "Can't allocate gnutar data");
+                                   "Can't allocate ustar data");
+                               archive_wstring_free(&ws);
                                return(ARCHIVE_FATAL);
                        }
-                       strcpy(t, p);
-                       strcat(t, "/");
-                       archive_entry_copy_pathname(entry, t);
-                       free(t);
+                       /* Should we keep '\' ? */
+                       if (wp[path_length -1] == L'\\')
+                               path_length--;
+                       archive_wstrncpy(&ws, wp, path_length);
+                       archive_wstrappend_wchar(&ws, L'/');
+                       archive_entry_copy_pathname_w(entry, ws.s);
+                       archive_wstring_free(&ws);
+                       p = NULL;
+               } else
+#endif
+                       p = archive_entry_pathname(entry);
+               /*
+                * On Windows, this is a backup operation just in
+                * case getting WCS failed. On POSIX, this is a
+                * normal operation.
+                */
+               if (p != NULL && p[strlen(p) - 1] != '/') {
+                       struct archive_string as;
+
+                       archive_string_init(&as);
+                       path_length = strlen(p);
+                       if (archive_string_ensure(&as,
+                           path_length + 2) == NULL) {
+                               archive_set_error(&a->archive, ENOMEM,
+                                   "Can't allocate ustar data");
+                               archive_string_free(&as);
+                               return(ARCHIVE_FATAL);
+                       }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       /* NOTE: This might break the pathname
+                        * if the current code page is CP932 and
+                        * the pathname includes a character '\'
+                        * as a part of its multibyte pathname. */
+                       if (p[strlen(p) -1] == '\\')
+                               path_length--;
+                       else
+#endif
+                       archive_strncpy(&as, p, path_length);
+                       archive_strappend_char(&as, '/');
+                       archive_entry_copy_pathname(entry, as.s);
+                       archive_string_free(&as);
                }
        }
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       entry_main = __la_win_entry_in_posix_pathseparator(entry);
+       if (entry_main == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate ustar data");
+               return(ARCHIVE_FATAL);
+       }
+       if (entry != entry_main)
+               entry = entry_main;
+       else
+               entry_main = NULL;
+#else
+       entry_main = NULL;
+#endif
        r = archive_entry_pathname_l(entry, &(gnutar->pathname),
            &(gnutar->pathname_length), sconv);
        if (r != 0) {
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Pathame");
-                       return (ARCHIVE_FATAL);
+                       ret = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Can't translate pathname '%s' to %s",
@@ -341,7 +404,8 @@ archive_write_gnutar_header(struct archive_write *a,
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Uname");
-                       return (ARCHIVE_FATAL);
+                       ret = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive,
                    ARCHIVE_ERRNO_FILE_FORMAT,
@@ -356,7 +420,8 @@ archive_write_gnutar_header(struct archive_write *a,
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Gname");
-                       return (ARCHIVE_FATAL);
+                       ret = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive,
                    ARCHIVE_ERRNO_FILE_FORMAT,
@@ -373,7 +438,8 @@ archive_write_gnutar_header(struct archive_write *a,
                if (errno == ENOMEM) {
                        archive_set_error(&a->archive, ENOMEM,
                            "Can't allocate memory for Linkname");
-                       return (ARCHIVE_FATAL);
+                       ret = ARCHIVE_FATAL;
+                       goto exit_write_header;
                }
                archive_set_error(&a->archive,
                    ARCHIVE_ERRNO_FILE_FORMAT,
@@ -389,7 +455,8 @@ archive_write_gnutar_header(struct archive_write *a,
                        if (errno == ENOMEM) {
                                archive_set_error(&a->archive, ENOMEM,
                                    "Can't allocate memory for Linkname");
-                               return (ARCHIVE_FATAL);
+                               ret = ARCHIVE_FATAL;
+                               goto exit_write_header;
                        }
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
@@ -412,18 +479,18 @@ archive_write_gnutar_header(struct archive_write *a,
                archive_entry_set_size(temp, gnutar->linkname_length + 1);
                ret = archive_format_gnutar_header(a, buff, temp, 'K');
                if (ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                ret = __archive_write_output(a, buff, 512);
                if(ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                archive_entry_free(temp);
                /* Write as many 512 bytes blocks as needed to write full name. */
                ret = __archive_write_output(a, gnutar->linkname, todo);
                if(ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo));
                if (ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
        }
 
        /* If pathname is longer than 100 chars we need to add an 'L' header. */
@@ -441,18 +508,18 @@ archive_write_gnutar_header(struct archive_write *a,
                archive_entry_set_size(temp, gnutar->pathname_length + 1);
                ret = archive_format_gnutar_header(a, buff, temp, 'L');
                if (ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                ret = __archive_write_output(a, buff, 512);
                if(ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                archive_entry_free(temp);
                /* Write as many 512 bytes blocks as needed to write full name. */
                ret = __archive_write_output(a, pathname, todo);
                if(ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
                ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)todo));
                if (ret < ARCHIVE_WARN)
-                       return (ret);
+                       goto exit_write_header;
        }
 
        if (archive_entry_hardlink(entry) != NULL) {
@@ -469,28 +536,35 @@ archive_write_gnutar_header(struct archive_write *a,
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
                            "tar format cannot archive socket");
-                       return (ARCHIVE_FAILED);
+                       ret = ARCHIVE_FAILED;
+                       goto exit_write_header;
                default:
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
                            "tar format cannot archive this (mode=0%lo)",
                            (unsigned long)archive_entry_mode(entry));
-                       return (ARCHIVE_FAILED);
+                       ret = ARCHIVE_FAILED;
+                       goto exit_write_header;
                }
 
        ret = archive_format_gnutar_header(a, buff, entry, tartype);
        if (ret < ARCHIVE_WARN)
-               return (ret);
+               goto exit_write_header;
        if (ret2 < ret)
                ret = ret2;
        ret2 = __archive_write_output(a, buff, 512);
-       if (ret2 < ARCHIVE_WARN)
-               return (ret2);
+       if (ret2 < ARCHIVE_WARN) {
+               ret = ret2;
+               goto exit_write_header;
+       }
        if (ret2 < ret)
                ret = ret2;
 
        gnutar->entry_bytes_remaining = archive_entry_size(entry);
        gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);
+exit_write_header:
+       if (entry_main)
+               archive_entry_free(entry_main);
        return (ret);
 }
 
index 2c2ef308ad286c37dc3021e58bb877ad1b5adcf8..dec6bc3630949b1ceae0120116935ae808a0e9db 100644 (file)
@@ -465,7 +465,6 @@ archive_write_pax_header(struct archive_write *a,
 {
        struct archive_entry *entry_main;
        const char *p;
-       char *t;
        const char *suffix;
        int need_extension, r, ret;
        int sparse_count;
@@ -543,24 +542,73 @@ archive_write_pax_header(struct archive_write *a,
                case AE_IFREG:
                        break;
                case AE_IFDIR:
+               {
                        /*
                         * Ensure a trailing '/'.  Modify the original
                         * entry so the client sees the change.
                         */
-                       p = archive_entry_pathname(entry_original);
-                       if (p[strlen(p) - 1] != '/') {
-                               t = (char *)malloc(strlen(p) + 2);
-                               if (t == NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       const wchar_t *wp;
+
+                       wp = archive_entry_pathname_w(entry_original);
+                       if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+                               struct archive_wstring ws;
+
+                               archive_string_init(&ws);
+                               path_length = wcslen(wp);
+                               if (archive_wstring_ensure(&ws,
+                                   path_length + 2) == NULL) {
+                                       archive_set_error(&a->archive, ENOMEM,
+                                           "Can't allocate pax data");
+                                       archive_wstring_free(&ws);
+                                       return(ARCHIVE_FATAL);
+                               }
+                               /* Should we keep '\' ? */
+                               if (wp[path_length -1] == L'\\')
+                                       path_length--;
+                               archive_wstrncpy(&ws, wp, path_length);
+                               archive_wstrappend_wchar(&ws, L'/');
+                               archive_entry_copy_pathname_w(
+                                   entry_original, ws.s);
+                               archive_wstring_free(&ws);
+                               p = NULL;
+                       } else
+#endif
+                               p = archive_entry_pathname(entry_original);
+                       /*
+                        * On Windows, this is a backup operation just in
+                        * case getting WCS failed. On POSIX, this is a
+                        * normal operation.
+                        */
+                       if (p != NULL && p[strlen(p) - 1] != '/') {
+                               struct archive_string as;
+
+                               archive_string_init(&as);
+                               path_length = strlen(p);
+                               if (archive_string_ensure(&as,
+                                   path_length + 2) == NULL) {
                                        archive_set_error(&a->archive, ENOMEM,
-                                       "Can't allocate pax data");
+                                           "Can't allocate pax data");
+                                       archive_string_free(&as);
                                        return(ARCHIVE_FATAL);
                                }
-                               strcpy(t, p);
-                               strcat(t, "/");
-                               archive_entry_copy_pathname(entry_original, t);
-                               free(t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                               /* NOTE: This might break the pathname
+                                * if the current code page is CP932 and
+                                * the pathname includes a character '\'
+                                * as a part of its multibyte pathname. */
+                               if (p[strlen(p) -1] == '\\')
+                                       path_length--;
+                               else
+#endif
+                               archive_strncpy(&as, p, path_length);
+                               archive_strappend_char(&as, '/');
+                               archive_entry_copy_pathname(
+                                   entry_original, as.s);
+                               archive_string_free(&as);
                        }
                        break;
+               }
                case AE_IFSOCK:
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
@@ -657,7 +705,20 @@ archive_write_pax_header(struct archive_write *a,
        }
 
        /* Copy entry so we can modify it as needed. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       entry_main = __la_win_entry_in_posix_pathseparator(entry_original);
+       if (entry_main == entry_original)
+               entry_main = archive_entry_clone(entry_original);
+#else
        entry_main = archive_entry_clone(entry_original);
+#endif
+       if (entry_main == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate pax data");
+               return(ARCHIVE_FATAL);
+       }
        archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
        archive_string_empty(&(pax->sparse_map));
        sparse_total = 0;
index 1bc2372c9985a5dd0427ebdecb06307d4b9774be..484ab34b2305a1590b957a9ef1a18e2baf407131 100644 (file)
@@ -239,6 +239,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
        char buff[512];
        int ret, ret2;
        struct ustar *ustar;
+       struct archive_entry *entry_main;
        struct archive_string_conv *sconv;
 
        ustar = (struct ustar *)a->format_data;
@@ -269,37 +270,106 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
 
        if (AE_IFDIR == archive_entry_filetype(entry)) {
                const char *p;
-               char *t;
+               size_t path_length;
                /*
                 * Ensure a trailing '/'.  Modify the entry so
                 * the client sees the change.
                 */
-               p = archive_entry_pathname(entry);
-               if (p[strlen(p) - 1] != '/') {
-                       t = (char *)malloc(strlen(p) + 2);
-                       if (t == NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+               const wchar_t *wp;
+
+               wp = archive_entry_pathname_w(entry);
+               if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+                       struct archive_wstring ws;
+
+                       archive_string_init(&ws);
+                       path_length = wcslen(wp);
+                       if (archive_wstring_ensure(&ws,
+                           path_length + 2) == NULL) {
+                               archive_set_error(&a->archive, ENOMEM,
+                                   "Can't allocate ustar data");
+                               archive_wstring_free(&ws);
+                               return(ARCHIVE_FATAL);
+                       }
+                       /* Should we keep '\' ? */
+                       if (wp[path_length -1] == L'\\')
+                               path_length--;
+                       archive_wstrncpy(&ws, wp, path_length);
+                       archive_wstrappend_wchar(&ws, L'/');
+                       archive_entry_copy_pathname_w(entry, ws.s);
+                       archive_wstring_free(&ws);
+                       p = NULL;
+               } else
+#endif
+                       p = archive_entry_pathname(entry);
+               /*
+                * On Windows, this is a backup operation just in
+                * case getting WCS failed. On POSIX, this is a
+                * normal operation.
+                */
+               if (p != NULL && p[strlen(p) - 1] != '/') {
+                       struct archive_string as;
+
+                       archive_string_init(&as);
+                       path_length = strlen(p);
+                       if (archive_string_ensure(&as,
+                           path_length + 2) == NULL) {
                                archive_set_error(&a->archive, ENOMEM,
-                               "Can't allocate ustar data");
+                                   "Can't allocate ustar data");
+                               archive_string_free(&as);
                                return(ARCHIVE_FATAL);
                        }
-                       strcpy(t, p);
-                       strcat(t, "/");
-                       archive_entry_copy_pathname(entry, t);
-                       free(t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+                       /* NOTE: This might break the pathname
+                        * if the current code page is CP932 and
+                        * the pathname includes a character '\'
+                        * as a part of its multibyte pathname. */
+                       if (p[strlen(p) -1] == '\\')
+                               path_length--;
+                       else
+#endif
+                       archive_strncpy(&as, p, path_length);
+                       archive_strappend_char(&as, '/');
+                       archive_entry_copy_pathname(entry, as.s);
+                       archive_string_free(&as);
                }
        }
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       entry_main = __la_win_entry_in_posix_pathseparator(entry);
+       if (entry_main == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate ustar data");
+               return(ARCHIVE_FATAL);
+       }
+       if (entry != entry_main)
+               entry = entry_main;
+       else
+               entry_main = NULL;
+#else
+       entry_main = NULL;
+#endif
        ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);
-       if (ret < ARCHIVE_WARN)
+       if (ret < ARCHIVE_WARN) {
+               if (entry_main)
+                       archive_entry_free(entry_main);
                return (ret);
+       }
        ret2 = __archive_write_output(a, buff, 512);
-       if (ret2 < ARCHIVE_WARN)
+       if (ret2 < ARCHIVE_WARN) {
+               if (entry_main)
+                       archive_entry_free(entry_main);
                return (ret2);
+       }
        if (ret2 < ret)
                ret = ret2;
 
        ustar->entry_bytes_remaining = archive_entry_size(entry);
        ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
+       if (entry_main)
+               archive_entry_free(entry_main);
        return (ret);
 }
 
index 60a8ad82174da0f8dbdf4ca7a734141341a65bf1..45492377a0f0a828f75b43ec9a702e202c40f41e 100644 (file)
@@ -380,7 +380,21 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
                    "Can't allocate zip header data");
                return (ARCHIVE_FATAL);
        }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       /* Make sure the path separators in pahtname, hardlink and symlink
+        * are all slash '/', not the Windows path separator '\'. */
+       l->entry = __la_win_entry_in_posix_pathseparator(entry);
+       if (l->entry == entry)
+               l->entry = archive_entry_clone(entry);
+#else
        l->entry = archive_entry_clone(entry);
+#endif
+       if (l->entry == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate zip header data");
+               free(l);
+               return (ARCHIVE_FATAL);
+       }
        l->flags = zip->flags;
        if (zip->opt_sconv != NULL)
                sconv = zip->opt_sconv;