From c57932f782ba03b3aab667a877c5b6143b214d26 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 23 Mar 2015 08:36:04 +0000 Subject: win32.c: readlink * win32/win32.c (wreadlink, rb_w32_ureadlink): implement readlink(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50062 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 4 ++ configure.in | 1 + include/ruby/win32.h | 3 ++ win32/Makefile.sub | 1 + win32/win32.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+) diff --git a/ChangeLog b/ChangeLog index b18f1e5f9a..d889505489 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Mon Mar 23 17:36:00 2015 Nobuyoshi Nakada + + * win32/win32.c (wreadlink, rb_w32_ureadlink): implement readlink(). + Mon Mar 23 14:40:45 2015 Nobuyoshi Nakada * win32/win32.c (winnt_stat): stat with following symbolic links. diff --git a/configure.in b/configure.in index d9b4f72e7b..db37cd6644 100644 --- a/configure.in +++ b/configure.in @@ -1104,6 +1104,7 @@ main() ac_cv_func_isnan=yes ac_cv_func_finite=yes ac_cv_func_link=yes + ac_cv_func_readlink=yes ac_cv_lib_crypt_crypt=no ac_cv_func_getpgrp_void=no ac_cv_func_memcmp_working=yes diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 88416e9e08..77ea0944c4 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -316,6 +316,9 @@ extern int chown(const char *, int, int); extern int rb_w32_uchown(const char *, int, int); extern int link(const char *, const char *); extern int rb_w32_ulink(const char *, const char *); +extern ssize_t readlink(const char *, char *, size_t); +extern ssize_t rb_w32_ureadlink(const char *, char *, size_t); +extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t); extern int gettimeofday(struct timeval *, struct timezone *); extern int clock_gettime(clockid_t, struct timespec *); extern int clock_getres(clockid_t, struct timespec *); diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 259193e0b2..5b4ebade95 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -712,6 +712,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define HAVE_TIMES 1 #define HAVE_FCNTL 1 #define HAVE_LINK 1 +#define HAVE_READLINK 1 #define HAVE__SETJMP 1 #define HAVE_TELLDIR 1 #define HAVE_SEEKDIR 1 diff --git a/win32/win32.c b/win32/win32.c index 73fd477edf..b3adc11fe3 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -4678,6 +4678,116 @@ link(const char *from, const char *to) return ret; } +/* License: Ruby's */ +ssize_t +rb_w32_wreadlink(const WCHAR *path, WCHAR *buf, size_t bufsize) +{ + struct { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[MAXPATHLEN * 2]; + } SymbolicLinkReparseBuffer; + } rp; + HANDLE f; + DWORD ret; + int e = 0; + + typedef BOOL (WINAPI *device_io_control_func)(HANDLE, DWORD, LPVOID, + DWORD, LPVOID, DWORD, + LPDWORD, LPOVERLAPPED); + static device_io_control_func device_io_control = (device_io_control_func)-1; + + if (device_io_control == (device_io_control_func)-1) { + device_io_control = (device_io_control_func) + get_proc_address("kernel32", "DeviceIoControl", NULL); + } + if (!device_io_control) { + errno = ENOSYS; + return -1; + } + + f = CreateFileW(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + if (f == INVALID_HANDLE_VALUE) { + errno = map_errno(GetLastError()); + return -1; + } + + if (!device_io_control(f, FSCTL_GET_REPARSE_POINT, NULL, 0, + &rp, sizeof(rp), &ret, NULL)) { + e = map_errno(GetLastError()); + } + else if (rp.ReparseTag != IO_REPARSE_TAG_SYMLINK){ + e = EINVAL; + } + else { + void *name = ((char *)rp.SymbolicLinkReparseBuffer.PathBuffer + + rp.SymbolicLinkReparseBuffer.PrintNameOffset); + ret = rp.SymbolicLinkReparseBuffer.PrintNameLength; + ((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0'; + translate_wchar(name, L'\\', L'/'); + bufsize *= sizeof(WCHAR); + memcpy(buf, name, ret > bufsize ? bufsize : ret); + } + + CloseHandle(f); + if (e) { + errno = e; + return -1; + } + return ret / sizeof(WCHAR); +} + +/* License: Ruby's */ +static ssize_t +w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize) +{ + WCHAR *wpath; + WCHAR wbuf[MAXPATHLEN]; + ssize_t ret; + + wpath = mbstr_to_wstr(cp, path, -1, NULL); + if (!wpath) return -1; + ret = rb_w32_wreadlink(wpath, wbuf, MAXPATHLEN); + free(wpath); + if (ret < 0) return ret; + ret = WideCharToMultiByte(cp, 0, wbuf, ret, buf, bufsize, NULL, NULL); + if (!ret) { + int e = GetLastError(); + if (e == ERROR_INSUFFICIENT_BUFFER) { + ret = bufsize; + } + else { + errno = map_errno(e); + ret = -1; + } + } + return ret; +} + +/* License: Ruby's */ +ssize_t +rb_w32_ureadlink(const char *path, char *buf, size_t bufsize) +{ + return w32_readlink(CP_UTF8, path, buf, bufsize); +} + +/* License: Ruby's */ +ssize_t +readlink(const char *path, char *buf, size_t bufsize) +{ + return w32_readlink(filecp(), path, buf, bufsize); +} + /* License: Ruby's */ int wait(int *status) -- cgit v1.2.3