diff options
Diffstat (limited to 'libarchive/archive_write_set_format_gnutar.c')
-rw-r--r-- | libarchive/archive_write_set_format_gnutar.c | 155 |
1 files changed, 116 insertions, 39 deletions
diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c index e091ed2..13942c1 100644 --- a/libarchive/archive_write_set_format_gnutar.c +++ b/libarchive/archive_write_set_format_gnutar.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). * Author: Jonas Gastal <jgastal@profusion.mobi> - * Copyright (c) 2011 Michihiro NAKAJIMA + * Copyright (c) 2011-2012 Michihiro NAKAJIMA * * All rights reserved. * @@ -177,7 +177,8 @@ archive_write_set_format_gnutar(struct archive *_a) gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar)); if (gnutar == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate gnutar data"); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate gnutar data"); return (ARCHIVE_FATAL); } a->format_data = gnutar; @@ -213,11 +214,13 @@ archive_write_gnutar_options(struct archive_write *a, const char *key, else ret = ARCHIVE_FATAL; } - } else - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: unknown keyword ``%s''", a->format_name, key); + return (ret); + } - return (ret); + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); } static int @@ -244,8 +247,8 @@ archive_write_gnutar_finish_entry(struct archive_write *a) int ret; gnutar = (struct gnutar *)a->format_data; - ret = __archive_write_nulls(a, - gnutar->entry_bytes_remaining + gnutar->entry_padding); + ret = __archive_write_nulls(a, (size_t) + (gnutar->entry_bytes_remaining + gnutar->entry_padding)); gnutar->entry_bytes_remaining = gnutar->entry_padding = 0; return (ret); } @@ -258,7 +261,7 @@ archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s) gnutar = (struct gnutar *)a->format_data; if (s > gnutar->entry_bytes_remaining) - s = gnutar->entry_bytes_remaining; + s = (size_t)gnutar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); gnutar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) @@ -275,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; @@ -298,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 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 gnutar 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 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", @@ -338,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, @@ -353,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, @@ -370,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, @@ -386,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, @@ -400,7 +470,7 @@ archive_write_gnutar_header(struct archive_write *a, size_t todo = gnutar->linkname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); - /* Uname/gname here don't really matter since noone reads them; + /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); @@ -409,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. */ @@ -429,7 +499,7 @@ archive_write_gnutar_header(struct archive_write *a, size_t todo = gnutar->pathname_length; struct archive_entry *temp = archive_entry_new2(&a->archive); - /* Uname/gname here don't really matter since noone reads them; + /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); @@ -438,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) { @@ -466,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); } |