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_pathw(const wchar_t *, wchar_t **);
+static void entry_symlink_from_pathw(struct archive_entry *,
+ const wchar_t *path);
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+/*
+ * Reads the target of a symbolic link
+ *
+ * Returns 0 on success and -1 on failure
+ * outbuf is allocated in the function
+ */
+static int
+la_linkname_from_handle(HANDLE h, wchar_t **linkname)
+{
+ DWORD inbytes;
+ REPARSE_DATA_BUFFER *buf;
+ size_t len;
+ BOOL ret;
+ BYTE *indata;
+ wchar_t *tbuf;
+
+ indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
+ 1024, &inbytes, NULL);
+ if (ret == 0) {
+ la_dosmaperr(GetLastError());
+ free(indata);
+ return (-1);
+ }
+
+ buf = (REPARSE_DATA_BUFFER *) indata;
+ if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ free(indata);
+ /* File is not a symbolic link */
+ errno = EINVAL;
+ return (-1);
+ }
+
+ len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
+ if (len <= 0) {
+ free(indata);
+ return (-1);
+ }
+
+ tbuf = malloc(len + sizeof(wchar_t));
+ if (tbuf == NULL) {
+ free(indata);
+ return (-1);
+ }
+
+ memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
+ [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
+ free(indata);
+
+ tbuf[len / sizeof(wchar_t)] = L'\0';
+
+ *linkname = tbuf;
+
+ /*
+ * Translate backslashes to slashes for libarchive internal use
+ */
+ while(*tbuf != L'\0') {
+ if (*tbuf == L'\\')
+ *tbuf = L'/';
+ tbuf++;
+ }
+ return (0);
+}
+
+static int
+la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf)
+{
+ HANDLE h;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT;
+ int ret;
+ 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);
+ CloseHandle(h);
+ return (ret);
+}
+
+static void
+entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)
+{
+ wchar_t *linkname = NULL;
+ int ret;
+
+ ret = la_linkname_from_pathw(path, &linkname);
+ if (ret == 0)
+ archive_entry_copy_symlink_w(entry, linkname);
+ free(linkname);
+
+ return;
+}
static struct archive_vtable *
archive_read_disk_vtable(void)
mode |= S_IWUSR | S_IWGRP | S_IWOTH;
if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
findData != NULL &&
- findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK)
+ findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
mode |= S_IFLNK;
- else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ entry_symlink_from_pathw(entry, path);
+ } else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
else {
const wchar_t *p;
fileAttributes = bhfi.dwFileAttributes;
} else {
archive_entry_copy_stat(entry, st);
+ if (st->st_mode & S_IFLNK)
+ entry_symlink_from_pathw(entry, path);
h = INVALID_HANDLE_VALUE;
}
#define MINIMUM_DIR_MODE 0700
#define MAXIMUM_DIR_MODE 0775
+static int disk_unlink(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 *,
return (ret);
}
+/*
+ * Create symolic link
+ *
+ * Always creates a file symbolic link.
+ * Directory symbolic links are currently not implemented.
+ */
+static int
+la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) {
+ static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
+ static int set;
+ wchar_t *ttarget, *p;
+ DWORD flags = 0;
+ BOOL ret = 0;
+
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionKernel32("CreateSymbolicLinkW");
+ }
+ if (!f)
+ return (0);
+
+ /*
+ * When writing path targets, we need to translate slashes
+ * to backslashes
+ */
+ ttarget = malloc((wcslen(target) + 1) * sizeof(wchar_t));
+ if (ttarget == NULL)
+ return(0);
+
+ p = ttarget;
+
+ while(*target != L'\0') {
+ if (*target == L'/')
+ *p = L'\\';
+ else
+ *p = *target;
+ target++;
+ p++;
+ }
+ *p = L'\0';
+
+ /*
+ * Windows won't overwrite existing links
+ */
+ disk_unlink(linkname);
+
+#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+ flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+#else
+ flags |= 0x2;
+#endif
+ ret = (*f)(linkname, ttarget, flags);
+ /*
+ * 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);
+ }
+ free(ttarget);
+ return (ret);
+}
+
static int
la_ftruncate(HANDLE handle, int64_t length)
{
}
static int
-disk_unlink(wchar_t *path)
+disk_unlink(const wchar_t *path)
{
wchar_t *fullname;
int r;
#if HAVE_SYMLINK
return symlink(linkname, a->name) ? errno : 0;
#else
- return (EPERM);
+ r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ r = errno;
+ } else
+ r = 0;
+ return (r);
#endif
}
static int my_GetFileInformationByName(const char *,
BY_HANDLE_FILE_INFORMATION *);
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
static void *
GetFunctionKernel32(const char *name)
{
{
static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
static int set;
+ int ret, tmpflags;
+ char *tgt, *p;
if (!set) {
set = 1;
f = GetFunctionKernel32("CreateSymbolicLinkA");
}
- return f == NULL ? 0 : (*f)(linkname, target, flags);
+ if (f == NULL)
+ return (0);
+
+ tgt = malloc(strlen(target) + 1);
+ if (tgt == NULL)
+ return (0);
+
+ /*
+ * Translate slashes to backslashes
+ */
+ p = tgt;
+ while(*target != '\0') {
+ if (*target == '/')
+ *p = '\\';
+ else
+ *p = *target;
+ target++;
+ p++;
+ }
+ *p = '\0';
+
+ /*
+ * Windows can't overwrite existing links
+ */
+ _unlink(linkname);
+#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+ tmpflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+#else
+ tmpflags = flags | 0x2;
+#endif
+ ret = (*f)(linkname, tgt, tmpflags);
+ /*
+ * Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+ * is not undestood
+ */
+ if (!ret)
+ ret = (*f)(linkname, tgt, flags);
+
+ free(tgt);
+ return (ret);
}
static int
const char *pathname, const char *contents)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
- (void)pathname; /* UNUSED */
- (void)contents; /* UNUSED */
- assertion_count(file, line);
- /* Windows sort-of has real symlinks, but they're only usable
- * by privileged users and are crippled even then, so there's
- * really not much point in bothering with this. */
- return (0);
+ HANDLE h;
+ DWORD inbytes;
+ REPARSE_DATA_BUFFER *buf;
+ size_t len, len2;
+ wchar_t *linknamew, *contentsw;
+ int ret = 0;
+ BYTE *indata;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT;
+
+ if (contents == NULL)
+ return (0);
+
+ h = CreateFileA(pathname, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ flag, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return (0);
+
+ indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
+ 1024, &inbytes, NULL);
+ CloseHandle(h);
+ if (ret == 0) {
+ free(indata);
+ return (0);
+ }
+
+ buf = (REPARSE_DATA_BUFFER *) indata;
+ if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ free(indata);
+ /* File is not a symbolic link */
+ errno = EINVAL;
+ return (0);
+ }
+
+ len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
+
+ linknamew = malloc(len + sizeof(wchar_t));
+ if (linknamew == NULL) {
+ free(indata);
+ return (0);
+ }
+
+ memcpy(linknamew, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
+ [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
+ free(indata);
+
+ linknamew[len / sizeof(wchar_t)] = L'\0';
+
+ contentsw = malloc(len + sizeof(wchar_t));
+ if (contentsw == NULL) {
+ free(linknamew);
+ return (0);
+ }
+
+ len2 = mbsrtowcs(contentsw, &contents, (len + sizeof(wchar_t)
+ / sizeof(wchar_t)), NULL);
+
+ if (len2 > 0 && wcscmp(linknamew, contentsw) != 0)
+ ret = 1;
+
+ free(linknamew);
+ free(contentsw);
+ return (ret);
#else
char buff[300];
struct stat st;