]> 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:42:48 +0000 (17:42 +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 4d37a9c25c4399be2f9884d01d31d0804a11732c..18c26c7a4a717338b09301b18aeff34cd721b96c 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>
@@ -639,6 +640,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 cfb3e97d2811983d6e5e00793b9baa3ab7b0f5fe..523f8553d56eeee618f563f3c03e1097c84667b5 100644 (file)
@@ -261,6 +261,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 *);
 
 
 #endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */
index 5d64cdcf0cb370340b0324f4c74b8f36f5986c14..3ab4f2f4b9e48077949869ee323246579d5017de 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 d7c2899ca9660e572c6abbf5360a42ed3bb6465b..63ae522373ea1503899658f7ec4e5fc2dd60bbb4 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 dc5b744886d38fae27407f117cf03051762020bf..5fac49c4705216ed120813360479af4bee832897 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 742b9ebdde168eff4ebff9d5822816f1e334cf8c..2cd31e7aaf0db74f6d993f88173f06784e3ce55f 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 3117dd3642a9434aba153448a676984f27a0a2a5..fe8c96ace0766cbe2a594759ea7e88fcef570468 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 0e3d7bb317d28be6f7e82fdfc792bfd04ffcd82b..3801ce1fa5ac026457b08c2f3aafbb261e1fca2e 100644 (file)
@@ -385,7 +385,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;