Win32: implement readlink()

Implement readlink() by reading NTFS reparse points. Works for symlinks
and directory junctions. If symlinks are disabled, fail with ENOSYS.

Signed-off-by: Karsten Blees <blees@dcon.de>
This commit is contained in:
Karsten Blees
2015-05-24 01:24:41 +02:00
committed by Johannes Schindelin
parent ac5610f37d
commit 23d87b3e38
2 changed files with 99 additions and 2 deletions

View File

@@ -2,6 +2,7 @@
#include "win32.h"
#include <conio.h>
#include <wchar.h>
#include <winioctl.h>
#include "../strbuf.h"
#include "../run-command.h"
#include "../cache.h"
@@ -2202,6 +2203,103 @@ int link(const char *oldpath, const char *newpath)
return 0;
}
#ifndef _WINNT_H
/*
* The REPARSE_DATA_BUFFER structure is defined in the Windows DDK (in
* ntifs.h) and in MSYS1's winnt.h (which defines _WINNT_H). So define
* it ourselves if we are on MSYS2 (whose winnt.h defines _WINNT_).
*/
typedef struct _REPARSE_DATA_BUFFER {
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
#ifndef _MSC_VER
_ANONYMOUS_UNION
#endif
union {
struct {
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
BYTE DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
int readlink(const char *path, char *buf, size_t bufsiz)
{
HANDLE handle;
WCHAR wpath[MAX_LONG_PATH], *wbuf;
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
DWORD dummy;
char tmpbuf[MAX_LONG_PATH];
int len;
if (xutftowcs_long_path(wpath, path) < 0)
return -1;
/* read reparse point data */
handle = CreateFileW(wpath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE) {
errno = err_win_to_posix(GetLastError());
return -1;
}
if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, b,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dummy, NULL)) {
errno = err_win_to_posix(GetLastError());
CloseHandle(handle);
return -1;
}
CloseHandle(handle);
/* get target path for symlinks or mount points (aka 'junctions') */
switch (b->ReparseTag) {
case IO_REPARSE_TAG_SYMLINK:
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
*(WCHAR*) (((char*) wbuf)
+ b->SymbolicLinkReparseBuffer.SubstituteNameLength) = 0;
break;
case IO_REPARSE_TAG_MOUNT_POINT:
wbuf = (WCHAR*) (((char*) b->MountPointReparseBuffer.PathBuffer)
+ b->MountPointReparseBuffer.SubstituteNameOffset);
*(WCHAR*) (((char*) wbuf)
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
break;
default:
errno = EINVAL;
return -1;
}
/*
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
* condition. There is no conversion function that produces invalid UTF-8,
* so convert to a (hopefully large enough) temporary buffer, then memcpy
* the requested number of bytes (including '\0' for robustness).
*/
if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
return -1;
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
return min(bufsiz, len);
}
pid_t waitpid(pid_t pid, int *status, int options)
{
HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,

View File

@@ -123,8 +123,6 @@ struct utsname {
* trivial stubs
*/
static inline int readlink(const char *path, char *buf, size_t bufsiz)
{ errno = ENOSYS; return -1; }
static inline int symlink(const char *oldpath, const char *newpath)
{ errno = ENOSYS; return -1; }
static inline int fchmod(int fildes, mode_t mode)
@@ -218,6 +216,7 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out);
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
int link(const char *oldpath, const char *newpath);
int uname(struct utsname *buf);
int readlink(const char *path, char *buf, size_t bufsiz);
/*
* replacements of existing functions