diff options
author | Brian Curtin <brian@python.org> | 2011-06-13 21:03:01 (GMT) |
---|---|---|
committer | Brian Curtin <brian@python.org> | 2011-06-13 21:03:01 (GMT) |
commit | c9d6a501db51db3038995aa014624f83e860ef63 (patch) | |
tree | 63c24f12abb94fce424b0666dae5cafe8e11b0ef /Modules/posixmodule.c | |
parent | 415007e30dceced68b2c7d3a19143f0a3737fed7 (diff) | |
parent | 3e86c99f9080633283e415f3bd4653285e24c31e (diff) | |
download | cpython-c9d6a501db51db3038995aa014624f83e860ef63.zip cpython-c9d6a501db51db3038995aa014624f83e860ef63.tar.gz cpython-c9d6a501db51db3038995aa014624f83e860ef63.tar.bz2 |
branch merge
Diffstat (limited to 'Modules/posixmodule.c')
-rw-r--r-- | Modules/posixmodule.c | 237 |
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); |