diff options
Diffstat (limited to 'libarchive')
-rw-r--r-- | libarchive/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libarchive/archive_entry.c | 17 | ||||
-rw-r--r-- | libarchive/archive_entry.h | 10 | ||||
-rw-r--r-- | libarchive/archive_entry_misc.3 | 62 | ||||
-rw-r--r-- | libarchive/archive_entry_private.h | 3 | ||||
-rw-r--r-- | libarchive/archive_read_disk_windows.c | 59 | ||||
-rw-r--r-- | libarchive/archive_read_support_format_tar.c | 9 | ||||
-rw-r--r-- | libarchive/archive_write_disk_windows.c | 24 | ||||
-rw-r--r-- | libarchive/archive_write_set_format_pax.c | 15 | ||||
-rw-r--r-- | libarchive/test/test_write_disk_symlink.c | 27 |
10 files changed, 188 insertions, 39 deletions
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 2bc69593..ec775bb4 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -170,6 +170,7 @@ SET(libarchive_MANS archive_entry.3 archive_entry_acl.3 archive_entry_linkify.3 + archive_entry_misc.3 archive_entry_paths.3 archive_entry_perms.3 archive_entry_stat.3 diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 34cd5811..d9d54b87 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -168,6 +168,7 @@ archive_entry_clear(struct archive_entry *entry) archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; memset(entry, 0, sizeof(*entry)); return entry; } @@ -202,6 +203,9 @@ archive_entry_clone(struct archive_entry *entry) entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); + /* Copy symlink type */ + entry2->ae_symlink_type = entry->ae_symlink_type; + /* Copy encryption status */ entry2->encryption = entry->encryption; @@ -253,6 +257,7 @@ archive_entry_new2(struct archive *a) if (entry == NULL) return (NULL); entry->archive = a; + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; return (entry); } @@ -675,6 +680,12 @@ archive_entry_symlink(struct archive_entry *entry) return (NULL); } +int +archive_entry_symlink_type(struct archive_entry *entry) +{ + return (entry->ae_symlink_type); +} + const char * archive_entry_symlink_utf8(struct archive_entry *entry) { @@ -1246,6 +1257,12 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) } void +archive_entry_set_symlink_type(struct archive_entry *entry, int type) +{ + entry->ae_symlink_type = type; +} + +void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&entry->ae_symlink, linkname); diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 47653f37..f8a7e532 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -191,6 +191,13 @@ struct archive_entry; #define AE_IFIFO ((__LA_MODE_T)0010000) /* + * Symlink types + */ +#define AE_SYMLINK_TYPE_UNDEFINED 0 +#define AE_SYMLINK_TYPE_FILE 1 +#define AE_SYMLINK_TYPE_DIRECTORY 2 + +/* * Basic object manipulation */ @@ -275,6 +282,7 @@ __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); +__LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); @@ -350,6 +358,7 @@ __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); @@ -692,7 +701,6 @@ __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); - #ifdef __cplusplus } #endif diff --git a/libarchive/archive_entry_misc.3 b/libarchive/archive_entry_misc.3 new file mode 100644 index 00000000..9b1e3ea2 --- /dev/null +++ b/libarchive/archive_entry_misc.3 @@ -0,0 +1,62 @@ +.\" Copyright (c) 2019 Martin Matuska +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 15, 2019 +.Dt ARCHIVE_ENTRY_MISC 3 +.Os +.Sh NAME +.Nm archive_entry_symlink_type , +.Nm archive_entry_set_symlink_type +.Nd miscellaneous functions for manipulating properties of archive_entry. +.Sh LIBRARY +Streaming Archive Library (libarchive, -larchive) +.Sh SYNOPSIS +.In archive_entry.h +.Ft int +.Fn archive_entry_symlink_type "struct archive_entry *a" +.Ft void +.Fn archive_entry_set_symlink_type "struct archive_entry *a" "int" +.Sh DESCRIPTION +The function +.Fn archive_entry_symlink_type +returns and the function +.Fn archive_entry_set_symlink_type +sets the type of the symbolic link stored in an archive entry. These functions +have special meaning on operating systems that support multiple symbolic link +types (e.g. Microsoft Windows). +.Pp +Supported values are: +.Bl -tag -width "AE_SYMLINK_TYPE_DIRECTORY" -compact +.It AE_SYMLINK_TYPE_UNDEFINED +Symbolic link target type is not defined (default on unix systems) +.It AE_SYMLINK_TYPE_FILE +Symbolic link points to a file +.It AE_SYMLINK_TYPE_DIRECTORY +Symbolic link points to a directory +.El +.Sh SEE ALSO +.Xr archive_entry 3 , +.Xr archive_entry_paths 3 , +.Xr archive_entry_stat 3 , +.Xr libarchive 3 diff --git a/libarchive/archive_entry_private.h b/libarchive/archive_entry_private.h index c69233e6..3d569bbf 100644 --- a/libarchive/archive_entry_private.h +++ b/libarchive/archive_entry_private.h @@ -176,6 +176,9 @@ struct archive_entry { /* Miscellaneous. */ char strmode[12]; + + /* Symlink type support */ + int ae_symlink_type; }; #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 964de749..ff79cc05 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -299,8 +299,8 @@ static int close_and_restore_time(HANDLE, struct tree *, struct restore_time *); static int setup_sparse_from_disk(struct archive_read_disk *, struct archive_entry *, HANDLE); -static int la_linkname_from_handle(HANDLE, wchar_t **, int); -static int la_linkname_from_pathw(const wchar_t *, wchar_t **); +static int la_linkname_from_handle(HANDLE, wchar_t **, int *); +static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *); static void entry_symlink_from_pathw(struct archive_entry *, const wchar_t *path); @@ -337,15 +337,22 @@ typedef struct _REPARSE_DATA_BUFFER { * outbuf is allocated in the function */ static int -la_linkname_from_handle(HANDLE h, wchar_t **linkname, int isdir) +la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype) { DWORD inbytes; REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; size_t len; BOOL ret; BYTE *indata; wchar_t *tbuf; + ret = GetFileInformationByHandle(h, &st); + if (ret == 0 || + (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + return (-1); + } + indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, 1024, &inbytes, NULL); @@ -369,8 +376,7 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname, int isdir) return (-1); } - /* We need an extra character here to append directory slash */ - tbuf = malloc(len + 2 * sizeof(wchar_t)); + tbuf = malloc(len + 1 * sizeof(wchar_t)); if (tbuf == NULL) { free(indata); return (-1); @@ -393,42 +399,35 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname, int isdir) tbuf++; } - /* - * Directory symlinks need special treatment - */ - if (isdir && wcscmp(*linkname, L".") != 0 && - wcscmp(*linkname, L"..") != 0) { - if (*(tbuf - 1) != L'/') { - *tbuf = L'/'; - *(tbuf + 1) = L'\0'; - } - } + if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + *linktype = AE_SYMLINK_TYPE_FILE; + else + *linktype = AE_SYMLINK_TYPE_DIRECTORY; + return (0); } +/* + * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error + */ static int -la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf) +la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype) { HANDLE h; - DWORD attrs; - DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT; int ret; - attrs = GetFileAttributesW(path); - if (attrs == INVALID_FILE_ATTRIBUTES || - (!(attrs & FILE_ATTRIBUTE_REPARSE_POINT))) - return (-1); - h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); return (-1); } - ret = la_linkname_from_handle(h, outbuf, - attrs & FILE_ATTRIBUTE_DIRECTORY); + + ret = la_linkname_from_handle(h, outbuf, linktype); CloseHandle(h); + return (ret); } @@ -436,11 +435,15 @@ static void entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path) { wchar_t *linkname = NULL; - int ret; + int ret, linktype; - ret = la_linkname_from_pathw(path, &linkname); - if (ret == 0) + ret = la_linkname_from_pathw(path, &linkname, &linktype); + if (ret != 0) + return; + if (linktype >= 0) { archive_entry_copy_symlink_w(entry, linkname); + archive_entry_set_symlink_type(entry, linktype); + } free(linkname); return; diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 60800bb8..81c03c88 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -1942,6 +1942,15 @@ pax_attribute(struct archive_read *a, struct tar *tar, pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } + if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) { + if (strcmp(value, "file") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_FILE); + } else if (strcmp(value, "dir") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_DIRECTORY); + } + } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index b29389c1..e3e65ba1 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -587,13 +587,14 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) } /* - * Create symolic link + * Create file or directory symolic link * - * Always creates a file symbolic link. - * Directory symbolic links are currently not implemented. + * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from + * the link target */ static int -la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) { +la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, + int linktype) { static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); static int set; wchar_t *ttarget, *p; @@ -636,14 +637,16 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) { *p = L'\0'; /* + * In case of undefined symlink type we guess it from the target. * If the target equals ".", "..", ends with a backslash or a - * backslash followed by "." or ".." it always points to a directory. - * In this case we can safely set the directory flag. - * All other symlinks are created as file symlinks. + * backslash followed by "." or ".." we assume it is a directory + * symlink. In all other cases we assume a file symlink. */ - if (*(p - 1) == L'\\' || (*(p - 1) == L'.' && ( + if (linktype != AE_SYMLINK_TYPE_FILE && ( + linktype == AE_SYMLINK_TYPE_DIRECTORY || + *(p - 1) == L'\\' || (*(p - 1) == L'.' && ( len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && ( - len == 2 || *(p - 3) == L'\\'))))) { + len == 2 || *(p - 3) == L'\\')))))) { #if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; #else @@ -1638,7 +1641,8 @@ create_filesystem_object(struct archive_write_disk *a) return symlink(linkname, a->name) ? errno : 0; #else errno = 0; - r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname); + r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname, + archive_entry_symlink_type(a->entry)); if (r == 0) { if (errno == 0) la_dosmaperr(GetLastError()); diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index 5a4c45a1..cf2a1f95 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -1114,6 +1114,10 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && acl_types != 0) need_extension = 1; + /* If the symlink type is defined, we need an extension */ + if (!need_extension && archive_entry_symlink_type(entry_main) > 0) + need_extension = 1; + /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who @@ -1247,6 +1251,17 @@ archive_write_pax_header(struct archive_write *a, archive_string_free(&entry_name); return (ARCHIVE_FATAL); } + + /* Store extended symlink information */ + if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_FILE) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "file"); + } else if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_DIRECTORY) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "dir"); + } } /* Only regular files have data. */ diff --git a/libarchive/test/test_write_disk_symlink.c b/libarchive/test/test_write_disk_symlink.c index 43796a45..aeadfee8 100644 --- a/libarchive/test/test_write_disk_symlink.c +++ b/libarchive/test/test_write_disk_symlink.c @@ -208,6 +208,29 @@ DEFINE_TEST(test_write_disk_symlink) assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); + /* Symbolic link: d1dir -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1dir"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1file -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1file"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_FILE); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); @@ -239,4 +262,8 @@ DEFINE_TEST(test_write_disk_symlink) assertIsSymlink("d1slash", "d1/", 1); assertIsSymlink("d1sldot", "d1/.", 1); assertIsSymlink("d1slddot", "d1/..", 1); + + /* Test #5: symlink_type is set */ + assertIsSymlink("d1dir", "d1", 1); + assertIsSymlink("d1file", "d1", 0); } |