diff options
author | Martin Matuska <martin@matuska.org> | 2019-03-27 16:22:41 +0100 |
---|---|---|
committer | Martin Matuska <martin@matuska.org> | 2019-03-27 16:33:16 +0100 |
commit | bc8efdef3e1030976617b8710ff0f71762d078d3 (patch) | |
tree | 24c9b7645dbe6e794105ba57118546c877a7e2ba /libarchive/archive_read_disk_windows.c | |
parent | 5fe1dadb88c499e8dff71f2e9d9366cb99acda91 (diff) | |
download | libarchive-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.c | 27 |
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); } |