summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Modules')
-rw-r--r--Modules/posixmodule.c237
1 files changed, 142 insertions, 95 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 2d26a0e..14d18bc 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -509,14 +509,11 @@ typedef struct _REPARSE_DATA_BUFFER {
#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
static int
-win32_read_link(HANDLE reparse_point_handle, ULONG *reparse_tag, wchar_t **target_path)
+win32_get_reparse_tag(HANDLE reparse_point_handle, ULONG *reparse_tag)
{
char target_buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)target_buffer;
DWORD n_bytes_returned;
- const wchar_t *ptr;
- wchar_t *buf;
- size_t len;
if (0 == DeviceIoControl(
reparse_point_handle,
@@ -525,41 +522,12 @@ win32_read_link(HANDLE reparse_point_handle, ULONG *reparse_tag, wchar_t **targe
target_buffer, sizeof(target_buffer),
&n_bytes_returned,
NULL)) /* we're not using OVERLAPPED_IO */
- return -1;
+ return FALSE;
if (reparse_tag)
*reparse_tag = rdb->ReparseTag;
- if (target_path) {
- switch (rdb->ReparseTag) {
- case IO_REPARSE_TAG_SYMLINK:
- /* XXX: Maybe should use SubstituteName? */
- ptr = rdb->SymbolicLinkReparseBuffer.PathBuffer +
- rdb->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
- len = rdb->SymbolicLinkReparseBuffer.PrintNameLength/sizeof(WCHAR);
- break;
- case IO_REPARSE_TAG_MOUNT_POINT:
- ptr = rdb->MountPointReparseBuffer.PathBuffer +
- rdb->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
- len = rdb->MountPointReparseBuffer.SubstituteNameLength/sizeof(WCHAR);
- break;
- default:
- SetLastError(ERROR_REPARSE_TAG_MISMATCH); /* XXX: Proper error code? */
- return -1;
- }
- buf = (wchar_t *)malloc(sizeof(wchar_t)*(len+1));
- if (!buf) {
- SetLastError(ERROR_OUTOFMEMORY);
- return -1;
- }
- wcsncpy(buf, ptr, len);
- buf[len] = L'\0';
- if (wcsncmp(buf, L"\\??\\", 4) == 0)
- buf[1] = L'\\';
- *target_path = buf;
- }
-
- return 0;
+ return TRUE;
}
#endif /* MS_WINDOWS */
@@ -1125,36 +1093,97 @@ attributes_from_dir_w(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *
return TRUE;
}
-#ifndef SYMLOOP_MAX
-#define SYMLOOP_MAX ( 88 )
-#endif
-
+/* Grab GetFinalPathNameByHandle dynamically from kernel32 */
+static int has_GetFinalPathNameByHandle = 0;
+static DWORD (CALLBACK *Py_GetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD,
+ DWORD);
+static DWORD (CALLBACK *Py_GetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD,
+ DWORD);
static int
-win32_xstat_impl_w(const wchar_t *path, struct win32_stat *result, BOOL traverse, int depth);
+check_GetFinalPathNameByHandle()
+{
+ HINSTANCE hKernel32;
+ /* only recheck */
+ if (!has_GetFinalPathNameByHandle)
+ {
+ hKernel32 = GetModuleHandle("KERNEL32");
+ *(FARPROC*)&Py_GetFinalPathNameByHandleA = GetProcAddress(hKernel32,
+ "GetFinalPathNameByHandleA");
+ *(FARPROC*)&Py_GetFinalPathNameByHandleW = GetProcAddress(hKernel32,
+ "GetFinalPathNameByHandleW");
+ has_GetFinalPathNameByHandle = Py_GetFinalPathNameByHandleA &&
+ Py_GetFinalPathNameByHandleW;
+ }
+ return has_GetFinalPathNameByHandle;
+}
+
+static BOOL
+get_target_path(HANDLE hdl, wchar_t **target_path)
+{
+ int buf_size, result_length;
+ wchar_t *buf;
+
+ /* We have a good handle to the target, use it to determine
+ the target path name (then we'll call lstat on it). */
+ buf_size = Py_GetFinalPathNameByHandleW(hdl, 0, 0,
+ VOLUME_NAME_DOS);
+ if(!buf_size)
+ return FALSE;
+
+ buf = (wchar_t *)malloc((buf_size+1)*sizeof(wchar_t));
+ result_length = Py_GetFinalPathNameByHandleW(hdl,
+ buf, buf_size, VOLUME_NAME_DOS);
+
+ if(!result_length) {
+ free(buf);
+ return FALSE;
+ }
+
+ if(!CloseHandle(hdl)) {
+ free(buf);
+ return FALSE;
+ }
+
+ buf[result_length] = 0;
+
+ *target_path = buf;
+ return TRUE;
+}
static int
-win32_xstat_impl(const char *path, struct win32_stat *result, BOOL traverse, int depth)
+win32_xstat_impl_w(const wchar_t *path, struct win32_stat *result,
+ BOOL traverse);
+static int
+win32_xstat_impl(const char *path, struct win32_stat *result,
+ BOOL traverse)
{
- int code;
- HANDLE hFile;
+ int code;
+ HANDLE hFile, hFile2;
BY_HANDLE_FILE_INFORMATION info;
ULONG reparse_tag = 0;
- wchar_t *target_path;
+ wchar_t *target_path;
const char *dot;
- if (depth > SYMLOOP_MAX) {
- SetLastError(ERROR_CANT_RESOLVE_FILENAME); /* XXX: ELOOP? */
+ if(!check_GetFinalPathNameByHandle()) {
+ /* If the OS doesn't have GetFinalPathNameByHandle, return a
+ NotImplementedError. */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "GetFinalPathNameByHandle not available on this platform");
return -1;
}
hFile = CreateFileA(
path,
- 0, /* desired access */
+ FILE_READ_ATTRIBUTES, /* desired access */
0, /* share mode */
NULL, /* security attributes */
OPEN_EXISTING,
/* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
- FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT,
+ /* FILE_FLAG_OPEN_REPARSE_POINT does not follow the symlink.
+ Because of this, calls like GetFinalPathNameByHandle will return
+ the symlink path agin and not the actual final path. */
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|
+ FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -1178,15 +1207,32 @@ win32_xstat_impl(const char *path, struct win32_stat *result, BOOL traverse, int
} else {
if (!GetFileInformationByHandle(hFile, &info)) {
CloseHandle(hFile);
- return -1;;
+ return -1;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- code = win32_read_link(hFile, &reparse_tag, traverse ? &target_path : NULL);
- CloseHandle(hFile);
- if (code < 0)
- return code;
+ if (!win32_get_reparse_tag(hFile, &reparse_tag))
+ return -1;
+
+ /* Close the outer open file handle now that we're about to
+ reopen it with different flags. */
+ if (!CloseHandle(hFile))
+ return -1;
+
if (traverse) {
- code = win32_xstat_impl_w(target_path, result, traverse, depth + 1);
+ /* In order to call GetFinalPathNameByHandle we need to open
+ the file without the reparse handling flag set. */
+ hFile2 = CreateFileA(
+ path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile2 == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (!get_target_path(hFile2, &target_path))
+ return -1;
+
+ code = win32_xstat_impl_w(target_path, result, FALSE);
free(target_path);
return code;
}
@@ -1206,28 +1252,36 @@ win32_xstat_impl(const char *path, struct win32_stat *result, BOOL traverse, int
}
static int
-win32_xstat_impl_w(const wchar_t *path, struct win32_stat *result, BOOL traverse, int depth)
+win32_xstat_impl_w(const wchar_t *path, struct win32_stat *result,
+ BOOL traverse)
{
int code;
- HANDLE hFile;
+ HANDLE hFile, hFile2;
BY_HANDLE_FILE_INFORMATION info;
ULONG reparse_tag = 0;
wchar_t *target_path;
const wchar_t *dot;
- if (depth > SYMLOOP_MAX) {
- SetLastError(ERROR_CANT_RESOLVE_FILENAME); /* XXX: ELOOP? */
+ if(!check_GetFinalPathNameByHandle()) {
+ /* If the OS doesn't have GetFinalPathNameByHandle, return a
+ NotImplementedError. */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "GetFinalPathNameByHandle not available on this platform");
return -1;
}
hFile = CreateFileW(
path,
- 0, /* desired access */
+ FILE_READ_ATTRIBUTES, /* desired access */
0, /* share mode */
NULL, /* security attributes */
OPEN_EXISTING,
/* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
- FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT,
+ /* FILE_FLAG_OPEN_REPARSE_POINT does not follow the symlink.
+ Because of this, calls like GetFinalPathNameByHandle will return
+ the symlink path agin and not the actual final path. */
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|
+ FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -1251,15 +1305,32 @@ win32_xstat_impl_w(const wchar_t *path, struct win32_stat *result, BOOL traverse
} else {
if (!GetFileInformationByHandle(hFile, &info)) {
CloseHandle(hFile);
- return -1;;
+ return -1;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- code = win32_read_link(hFile, &reparse_tag, traverse ? &target_path : NULL);
- CloseHandle(hFile);
- if (code < 0)
- return code;
+ if (!win32_get_reparse_tag(hFile, &reparse_tag))
+ return -1;
+
+ /* Close the outer open file handle now that we're about to
+ reopen it with different flags. */
+ if (!CloseHandle(hFile))
+ return -1;
+
if (traverse) {
- code = win32_xstat_impl_w(target_path, result, traverse, depth + 1);
+ /* In order to call GetFinalPathNameByHandle we need to open
+ the file without the reparse handling flag set. */
+ hFile2 = CreateFileW(
+ path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile2 == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (!get_target_path(hFile2, &target_path))
+ return -1;
+
+ code = win32_xstat_impl_w(target_path, result, FALSE);
free(target_path);
return code;
}
@@ -1283,7 +1354,7 @@ win32_xstat(const char *path, struct win32_stat *result, BOOL traverse)
{
/* Protocol violation: we explicitly clear errno, instead of
setting it to a POSIX error. Callers should use GetLastError. */
- int code = win32_xstat_impl(path, result, traverse, 0);
+ int code = win32_xstat_impl(path, result, traverse);
errno = 0;
return code;
}
@@ -1293,13 +1364,11 @@ win32_xstat_w(const wchar_t *path, struct win32_stat *result, BOOL traverse)
{
/* Protocol violation: we explicitly clear errno, instead of
setting it to a POSIX error. Callers should use GetLastError. */
- int code = win32_xstat_impl_w(path, result, traverse, 0);
+ int code = win32_xstat_impl_w(path, result, traverse);
errno = 0;
return code;
}
-
-/* About the following functions: win32_lstat, win32_lstat_w, win32_stat,
- win32_stat_w
+/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w
In Posix, stat automatically traverses symlinks and returns the stat
structure for the target. In Windows, the equivalent GetFileAttributes by
@@ -2848,29 +2917,7 @@ posix__getfullpathname(PyObject *self, PyObject *args)
return PyBytes_FromString(outbuf);
} /* end of posix__getfullpathname */
-/* Grab GetFinalPathNameByHandle dynamically from kernel32 */
-static int has_GetFinalPathNameByHandle = 0;
-static DWORD (CALLBACK *Py_GetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD,
- DWORD);
-static DWORD (CALLBACK *Py_GetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD,
- DWORD);
-static int
-check_GetFinalPathNameByHandle()
-{
- HINSTANCE hKernel32;
- /* only recheck */
- if (!has_GetFinalPathNameByHandle)
- {
- hKernel32 = GetModuleHandle("KERNEL32");
- *(FARPROC*)&Py_GetFinalPathNameByHandleA = GetProcAddress(hKernel32,
- "GetFinalPathNameByHandleA");
- *(FARPROC*)&Py_GetFinalPathNameByHandleW = GetProcAddress(hKernel32,
- "GetFinalPathNameByHandleW");
- has_GetFinalPathNameByHandle = Py_GetFinalPathNameByHandleA &&
- Py_GetFinalPathNameByHandleW;
- }
- return has_GetFinalPathNameByHandle;
-}
+
/* A helper function for samepath on windows */
static PyObject *
@@ -5610,7 +5657,7 @@ posix_lstat(PyObject *self, PyObject *args)
return posix_do_stat(self, args, "O&:lstat", lstat, NULL, NULL);
#else /* !HAVE_LSTAT */
#ifdef MS_WINDOWS
- return posix_do_stat(self, args, "O&:lstat", STAT, "U:lstat",
+ return posix_do_stat(self, args, "O&:lstat", win32_lstat, "U:lstat",
win32_lstat_w);
#else
return posix_do_stat(self, args, "O&:lstat", STAT, NULL, NULL);