summaryrefslogtreecommitdiff
path: root/libarchive/archive_read_disk_windows.c
diff options
context:
space:
mode:
authorMartin Matuska <martin@matuska.org>2019-03-27 16:22:41 +0100
committerMartin Matuska <martin@matuska.org>2019-03-27 16:33:16 +0100
commitbc8efdef3e1030976617b8710ff0f71762d078d3 (patch)
tree24c9b7645dbe6e794105ba57118546c877a7e2ba /libarchive/archive_read_disk_windows.c
parent5fe1dadb88c499e8dff71f2e9d9366cb99acda91 (diff)
downloadlibarchive-bc8efdef3e1030976617b8710ff0f71762d078d3.tar.gz
Add support for directory symlinks on Windows
Symlinks with the targets ".", ".." or with an ending slash in the target are treated as directory symlinks on Windows.
Diffstat (limited to 'libarchive/archive_read_disk_windows.c')
-rw-r--r--libarchive/archive_read_disk_windows.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c
index 57774b15..964de749 100644
--- a/libarchive/archive_read_disk_windows.c
+++ b/libarchive/archive_read_disk_windows.c
@@ -299,7 +299,7 @@ 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 **);
+static int la_linkname_from_handle(HANDLE, wchar_t **, int);
static int la_linkname_from_pathw(const wchar_t *, wchar_t **);
static void entry_symlink_from_pathw(struct archive_entry *,
const wchar_t *path);
@@ -337,7 +337,7 @@ typedef struct _REPARSE_DATA_BUFFER {
* outbuf is allocated in the function
*/
static int
-la_linkname_from_handle(HANDLE h, wchar_t **linkname)
+la_linkname_from_handle(HANDLE h, wchar_t **linkname, int isdir)
{
DWORD inbytes;
REPARSE_DATA_BUFFER *buf;
@@ -369,7 +369,8 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname)
return (-1);
}
- tbuf = malloc(len + sizeof(wchar_t));
+ /* We need an extra character here to append directory slash */
+ tbuf = malloc(len + 2 * sizeof(wchar_t));
if (tbuf == NULL) {
free(indata);
return (-1);
@@ -391,6 +392,17 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname)
*tbuf = L'/';
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';
+ }
+ }
return (0);
}
@@ -398,17 +410,24 @@ static int
la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf)
{
HANDLE h;
+ DWORD attrs;
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);
+ ret = la_linkname_from_handle(h, outbuf,
+ attrs & FILE_ATTRIBUTE_DIRECTORY);
CloseHandle(h);
return (ret);
}