summaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-08-25 05:11:19 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-08-25 05:11:19 +0000
commit2f6fdd3aebdee2ce04d003b206f6da78120e8235 (patch)
treeef40ffc3c0be7def2416de6319e6823a09b56cfd /win32
parentb4b848e6a0ef42df0a4abe55e16cac64fc6395ec (diff)
win32.c: rb_w32_reparse
* win32/win32.c (rb_w32_reparse): read reparse point in a dynamic buffer. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51676 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'win32')
-rw-r--r--win32/file.c26
-rw-r--r--win32/file.h40
-rw-r--r--win32/win32.c119
3 files changed, 116 insertions, 69 deletions
diff --git a/win32/file.c b/win32/file.c
index 7de58d48c9..5b5f899935 100644
--- a/win32/file.c
+++ b/win32/file.c
@@ -9,6 +9,7 @@
#include <winbase.h>
#include <wchar.h>
#include <shlwapi.h>
+#include "win32/file.h"
#ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
@@ -658,15 +659,16 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
return result;
}
-ssize_t rb_w32_wreadlink(const WCHAR *path, WCHAR *buf, size_t bufsize);
-
VALUE
rb_readlink(VALUE path)
{
- ssize_t len;
- WCHAR *wpath, wbuf[MAX_PATH];
+ DWORD len;
+ VALUE wtmp = 0, str;
+ rb_w32_reparse_buffer_t rbuf, *rp = &rbuf;
+ WCHAR *wpath, *wbuf;
rb_encoding *enc;
UINT cp, path_cp;
+ int e;
FilePathValue(path);
enc = rb_enc_get(path);
@@ -678,13 +680,23 @@ rb_readlink(VALUE path)
wpath = mbstr_to_wstr(cp, RSTRING_PTR(path),
RSTRING_LEN(path)+rb_enc_mbminlen(enc), NULL);
if (!wpath) rb_memerror();
- len = rb_w32_wreadlink(wpath, wbuf, numberof(wbuf));
+ e = rb_w32_read_reparse_point(wpath, rp, sizeof(rbuf), &wbuf, &len);
+ if (e == ERROR_MORE_DATA) {
+ size_t size = rb_w32_reparse_buffer_size(len + 1);
+ rp = ALLOCV(wtmp, size);
+ e = rb_w32_read_reparse_point(wpath, rp, size, &wbuf, &len);
+ }
free(wpath);
- if (len < 0) rb_sys_fail_path(path);
+ if (e) {
+ ALLOCV_END(wtmp);
+ rb_syserr_fail_path(rb_w32_map_errno(e), path);
+ }
enc = rb_filesystem_encoding();
cp = path_cp = code_page(enc);
if (cp == INVALID_CODE_PAGE) cp = CP_UTF8;
- return append_wstr(rb_enc_str_new(0, 0, enc), wbuf, len, cp, path_cp, enc);
+ str = append_wstr(rb_enc_str_new(0, 0, enc), wbuf, len, cp, path_cp, enc);
+ ALLOCV_END(wtmp);
+ return str;
}
static void *
diff --git a/win32/file.h b/win32/file.h
new file mode 100644
index 0000000000..95a792a63f
--- /dev/null
+++ b/win32/file.h
@@ -0,0 +1,40 @@
+#ifndef RUBY_WIN32_FILE_H
+#define RUBY_WIN32_FILE_H
+
+#define MAX_REPARSE_PATH_LEN 4092
+
+enum {
+ MINIMUM_REPARSE_BUFFER_PATH_LEN = 4
+};
+/* License: Ruby's */
+typedef struct {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[4];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[4];
+ } MountPointReparseBuffer;
+ };
+} rb_w32_reparse_buffer_t;
+
+#define rb_w32_reparse_buffer_size(n) \
+ (sizeof(rb_w32_reparse_buffer_t) + \
+ sizeof(WCHAR)*((n)-MINIMUM_REPARSE_BUFFER_PATH_LEN))
+
+int rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
+ size_t bufsize, WCHAR **result, DWORD *len);
+
+#endif /* RUBY_WIN32_FILE_H */
diff --git a/win32/win32.c b/win32/win32.c
index beea93871e..9b838b516e 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -50,6 +50,7 @@
#endif
#include "ruby/win32.h"
#include "win32/dir.h"
+#include "win32/file.h"
#include "internal.h"
#define isdirsep(x) ((x) == '/' || (x) == '\\')
@@ -4668,32 +4669,8 @@ link(const char *from, const char *to)
#endif
/* License: Ruby's */
-typedef struct {
- ULONG ReparseTag;
- USHORT ReparseDataLength;
- USHORT Reserved;
- union {
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- ULONG Flags;
- WCHAR PathBuffer[MAXPATHLEN * 2];
- } SymbolicLinkReparseBuffer;
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- WCHAR PathBuffer[MAXPATHLEN * 2];
- } MountPointReparseBuffer;
- };
-} reparse_buffer_t;
-
-/* License: Ruby's */
static int
-reparse_symlink(const WCHAR *path, reparse_buffer_t *rp)
+reparse_symlink(const WCHAR *path, rb_w32_reparse_buffer_t *rp, size_t size)
{
HANDLE f;
DWORD ret;
@@ -4721,12 +4698,12 @@ reparse_symlink(const WCHAR *path, reparse_buffer_t *rp)
}
if (!device_io_control(f, FSCTL_GET_REPARSE_POINT, NULL, 0,
- rp, sizeof(*rp), &ret, NULL)) {
- e = map_errno(GetLastError());
+ rp, size, &ret, NULL)) {
+ e = GetLastError();
}
else if (rp->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
- e = EINVAL;
+ e = ERROR_INVALID_PARAMETER;
}
CloseHandle(f);
return e;
@@ -4736,68 +4713,86 @@ reparse_symlink(const WCHAR *path, reparse_buffer_t *rp)
int
rb_w32_reparse_symlink_p(const WCHAR *path)
{
- reparse_buffer_t rp;
- return reparse_symlink(path, &rp) == 0;
+ rb_w32_reparse_buffer_t rp;
+ switch (reparse_symlink(path, &rp, sizeof(rp))) {
+ case 0:
+ case ERROR_MORE_DATA:
+ return TRUE;
+ }
+ return FALSE;
}
/* License: Ruby's */
-ssize_t
-rb_w32_wreadlink(const WCHAR *path, WCHAR *buf, size_t bufsize)
+int
+rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp,
+ size_t bufsize, WCHAR **result, DWORD *len)
{
- reparse_buffer_t rp;
- int e = reparse_symlink(path, &rp);
+ int e = reparse_symlink(path, rp, bufsize);
DWORD ret;
- if (!e) {
+ if (!e || e == ERROR_MORE_DATA) {
void *name;
- if (rp.ReparseTag == IO_REPARSE_TAG_SYMLINK) {
- name = ((char *)rp.SymbolicLinkReparseBuffer.PathBuffer +
- rp.SymbolicLinkReparseBuffer.PrintNameOffset);
- ret = rp.SymbolicLinkReparseBuffer.PrintNameLength;
+ if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ name = ((char *)rp->SymbolicLinkReparseBuffer.PathBuffer +
+ rp->SymbolicLinkReparseBuffer.PrintNameOffset);
+ ret = rp->SymbolicLinkReparseBuffer.PrintNameLength;
}
else { /* IO_REPARSE_TAG_MOUNT_POINT */
/* +4/-4 means to drop "\??\" */
- name = ((char *)rp.MountPointReparseBuffer.PathBuffer +
- rp.MountPointReparseBuffer.SubstituteNameOffset +
+ name = ((char *)rp->MountPointReparseBuffer.PathBuffer +
+ rp->MountPointReparseBuffer.SubstituteNameOffset +
4 * sizeof(WCHAR));
- ret = rp.MountPointReparseBuffer.SubstituteNameLength -
+ ret = rp->MountPointReparseBuffer.SubstituteNameLength -
4 * sizeof(WCHAR);
}
+ *result = name;
+ *len = ret / sizeof(WCHAR);
+ if (e) {
+ if ((char *)name + ret + sizeof(WCHAR) > (char *)rp + bufsize)
+ return e;
+ /* SubstituteName is not used */
+ }
((WCHAR *)name)[ret/sizeof(WCHAR)] = L'\0';
translate_wchar(name, L'\\', L'/');
- bufsize *= sizeof(WCHAR);
- memcpy(buf, name, ret > bufsize ? bufsize : ret);
+ return 0;
}
else {
- errno = e;
- return -1;
+ return e;
}
- 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];
+ WCHAR *wpath, *wname;
+ VALUE wtmp;
+ size_t size = rb_w32_reparse_buffer_size(bufsize);
+ rb_w32_reparse_buffer_t *rp = ALLOCV(wtmp, size);
+ DWORD len;
ssize_t ret;
+ int e;
wpath = mbstr_to_wstr(cp, path, -1, NULL);
- if (!wpath) return -1;
- ret = rb_w32_wreadlink(wpath, wbuf, MAXPATHLEN);
+ if (!wpath) {
+ ALLOCV_END(wtmp);
+ return -1;
+ }
+ e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len);
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;
- }
+ if (e && e != ERROR_MORE_DATA) {
+ ALLOCV_END(wtmp);
+ errno = map_errno(e);
+ return -1;
+ }
+ ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL);
+ if (e) {
+ ret = bufsize;
+ }
+ else if (!ret) {
+ e = GetLastError();
+ errno = map_errno(e);
+ ret = -1;
}
return ret;
}