summaryrefslogtreecommitdiff
path: root/libarchive
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive')
-rw-r--r--libarchive/CMakeLists.txt1
-rw-r--r--libarchive/archive_entry.c17
-rw-r--r--libarchive/archive_entry.h10
-rw-r--r--libarchive/archive_entry_misc.362
-rw-r--r--libarchive/archive_entry_private.h3
-rw-r--r--libarchive/archive_read_disk_windows.c59
-rw-r--r--libarchive/archive_read_support_format_tar.c9
-rw-r--r--libarchive/archive_write_disk_windows.c24
-rw-r--r--libarchive/archive_write_set_format_pax.c15
-rw-r--r--libarchive/test/test_write_disk_symlink.c27
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);
}