mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 18:28:49 +00:00 
			
		
		
		
	Fix #10027. st_nlink not set on Windows calls to os.stat/lstat.
Note: This patch has no tests because as of now there is no way to create links. #8879 adds that and the tests will go in there. I've manually observed that existing links on my system function properly with this.
This commit is contained in:
		
							parent
							
								
									59540f23ee
								
							
						
					
					
						commit
						f5e76d01ea
					
				
					 2 changed files with 311 additions and 331 deletions
				
			
		|  | @ -10,6 +10,9 @@ What's New in Python 3.2 Beta 1? | ||||||
| Core and Builtins | Core and Builtins | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #10027. st_nlink was not being set on Windows calls to os.stat or | ||||||
|  |   os.lstat. Patch by Hirokazu Yamamoto. | ||||||
|  | 
 | ||||||
| - Issue #10474: range().count() should return integers. | - Issue #10474: range().count() should return integers. | ||||||
| 
 | 
 | ||||||
| - Issue #10255: Fix reference leak in Py_InitializeEx().  Patch by Neil | - Issue #10255: Fix reference leak in Py_InitializeEx().  Patch by Neil | ||||||
|  |  | ||||||
|  | @ -437,6 +437,96 @@ _PyVerify_fd_dup2(int fd1, int fd2) | ||||||
| #define _PyVerify_fd_dup2(A, B) (1) | #define _PyVerify_fd_dup2(A, B) (1) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* The following structure was copied from
 | ||||||
|  |    http://msdn.microsoft.com/en-us/library/ms791514.aspx as the required
 | ||||||
|  |    include doesn't seem to be present in the Windows SDK (at least as included | ||||||
|  |    with Visual Studio Express). */ | ||||||
|  | typedef struct _REPARSE_DATA_BUFFER { | ||||||
|  |     ULONG ReparseTag; | ||||||
|  |     USHORT ReparseDataLength; | ||||||
|  |     USHORT Reserved; | ||||||
|  |     union { | ||||||
|  |         struct { | ||||||
|  |             USHORT SubstituteNameOffset; | ||||||
|  |             USHORT SubstituteNameLength; | ||||||
|  |             USHORT PrintNameOffset; | ||||||
|  |             USHORT PrintNameLength; | ||||||
|  |             ULONG Flags; | ||||||
|  |             WCHAR PathBuffer[1]; | ||||||
|  |         } SymbolicLinkReparseBuffer; | ||||||
|  | 
 | ||||||
|  |         struct { | ||||||
|  |             USHORT SubstituteNameOffset; | ||||||
|  |             USHORT  SubstituteNameLength; | ||||||
|  |             USHORT  PrintNameOffset; | ||||||
|  |             USHORT  PrintNameLength; | ||||||
|  |             WCHAR  PathBuffer[1]; | ||||||
|  |         } MountPointReparseBuffer; | ||||||
|  | 
 | ||||||
|  |         struct { | ||||||
|  |             UCHAR  DataBuffer[1]; | ||||||
|  |         } GenericReparseBuffer; | ||||||
|  |     }; | ||||||
|  | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; | ||||||
|  | 
 | ||||||
|  | #define REPARSE_DATA_BUFFER_HEADER_SIZE  FIELD_OFFSET(REPARSE_DATA_BUFFER,\ | ||||||
|  |                                                       GenericReparseBuffer) | ||||||
|  | #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 ) | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | _Py_ReadLink(HANDLE reparse_point_handle, ULONG *reparse_tag, wchar_t **target_path) | ||||||
|  | { | ||||||
|  |     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, | ||||||
|  |         FSCTL_GET_REPARSE_POINT, | ||||||
|  |         NULL, 0, /* in buffer */ | ||||||
|  |         target_buffer, sizeof(target_buffer), | ||||||
|  |         &n_bytes_returned, | ||||||
|  |         NULL)) /* we're not using OVERLAPPED_IO */ | ||||||
|  |         return 0; | ||||||
|  | 
 | ||||||
|  |     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 0; | ||||||
|  |         } | ||||||
|  |         buf = (wchar_t *)malloc(sizeof(wchar_t)*(len+1)); | ||||||
|  |         if (!buf) { | ||||||
|  |             SetLastError(ERROR_OUTOFMEMORY); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         wcsncpy(buf, ptr, len); | ||||||
|  |         buf[len] = L'\0'; | ||||||
|  |         if (wcsncmp(buf, L"\\??\\", 4) == 0) | ||||||
|  |             buf[1] = L'\\'; | ||||||
|  |         *target_path = buf; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Return a dictionary corresponding to the POSIX environment table */ | /* Return a dictionary corresponding to the POSIX environment table */ | ||||||
| #ifdef WITH_NEXT_FRAMEWORK | #ifdef WITH_NEXT_FRAMEWORK | ||||||
| /* On Darwin/MacOSX a shared library or framework has no access to
 | /* On Darwin/MacOSX a shared library or framework has no access to
 | ||||||
|  | @ -934,7 +1024,7 @@ attributes_to_mode(DWORD attr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
| attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *result) | attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, struct win32_stat *result) | ||||||
| { | { | ||||||
|     memset(result, 0, sizeof(*result)); |     memset(result, 0, sizeof(*result)); | ||||||
|     result->st_mode = attributes_to_mode(info->dwFileAttributes); |     result->st_mode = attributes_to_mode(info->dwFileAttributes); | ||||||
|  | @ -942,12 +1032,13 @@ attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *resul | ||||||
|     FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); |     FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec); | ||||||
|     FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); |     FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec); | ||||||
|     FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); |     FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec); | ||||||
|  |     result->st_nlink = info->nNumberOfLinks; | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static BOOL | static BOOL | ||||||
| attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) | attributes_from_dir(LPCSTR pszFile, BY_HANDLE_FILE_INFORMATION *info) | ||||||
| { | { | ||||||
|     HANDLE hFindFile; |     HANDLE hFindFile; | ||||||
|     WIN32_FIND_DATAA FileData; |     WIN32_FIND_DATAA FileData; | ||||||
|  | @ -955,17 +1046,19 @@ attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) | ||||||
|     if (hFindFile == INVALID_HANDLE_VALUE) |     if (hFindFile == INVALID_HANDLE_VALUE) | ||||||
|         return FALSE; |         return FALSE; | ||||||
|     FindClose(hFindFile); |     FindClose(hFindFile); | ||||||
|     pfad->dwFileAttributes = FileData.dwFileAttributes; |     memset(info, 0, sizeof(*info)); | ||||||
|     pfad->ftCreationTime   = FileData.ftCreationTime; |     info->dwFileAttributes = FileData.dwFileAttributes; | ||||||
|     pfad->ftLastAccessTime = FileData.ftLastAccessTime; |     info->ftCreationTime   = FileData.ftCreationTime; | ||||||
|     pfad->ftLastWriteTime  = FileData.ftLastWriteTime; |     info->ftLastAccessTime = FileData.ftLastAccessTime; | ||||||
|     pfad->nFileSizeHigh    = FileData.nFileSizeHigh; |     info->ftLastWriteTime  = FileData.ftLastWriteTime; | ||||||
|     pfad->nFileSizeLow     = FileData.nFileSizeLow; |     info->nFileSizeHigh    = FileData.nFileSizeHigh; | ||||||
|  |     info->nFileSizeLow     = FileData.nFileSizeLow; | ||||||
|  | /*  info->nNumberOfLinks   = 1; */ | ||||||
|     return TRUE; |     return TRUE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static BOOL | static BOOL | ||||||
| attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) | attributes_from_dir_w(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info) | ||||||
| { | { | ||||||
|     HANDLE hFindFile; |     HANDLE hFindFile; | ||||||
|     WIN32_FIND_DATAW FileData; |     WIN32_FIND_DATAW FileData; | ||||||
|  | @ -973,15 +1066,185 @@ attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) | ||||||
|     if (hFindFile == INVALID_HANDLE_VALUE) |     if (hFindFile == INVALID_HANDLE_VALUE) | ||||||
|         return FALSE; |         return FALSE; | ||||||
|     FindClose(hFindFile); |     FindClose(hFindFile); | ||||||
|     pfad->dwFileAttributes = FileData.dwFileAttributes; |     memset(info, 0, sizeof(*info)); | ||||||
|     pfad->ftCreationTime   = FileData.ftCreationTime; |     info->dwFileAttributes = FileData.dwFileAttributes; | ||||||
|     pfad->ftLastAccessTime = FileData.ftLastAccessTime; |     info->ftCreationTime   = FileData.ftCreationTime; | ||||||
|     pfad->ftLastWriteTime  = FileData.ftLastWriteTime; |     info->ftLastAccessTime = FileData.ftLastAccessTime; | ||||||
|     pfad->nFileSizeHigh    = FileData.nFileSizeHigh; |     info->ftLastWriteTime  = FileData.ftLastWriteTime; | ||||||
|     pfad->nFileSizeLow     = FileData.nFileSizeLow; |     info->nFileSizeHigh    = FileData.nFileSizeHigh; | ||||||
|  |     info->nFileSizeLow     = FileData.nFileSizeLow; | ||||||
|  | /*  info->nNumberOfLinks   = 1; */ | ||||||
|     return TRUE; |     return TRUE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifndef SYMLOOP_MAX | ||||||
|  | #define SYMLOOP_MAX ( 88 ) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, int depth); | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | win32_xstat(const char *path, struct win32_stat *result, BOOL traverse, int depth) | ||||||
|  | { | ||||||
|  |     int code; | ||||||
|  |     HANDLE hFile; | ||||||
|  |     BY_HANDLE_FILE_INFORMATION info; | ||||||
|  |     const char *dot; | ||||||
|  | 
 | ||||||
|  |     hFile = CreateFileA( | ||||||
|  |         path, | ||||||
|  |         0, /* 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, | ||||||
|  |         NULL); | ||||||
|  | 
 | ||||||
|  |     if(hFile == INVALID_HANDLE_VALUE) { | ||||||
|  |         /* Either the target doesn't exist, or we don't have access to
 | ||||||
|  |            get a handle to it. If the former, we need to return an error. | ||||||
|  |            If the latter, we can use attributes_from_dir. */ | ||||||
|  |         if (GetLastError() != ERROR_SHARING_VIOLATION) | ||||||
|  |             goto err; | ||||||
|  |         else { | ||||||
|  |             /* Could not get attributes on open file. Fall back to
 | ||||||
|  |                reading the directory. */ | ||||||
|  |             if (!attributes_from_dir(path, &info)) | ||||||
|  |                 /* Very strange. This should not fail now */ | ||||||
|  |                 goto err; | ||||||
|  |             if (traverse && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | ||||||
|  |                 /* Should traverse, but cannot open reparse point handle */ | ||||||
|  |                 SetLastError(ERROR_SHARING_VIOLATION); | ||||||
|  |                 goto err; | ||||||
|  |             } | ||||||
|  |             attribute_data_to_stat(&info, result); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         code = win32_xstat_for_handle(hFile, result, traverse, depth); | ||||||
|  |         CloseHandle(hFile); | ||||||
|  |         if (code != 0) | ||||||
|  |             return code; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Set S_IEXEC if it is an .exe, .bat, ... */ | ||||||
|  |     dot = strrchr(path, '.'); | ||||||
|  |     if (dot) { | ||||||
|  |         if (stricmp(dot, ".bat") == 0 || stricmp(dot, ".cmd") == 0 || | ||||||
|  |             stricmp(dot, ".exe") == 0 || stricmp(dot, ".com") == 0) | ||||||
|  |             result->st_mode |= 0111; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  |     /* Protocol violation: we explicitly clear errno, instead of
 | ||||||
|  |        setting it to a POSIX error. Callers should use GetLastError. */ | ||||||
|  |     errno = 0; | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | win32_xstat_w(const wchar_t *path, struct win32_stat *result, BOOL traverse, int depth) | ||||||
|  | { | ||||||
|  |     int code; | ||||||
|  |     HANDLE hFile; | ||||||
|  |     BY_HANDLE_FILE_INFORMATION info; | ||||||
|  |     const wchar_t *dot; | ||||||
|  | 
 | ||||||
|  |     hFile = CreateFileW( | ||||||
|  |         path, | ||||||
|  |         0, /* 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, | ||||||
|  |         NULL); | ||||||
|  | 
 | ||||||
|  |     if(hFile == INVALID_HANDLE_VALUE) { | ||||||
|  |         /* Either the target doesn't exist, or we don't have access to
 | ||||||
|  |            get a handle to it. If the former, we need to return an error. | ||||||
|  |            If the latter, we can use attributes_from_dir. */ | ||||||
|  |         if (GetLastError() != ERROR_SHARING_VIOLATION) | ||||||
|  |             goto err; | ||||||
|  |         else { | ||||||
|  |             /* Could not get attributes on open file. Fall back to
 | ||||||
|  |                reading the directory. */ | ||||||
|  |             if (!attributes_from_dir_w(path, &info)) | ||||||
|  |                 /* Very strange. This should not fail now */ | ||||||
|  |                 goto err; | ||||||
|  |             if (traverse && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { | ||||||
|  |                 /* Should traverse, but cannot open reparse point handle */ | ||||||
|  |                 SetLastError(ERROR_SHARING_VIOLATION); | ||||||
|  |                 goto err; | ||||||
|  |             } | ||||||
|  |             attribute_data_to_stat(&info, result); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         code = win32_xstat_for_handle(hFile, result, traverse, depth); | ||||||
|  |         CloseHandle(hFile); | ||||||
|  |         if (code != 0) | ||||||
|  |             return code; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Set S_IEXEC if it is an .exe, .bat, ... */ | ||||||
|  |     dot = wcsrchr(path, '.'); | ||||||
|  |     if (dot) { | ||||||
|  |         if (_wcsicmp(dot, L".bat") == 0 || _wcsicmp(dot, L".cmd") == 0 || | ||||||
|  |             _wcsicmp(dot, L".exe") == 0 || _wcsicmp(dot, L".com") == 0) | ||||||
|  |             result->st_mode |= 0111; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  |     /* Protocol violation: we explicitly clear errno, instead of
 | ||||||
|  |        setting it to a POSIX error. Callers should use GetLastError. */ | ||||||
|  |     errno = 0; | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, int depth) | ||||||
|  | { | ||||||
|  |     int code; | ||||||
|  |     BOOL reparse_tag; | ||||||
|  |     wchar_t *target_path; | ||||||
|  |     BY_HANDLE_FILE_INFORMATION info; | ||||||
|  | 
 | ||||||
|  |     if (!GetFileInformationByHandle(hFile, &info)) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { | ||||||
|  |         if (traverse) { | ||||||
|  |             if (depth + 1 > SYMLOOP_MAX) { | ||||||
|  |                 SetLastError(ERROR_CANT_RESOLVE_FILENAME); /* XXX: ELOOP? */ | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |             if (!_Py_ReadLink(hFile, NULL, &target_path)) | ||||||
|  |                 return -1; | ||||||
|  |             code = win32_xstat_w(target_path, result, traverse, depth + 1); | ||||||
|  |             free(target_path); | ||||||
|  |             return code; | ||||||
|  |         } else { | ||||||
|  |             if (!_Py_ReadLink(hFile, &reparse_tag, NULL)) | ||||||
|  |                 return -1; | ||||||
|  |             attribute_data_to_stat(&info, result); | ||||||
|  |             if (reparse_tag == IO_REPARSE_TAG_SYMLINK) { | ||||||
|  |                 /* first clear the S_IFMT bits */ | ||||||
|  |                 result->st_mode ^= (result->st_mode & 0170000); | ||||||
|  |                 /* now set the bits that make this a symlink */ | ||||||
|  |                 result->st_mode |= 0120000; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         attribute_data_to_stat(&info, result); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* About the following functions: win32_lstat, win32_lstat_w, win32_stat,
 | /* About the following functions: win32_lstat, win32_lstat_w, win32_stat,
 | ||||||
|    win32_stat_w |    win32_stat_w | ||||||
| 
 | 
 | ||||||
|  | @ -999,289 +1262,25 @@ attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) | ||||||
| static int  | static int  | ||||||
| win32_lstat(const char* path, struct win32_stat *result) | win32_lstat(const char* path, struct win32_stat *result) | ||||||
| { | { | ||||||
|     WIN32_FILE_ATTRIBUTE_DATA info; |     return win32_xstat(path, result, FALSE, 0); | ||||||
|     int code; |  | ||||||
|     char *dot; |  | ||||||
|     WIN32_FIND_DATAA find_data; |  | ||||||
|     HANDLE find_data_handle; |  | ||||||
|     if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) { |  | ||||||
|         if (GetLastError() != ERROR_SHARING_VIOLATION) { |  | ||||||
|             /* Protocol violation: we explicitly clear errno, instead of
 |  | ||||||
|                setting it to a POSIX error. Callers should use GetLastError. */ |  | ||||||
|             errno = 0; |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             /* Could not get attributes on open file. Fall back to
 |  | ||||||
|                reading the directory. */ |  | ||||||
|             if (!attributes_from_dir(path, &info)) { |  | ||||||
|                 /* Very strange. This should not fail now */ |  | ||||||
|                 errno = 0; |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     code = attribute_data_to_stat(&info, result); |  | ||||||
|     if (code != 0) |  | ||||||
|         return code; |  | ||||||
| 
 |  | ||||||
|     /* Get WIN32_FIND_DATA structure for the path to determine if
 |  | ||||||
|        it is a symlink */ |  | ||||||
|     if(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |  | ||||||
|         find_data_handle = FindFirstFileA(path, &find_data); |  | ||||||
|         if(find_data_handle != INVALID_HANDLE_VALUE) { |  | ||||||
|             if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { |  | ||||||
|                 /* first clear the S_IFMT bits */ |  | ||||||
|                 result->st_mode ^= (result->st_mode & 0170000); |  | ||||||
|                 /* now set the bits that make this a symlink */ |  | ||||||
|                 result->st_mode |= 0120000; |  | ||||||
|             } |  | ||||||
|             FindClose(find_data_handle); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Set S_IFEXEC if it is an .exe, .bat, ... */ |  | ||||||
|     dot = strrchr(path, '.'); |  | ||||||
|     if (dot) { |  | ||||||
|         if (stricmp(dot, ".bat") == 0 || stricmp(dot, ".cmd") == 0 || |  | ||||||
|             stricmp(dot, ".exe") == 0 || stricmp(dot, ".com") == 0) |  | ||||||
|             result->st_mode |= 0111; |  | ||||||
|     } |  | ||||||
|     return code; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
| win32_lstat_w(const wchar_t* path, struct win32_stat *result) | win32_lstat_w(const wchar_t* path, struct win32_stat *result) | ||||||
| { | { | ||||||
|     int code; |     return win32_xstat_w(path, result, FALSE, 0); | ||||||
|     const wchar_t *dot; |  | ||||||
|     WIN32_FILE_ATTRIBUTE_DATA info; |  | ||||||
|     WIN32_FIND_DATAW find_data; |  | ||||||
|     HANDLE find_data_handle; |  | ||||||
|     if (!GetFileAttributesExW(path, GetFileExInfoStandard, &info)) { |  | ||||||
|         if (GetLastError() != ERROR_SHARING_VIOLATION) { |  | ||||||
|             /* Protocol violation: we explicitly clear errno, instead of
 |  | ||||||
|                setting it to a POSIX error. Callers should use GetLastError. */ |  | ||||||
|             errno = 0; |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             /* Could not get attributes on open file. Fall back to reading
 |  | ||||||
|             the directory. */ |  | ||||||
|             if (!attributes_from_dir_w(path, &info)) { |  | ||||||
|                 /* Very strange. This should not fail now */ |  | ||||||
|                 errno = 0; |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     code = attribute_data_to_stat(&info, result); |  | ||||||
|     if (code < 0) |  | ||||||
|         return code; |  | ||||||
| 
 |  | ||||||
|     /* Get WIN32_FIND_DATA structure for the path to determine if
 |  | ||||||
|        it is a symlink */ |  | ||||||
|     if(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |  | ||||||
|         find_data_handle = FindFirstFileW(path, &find_data); |  | ||||||
|         if(find_data_handle != INVALID_HANDLE_VALUE) { |  | ||||||
|             if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { |  | ||||||
|                 /* first clear the S_IFMT bits */ |  | ||||||
|                 result->st_mode ^= (result->st_mode & 0170000); |  | ||||||
|                 /* now set the bits that make this a symlink */ |  | ||||||
|                 result->st_mode |= 0120000; |  | ||||||
|             } |  | ||||||
|             FindClose(find_data_handle); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Set IFEXEC if it is an .exe, .bat, ... */ |  | ||||||
|     dot = wcsrchr(path, '.'); |  | ||||||
|     if (dot) { |  | ||||||
|         if (_wcsicmp(dot, L".bat") == 0 || _wcsicmp(dot, L".cmd") == 0 || |  | ||||||
|             _wcsicmp(dot, L".exe") == 0 || _wcsicmp(dot, L".com") == 0) |  | ||||||
|             result->st_mode |= 0111; |  | ||||||
|     } |  | ||||||
|     return code; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* 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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
| win32_stat(const char* path, struct win32_stat *result) | win32_stat(const char* path, struct win32_stat *result) | ||||||
| { | { | ||||||
|     /* Traverse the symlink to the target using
 |     return win32_xstat(path, result, TRUE, 0); | ||||||
|     GetFinalPathNameByHandle() |  | ||||||
|     */ |  | ||||||
|     int code; |  | ||||||
|     HANDLE hFile; |  | ||||||
|     int buf_size; |  | ||||||
|     char *target_path; |  | ||||||
|     int result_length; |  | ||||||
|     WIN32_FILE_ATTRIBUTE_DATA info; |  | ||||||
|      |  | ||||||
|     if(!check_GetFinalPathNameByHandle()) { |  | ||||||
|         /* if the OS doesn't have GetFinalPathNameByHandle, it doesn't
 |  | ||||||
|            have symlinks, so just fall back to the traditional behavior |  | ||||||
|            found in lstat. */ |  | ||||||
|         return win32_lstat(path, result); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     hFile = CreateFileA( |  | ||||||
|         path, |  | ||||||
|         0, /* 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, |  | ||||||
|         NULL); |  | ||||||
|      |  | ||||||
|     if(hFile == INVALID_HANDLE_VALUE) { |  | ||||||
|         /* Either the target doesn't exist, or we don't have access to
 |  | ||||||
|            get a handle to it. If the former, we need to return an error. |  | ||||||
|            If the latter, we can use attributes_from_dir. */ |  | ||||||
|         if (GetLastError() != ERROR_SHARING_VIOLATION) { |  | ||||||
|             /* Protocol violation: we explicitly clear errno, instead of
 |  | ||||||
|                setting it to a POSIX error. Callers should use GetLastError. */ |  | ||||||
|             errno = 0; |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             /* Could not get attributes on open file. Fall back to
 |  | ||||||
|                reading the directory. */ |  | ||||||
|             if (!attributes_from_dir(path, &info)) { |  | ||||||
|                 /* Very strange. This should not fail now */ |  | ||||||
|                 errno = 0; |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         code = attribute_data_to_stat(&info, result); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         /* 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_GetFinalPathNameByHandleA(hFile, 0, 0, VOLUME_NAME_DOS); |  | ||||||
|         if(!buf_size) return -1; |  | ||||||
|         /* Due to a slight discrepancy between GetFinalPathNameByHandleA
 |  | ||||||
|            and GetFinalPathNameByHandleW, we must allocate one more byte |  | ||||||
|            than reported. */ |  | ||||||
|         target_path = (char *)malloc((buf_size+2)*sizeof(char)); |  | ||||||
|         result_length = Py_GetFinalPathNameByHandleA(hFile, target_path, |  | ||||||
|                                                      buf_size+1, VOLUME_NAME_DOS); |  | ||||||
| 
 |  | ||||||
|         if(!result_length) { |  | ||||||
|             free(target_path); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!CloseHandle(hFile)) { |  | ||||||
|             free(target_path); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         target_path[result_length] = 0; |  | ||||||
|         code = win32_lstat(target_path, result); |  | ||||||
|         free(target_path); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     return code; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int  | static int  | ||||||
| win32_stat_w(const wchar_t* path, struct win32_stat *result) | win32_stat_w(const wchar_t* path, struct win32_stat *result) | ||||||
| { | { | ||||||
|     /* Traverse the symlink to the target using GetFinalPathNameByHandle() */ |     return win32_xstat_w(path, result, TRUE, 0); | ||||||
|     int code; |  | ||||||
|     HANDLE hFile; |  | ||||||
|     int buf_size; |  | ||||||
|     wchar_t *target_path; |  | ||||||
|     int result_length; |  | ||||||
|     WIN32_FILE_ATTRIBUTE_DATA info; |  | ||||||
|      |  | ||||||
|     if(!check_GetFinalPathNameByHandle()) { |  | ||||||
|         /* If the OS doesn't have GetFinalPathNameByHandle, it doesn't have
 |  | ||||||
|            symlinks, so just fall back to the traditional behavior found |  | ||||||
|            in lstat. */ |  | ||||||
|         return win32_lstat_w(path, result); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     hFile = CreateFileW( |  | ||||||
|         path, |  | ||||||
|         0, /* 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, |  | ||||||
|         NULL); |  | ||||||
| 
 |  | ||||||
|     if(hFile == INVALID_HANDLE_VALUE) { |  | ||||||
|         /* Either the target doesn't exist, or we don't have access to
 |  | ||||||
|            get a handle to it. If the former, we need to return an error. |  | ||||||
|            If the latter, we can use attributes_from_dir. */ |  | ||||||
|         if (GetLastError() != ERROR_SHARING_VIOLATION) { |  | ||||||
|             /* Protocol violation: we explicitly clear errno, instead of
 |  | ||||||
|                setting it to a POSIX error. Callers should use GetLastError. */ |  | ||||||
|             errno = 0; |  | ||||||
|             return -1; |  | ||||||
|         } else { |  | ||||||
|             /* Could not get attributes on open file. Fall back to
 |  | ||||||
|                reading the directory. */ |  | ||||||
|             if (!attributes_from_dir_w(path, &info)) { |  | ||||||
|                 /* Very strange. This should not fail now */ |  | ||||||
|                 errno = 0; |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         code = attribute_data_to_stat(&info, result); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         /* 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(hFile, 0, 0, VOLUME_NAME_DOS); |  | ||||||
|         if(!buf_size) |  | ||||||
|             return -1; |  | ||||||
| 
 |  | ||||||
|         target_path = (wchar_t *)malloc((buf_size+1)*sizeof(wchar_t)); |  | ||||||
|         result_length = Py_GetFinalPathNameByHandleW(hFile, target_path, |  | ||||||
|                                                 buf_size, VOLUME_NAME_DOS); |  | ||||||
|          |  | ||||||
|         if(!result_length) { |  | ||||||
|             free(target_path); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!CloseHandle(hFile)) { |  | ||||||
|             free(target_path); |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         target_path[result_length] = 0; |  | ||||||
|         code = win32_lstat_w(target_path, result); |  | ||||||
|         free(target_path); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     return code; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
|  | @ -1309,7 +1308,7 @@ win32_fstat(int file_number, struct win32_stat *result) | ||||||
|     if (type == FILE_TYPE_UNKNOWN) { |     if (type == FILE_TYPE_UNKNOWN) { | ||||||
|         DWORD error = GetLastError(); |         DWORD error = GetLastError(); | ||||||
|         if (error != 0) { |         if (error != 0) { | ||||||
|         return -1; |             return -1; | ||||||
|         } |         } | ||||||
|         /* else: valid but unknown file */ |         /* else: valid but unknown file */ | ||||||
|     } |     } | ||||||
|  | @ -1326,17 +1325,8 @@ win32_fstat(int file_number, struct win32_stat *result) | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* similar to stat() */ |     attribute_data_to_stat(&info, result); | ||||||
|     result->st_mode = attributes_to_mode(info.dwFileAttributes); |  | ||||||
|     result->st_size = (((__int64)info.nFileSizeHigh)<<32) + info.nFileSizeLow; |  | ||||||
|     FILE_TIME_to_time_t_nsec(&info.ftCreationTime, &result->st_ctime, |  | ||||||
|                              &result->st_ctime_nsec); |  | ||||||
|     FILE_TIME_to_time_t_nsec(&info.ftLastWriteTime, &result->st_mtime, |  | ||||||
|                              &result->st_mtime_nsec); |  | ||||||
|     FILE_TIME_to_time_t_nsec(&info.ftLastAccessTime, &result->st_atime, |  | ||||||
|                              &result->st_atime_nsec); |  | ||||||
|     /* specific to fstat() */ |     /* specific to fstat() */ | ||||||
|     result->st_nlink = info.nNumberOfLinks; |  | ||||||
|     result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow; |     result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | @ -2644,6 +2634,30 @@ posix__getfullpathname(PyObject *self, PyObject *args) | ||||||
|     return PyBytes_FromString(outbuf); |     return PyBytes_FromString(outbuf); | ||||||
| } /* end of posix__getfullpathname */ | } /* 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 */ | /* A helper function for samepath on windows */ | ||||||
| static PyObject * | static PyObject * | ||||||
| posix__getfinalpathname(PyObject *self, PyObject *args) | posix__getfinalpathname(PyObject *self, PyObject *args) | ||||||
|  | @ -5044,43 +5058,6 @@ PyDoc_STRVAR(win_readlink__doc__, | ||||||
| "readlink(path) -> path\n\n\
 | "readlink(path) -> path\n\n\
 | ||||||
| Return a string representing the path to which the symbolic link points."); | Return a string representing the path to which the symbolic link points."); | ||||||
| 
 | 
 | ||||||
| /* The following structure was copied from
 |  | ||||||
|    http://msdn.microsoft.com/en-us/library/ms791514.aspx as the required
 |  | ||||||
|    include doesn't seem to be present in the Windows SDK (at least as included |  | ||||||
|    with Visual Studio Express). */ |  | ||||||
| typedef struct _REPARSE_DATA_BUFFER { |  | ||||||
|     ULONG ReparseTag; |  | ||||||
|     USHORT ReparseDataLength; |  | ||||||
|     USHORT Reserved; |  | ||||||
|     union { |  | ||||||
|         struct { |  | ||||||
|             USHORT SubstituteNameOffset; |  | ||||||
|             USHORT SubstituteNameLength; |  | ||||||
|             USHORT PrintNameOffset; |  | ||||||
|             USHORT PrintNameLength; |  | ||||||
|             ULONG Flags; |  | ||||||
|             WCHAR PathBuffer[1]; |  | ||||||
|         } SymbolicLinkReparseBuffer; |  | ||||||
| 
 |  | ||||||
|         struct { |  | ||||||
|             USHORT SubstituteNameOffset; |  | ||||||
|             USHORT  SubstituteNameLength; |  | ||||||
|             USHORT  PrintNameOffset; |  | ||||||
|             USHORT  PrintNameLength; |  | ||||||
|             WCHAR  PathBuffer[1]; |  | ||||||
|         } MountPointReparseBuffer; |  | ||||||
| 
 |  | ||||||
|         struct { |  | ||||||
|             UCHAR  DataBuffer[1]; |  | ||||||
|         } GenericReparseBuffer; |  | ||||||
|     }; |  | ||||||
| } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |  | ||||||
| 
 |  | ||||||
| #define REPARSE_DATA_BUFFER_HEADER_SIZE  FIELD_OFFSET(REPARSE_DATA_BUFFER,\ |  | ||||||
|                                                       GenericReparseBuffer) |  | ||||||
| 
 |  | ||||||
| #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 ) |  | ||||||
| 
 |  | ||||||
| /* Windows readlink implementation */ | /* Windows readlink implementation */ | ||||||
| static PyObject * | static PyObject * | ||||||
| win_readlink(PyObject *self, PyObject *args) | win_readlink(PyObject *self, PyObject *args) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Brian Curtin
						Brian Curtin