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);
* 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;
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);
*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);
}
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);
}
#define MAXIMUM_DIR_MODE 0775
static int disk_unlink(const wchar_t *);
+static int disk_rmdir(const wchar_t *);
static int check_symlinks(struct archive_write_disk *);
static int create_filesystem_object(struct archive_write_disk *);
static struct fixup_entry *current_fixup(struct archive_write_disk *,
static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
static int set;
wchar_t *ttarget, *p;
+ DWORD attrs = 0;
DWORD flags = 0;
+ DWORD newflags = 0;
BOOL ret = 0;
if (!set) {
*p = L'\0';
/*
- * Windows won't overwrite existing links
+ * If the target equals ".", ".." or ends with a backslash, it always
+ * points to a directory. In this case we can safely set the directory
+ * flag. All other symlinks are created as file symlinks.
*/
- disk_unlink(linkname);
+ if (wcscmp(ttarget, L".") == 0 || wcscmp(ttarget, L"..") == 0 ||
+ *(p - 1) == L'\\') {
+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+#else
+ flags |= 0x1;
+#endif
+ /* Now we remove trailing backslashes, if any */
+ p--;
+ while(*p == L'\\') {
+ *p = L'\0';
+ p--;
+ }
+ }
#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
- flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+ newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
#else
- flags |= 0x2;
+ newflags = flags | 0x2;
#endif
- ret = (*f)(linkname, ttarget, flags);
+
+ /*
+ * Windows won't overwrite existing links
+ */
+ attrs = GetFileAttributesW(linkname);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(linkname);
+ else
+ disk_unlink(linkname);
+ }
+
+ ret = (*f)(linkname, ttarget, newflags);
/*
* Prior to Windows 10 calling CreateSymbolicLinkW() will fail
* if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
*/
- if (!ret && flags != 0) {
- ret = (*f)(linkname, ttarget, 0);
+ if (!ret) {
+ ret = (*f)(linkname, ttarget, flags);
}
free(ttarget);
return (ret);
}
static int
-disk_rmdir(wchar_t *path)
+disk_rmdir(const wchar_t *path)
{
wchar_t *fullname;
int r;
* so we can overwrite it with the
* item being extracted.
*/
- if (disk_unlink(a->name)) {
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r) {
archive_set_error(&a->archive, errno,
"Could not remove symlink %ls",
a->name);
assertMakeDir("h", 0755);
assertChdir("h");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1/");
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
assertMakeSymlink("d1/link1", "file1");
assertMakeDir("l", 0755);
assertChdir("l");
assertMakeDir("d1", 0755);
- assertMakeSymlink("ld1", "d1");
+ assertMakeSymlink("ld1", "d1/");
assertMakeFile("d1/file1", 0644, "d1/file1");
assertMakeFile("d1/file2", 0644, "d1/file2");
assertMakeSymlink("d1/link1", "file1");
assertMakeDir("d1/d2/d3", 0755);
assertMakeDir("d2", 0755);
assertMakeFile("d2/file1", 0644, "d2/file1");
- assertMakeSymlink("d1/d2/ld1", "../../d1");
- assertMakeSymlink("d1/d2/ld2", "../../d2");
+ assertMakeSymlink("d1/d2/ld1", "../../d1/");
+ assertMakeSymlink("d1/d2/ld2", "../../d2/");
assertChdir("..");
assert((ae = archive_entry_new()) != NULL);
my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
{
static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
+ DWORD attrs;
static int set;
int ret, tmpflags;
char *tgt, *p;
*p = '\0';
/*
- * Windows can't overwrite existing links
+ * If the target equals ".", ".." or ends with a slash, it always
+ * points to a directory. In this case we can set the directory flag.
*/
- _unlink(linkname);
+ if (strcmp(tgt, ".") == 0 || strcmp(tgt, "..") == 0 ||
+ *(p - 1) == '\\') {
+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+#else
+ flags |= 0x1;
+#endif
+ /* Now we remove trailing backslashes */
+ p--;
+ while(*p == '\\') {
+ *p = '\0';
+ p--;
+ }
+ }
+
#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
tmpflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
#else
tmpflags = flags | 0x2;
#endif
+ /*
+ * Windows won't overwrite existing links
+ */
+ attrs = GetFileAttributesA(linkname);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ RemoveDirectoryA(linkname);
+ else
+ DeleteFileA(linkname);
+ }
+
ret = (*f)(linkname, tgt, tmpflags);
/*
* Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE