Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(29880)

Side by Side Diff: Modules/posixmodule.c

Issue 10027: os.lstat/os.stat don't set st_nlink on Windows
Patch Set: Created 8 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* POSIX module implementation */ 2 /* POSIX module implementation */
3 3
4 /* This file is also used for Windows NT/MS-Win and OS/2. In that case the 4 /* This file is also used for Windows NT/MS-Win and OS/2. In that case the
5 module actually calls itself 'nt' or 'os2', not 'posix', and a few 5 module actually calls itself 'nt' or 'os2', not 'posix', and a few
6 functions are either unimplemented or implemented differently. The source 6 functions are either unimplemented or implemented differently. The source
7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent 7 assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
8 of the compiler used. Different compilers define their own feature 8 of the compiler used. Different compilers define their own feature
9 test macro, e.g. '__BORLANDC__' or '_MSC_VER'. For OS/2, the compiler 9 test macro, e.g. '__BORLANDC__' or '_MSC_VER'. For OS/2, the compiler
10 independent macro PYOS_OS2 should be defined. On OS/2 the default 10 independent macro PYOS_OS2 should be defined. On OS/2 the default
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 return 0; 429 return 0;
430 if ((unsigned)fd2 < _NHANDLE_) 430 if ((unsigned)fd2 < _NHANDLE_)
431 return 1; 431 return 1;
432 else 432 else
433 return 0; 433 return 0;
434 } 434 }
435 #else 435 #else
436 /* dummy version. _PyVerify_fd() is already defined in fileobject.h */ 436 /* dummy version. _PyVerify_fd() is already defined in fileobject.h */
437 #define _PyVerify_fd_dup2(A, B) (1) 437 #define _PyVerify_fd_dup2(A, B) (1)
438 #endif 438 #endif
439
440 /* The following structure was copied from
441 http://msdn.microsoft.com/en-us/library/ms791514.aspx as the required
442 include doesn't seem to be present in the Windows SDK (at least as included
443 with Visual Studio Express). */
444 typedef struct _REPARSE_DATA_BUFFER {
445 ULONG ReparseTag;
446 USHORT ReparseDataLength;
447 USHORT Reserved;
448 union {
449 struct {
450 USHORT SubstituteNameOffset;
451 USHORT SubstituteNameLength;
452 USHORT PrintNameOffset;
453 USHORT PrintNameLength;
454 ULONG Flags;
455 WCHAR PathBuffer[1];
456 } SymbolicLinkReparseBuffer;
457
458 struct {
459 USHORT SubstituteNameOffset;
460 USHORT SubstituteNameLength;
461 USHORT PrintNameOffset;
462 USHORT PrintNameLength;
463 WCHAR PathBuffer[1];
464 } MountPointReparseBuffer;
465
466 struct {
467 UCHAR DataBuffer[1];
468 } GenericReparseBuffer;
469 };
470 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
471
472 #define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER,\
473 GenericReparseBuffer)
474 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
475
476 static int
477 _Py_ReadLink(HANDLE reparse_point_handle, ULONG *reparse_tag, wchar_t **target_p ath)
478 {
479 char target_buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
480 REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)target_buffer;
481 DWORD n_bytes_returned;
482 const wchar_t *ptr;
483 wchar_t *buf;
484 size_t len;
485
486 if (0 == DeviceIoControl(
487 reparse_point_handle,
488 FSCTL_GET_REPARSE_POINT,
489 NULL, 0, /* in buffer */
490 target_buffer, sizeof(target_buffer),
491 &n_bytes_returned,
492 NULL)) /* we're not using OVERLAPPED_IO */
493 return 0;
494
495 if (reparse_tag)
496 *reparse_tag = rdb->ReparseTag;
497
498 if (target_path) {
499 switch (rdb->ReparseTag) {
500 case IO_REPARSE_TAG_SYMLINK:
501 /* XXX: Maybe should use SubstituteName? */
502 ptr = rdb->SymbolicLinkReparseBuffer.PathBuffer +
503 rdb->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
504 len = rdb->SymbolicLinkReparseBuffer.PrintNameLength/sizeof(WCHAR);
505 break;
506 case IO_REPARSE_TAG_MOUNT_POINT:
507 ptr = rdb->MountPointReparseBuffer.PathBuffer +
508 rdb->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR );
509 len = rdb->MountPointReparseBuffer.SubstituteNameLength/sizeof(WCHAR );
510 break;
511 default:
512 SetLastError(ERROR_REPARSE_TAG_MISMATCH); /* XXX: Proper error code? */
513 return 0;
514 }
515 buf = (wchar_t *)malloc(sizeof(wchar_t)*(len+1));
516 if (!buf) {
517 SetLastError(ERROR_OUTOFMEMORY);
518 return 0;
519 }
520 wcsncpy(buf, ptr, len);
521 buf[len] = L'\0';
522 if (wcsncmp(buf, L"\\??\\", 4) == 0)
523 buf[1] = L'\\';
524 *target_path = buf;
525 }
526
527 return 1;
528 }
439 529
440 /* Return a dictionary corresponding to the POSIX environment table */ 530 /* Return a dictionary corresponding to the POSIX environment table */
441 #ifdef WITH_NEXT_FRAMEWORK 531 #ifdef WITH_NEXT_FRAMEWORK
442 /* On Darwin/MacOSX a shared library or framework has no access to 532 /* On Darwin/MacOSX a shared library or framework has no access to
443 ** environ directly, we must obtain it with _NSGetEnviron(). 533 ** environ directly, we must obtain it with _NSGetEnviron().
444 */ 534 */
445 #include <crt_externs.h> 535 #include <crt_externs.h>
446 static char **environ; 536 static char **environ;
447 #elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) ) 537 #elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) )
448 extern char **environ; 538 extern char **environ;
(...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after
927 else 1017 else
928 m |= _S_IFREG; 1018 m |= _S_IFREG;
929 if (attr & FILE_ATTRIBUTE_READONLY) 1019 if (attr & FILE_ATTRIBUTE_READONLY)
930 m |= 0444; 1020 m |= 0444;
931 else 1021 else
932 m |= 0666; 1022 m |= 0666;
933 return m; 1023 return m;
934 } 1024 }
935 1025
936 static int 1026 static int
937 attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *resul t) 1027 attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, struct win32_stat *resu lt)
938 { 1028 {
939 memset(result, 0, sizeof(*result)); 1029 memset(result, 0, sizeof(*result));
940 result->st_mode = attributes_to_mode(info->dwFileAttributes); 1030 result->st_mode = attributes_to_mode(info->dwFileAttributes);
941 result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow; 1031 result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
942 FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result-> st_ctime_nsec); 1032 FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result-> st_ctime_nsec);
943 FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result- >st_mtime_nsec); 1033 FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result- >st_mtime_nsec);
944 FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result ->st_atime_nsec); 1034 FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result ->st_atime_nsec);
1035 result->st_nlink = info->nNumberOfLinks;
945 1036
946 return 0; 1037 return 0;
947 } 1038 }
948 1039
949 static BOOL 1040 static BOOL
950 attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) 1041 attributes_from_dir(LPCSTR pszFile, BY_HANDLE_FILE_INFORMATION *info)
951 { 1042 {
952 HANDLE hFindFile; 1043 HANDLE hFindFile;
953 WIN32_FIND_DATAA FileData; 1044 WIN32_FIND_DATAA FileData;
954 hFindFile = FindFirstFileA(pszFile, &FileData); 1045 hFindFile = FindFirstFileA(pszFile, &FileData);
955 if (hFindFile == INVALID_HANDLE_VALUE) 1046 if (hFindFile == INVALID_HANDLE_VALUE)
956 return FALSE; 1047 return FALSE;
957 FindClose(hFindFile); 1048 FindClose(hFindFile);
958 pfad->dwFileAttributes = FileData.dwFileAttributes; 1049 memset(info, 0, sizeof(*info));
959 pfad->ftCreationTime = FileData.ftCreationTime; 1050 info->dwFileAttributes = FileData.dwFileAttributes;
960 pfad->ftLastAccessTime = FileData.ftLastAccessTime; 1051 info->ftCreationTime = FileData.ftCreationTime;
961 pfad->ftLastWriteTime = FileData.ftLastWriteTime; 1052 info->ftLastAccessTime = FileData.ftLastAccessTime;
962 pfad->nFileSizeHigh = FileData.nFileSizeHigh; 1053 info->ftLastWriteTime = FileData.ftLastWriteTime;
963 pfad->nFileSizeLow = FileData.nFileSizeLow; 1054 info->nFileSizeHigh = FileData.nFileSizeHigh;
1055 info->nFileSizeLow = FileData.nFileSizeLow;
1056 /* info->nNumberOfLinks = 1; */
964 return TRUE; 1057 return TRUE;
965 } 1058 }
966 1059
967 static BOOL 1060 static BOOL
968 attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) 1061 attributes_from_dir_w(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info)
969 { 1062 {
970 HANDLE hFindFile; 1063 HANDLE hFindFile;
971 WIN32_FIND_DATAW FileData; 1064 WIN32_FIND_DATAW FileData;
972 hFindFile = FindFirstFileW(pszFile, &FileData); 1065 hFindFile = FindFirstFileW(pszFile, &FileData);
973 if (hFindFile == INVALID_HANDLE_VALUE) 1066 if (hFindFile == INVALID_HANDLE_VALUE)
974 return FALSE; 1067 return FALSE;
975 FindClose(hFindFile); 1068 FindClose(hFindFile);
976 pfad->dwFileAttributes = FileData.dwFileAttributes; 1069 memset(info, 0, sizeof(*info));
977 pfad->ftCreationTime = FileData.ftCreationTime; 1070 info->dwFileAttributes = FileData.dwFileAttributes;
978 pfad->ftLastAccessTime = FileData.ftLastAccessTime; 1071 info->ftCreationTime = FileData.ftCreationTime;
979 pfad->ftLastWriteTime = FileData.ftLastWriteTime; 1072 info->ftLastAccessTime = FileData.ftLastAccessTime;
980 pfad->nFileSizeHigh = FileData.nFileSizeHigh; 1073 info->ftLastWriteTime = FileData.ftLastWriteTime;
981 pfad->nFileSizeLow = FileData.nFileSizeLow; 1074 info->nFileSizeHigh = FileData.nFileSizeHigh;
1075 info->nFileSizeLow = FileData.nFileSizeLow;
1076 /* info->nNumberOfLinks = 1; */
982 return TRUE; 1077 return TRUE;
983 } 1078 }
984 1079
985 /* About the following functions: win32_lstat, win32_lstat_w, win32_stat, 1080 #ifndef SYMLOOP_MAX
986 win32_stat_w 1081 #define SYMLOOP_MAX ( 88 )
987 1082 #endif
988 In Posix, stat automatically traverses symlinks and returns the stat 1083
989 structure for the target. In Windows, the equivalent GetFileAttributes by 1084 static int
990 default does not traverse symlinks and instead returns attributes for 1085 win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, i nt depth);
991 the symlink. 1086
992 1087 static int
993 Therefore, win32_lstat will get the attributes traditionally, and 1088 win32_xstat(const char *path, struct win32_stat *result, BOOL traverse, int dept h)
994 win32_stat will first explicitly resolve the symlink target and then will 1089 {
995 call win32_lstat on that result.
996
997 The _w represent Unicode equivalents of the aformentioned ANSI functions. */
998
999 static int
1000 win32_lstat(const char* path, struct win32_stat *result)
1001 {
1002 WIN32_FILE_ATTRIBUTE_DATA info;
1003 int code;
1004 char *dot;
1005 WIN32_FIND_DATAA find_data;
1006 HANDLE find_data_handle;
1007 if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) {
1008 if (GetLastError() != ERROR_SHARING_VIOLATION) {
1009 /* Protocol violation: we explicitly clear errno, instead of
1010 setting it to a POSIX error. Callers should use GetLastError. */
1011 errno = 0;
1012 return -1;
1013 } else {
1014 /* Could not get attributes on open file. Fall back to
1015 reading the directory. */
1016 if (!attributes_from_dir(path, &info)) {
1017 /* Very strange. This should not fail now */
1018 errno = 0;
1019 return -1;
1020 }
1021 }
1022 }
1023
1024 code = attribute_data_to_stat(&info, result);
1025 if (code != 0)
1026 return code;
1027
1028 /* Get WIN32_FIND_DATA structure for the path to determine if
1029 it is a symlink */
1030 if(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1031 find_data_handle = FindFirstFileA(path, &find_data);
1032 if(find_data_handle != INVALID_HANDLE_VALUE) {
1033 if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
1034 /* first clear the S_IFMT bits */
1035 result->st_mode ^= (result->st_mode & 0170000);
1036 /* now set the bits that make this a symlink */
1037 result->st_mode |= 0120000;
1038 }
1039 FindClose(find_data_handle);
1040 }
1041 }
1042
1043 /* Set S_IFEXEC if it is an .exe, .bat, ... */
1044 dot = strrchr(path, '.');
1045 if (dot) {
1046 if (stricmp(dot, ".bat") == 0 || stricmp(dot, ".cmd") == 0 ||
1047 stricmp(dot, ".exe") == 0 || stricmp(dot, ".com") == 0)
1048 result->st_mode |= 0111;
1049 }
1050 return code;
1051 }
1052
1053 static int
1054 win32_lstat_w(const wchar_t* path, struct win32_stat *result)
1055 {
1056 int code;
1057 const wchar_t *dot;
1058 WIN32_FILE_ATTRIBUTE_DATA info;
1059 WIN32_FIND_DATAW find_data;
1060 HANDLE find_data_handle;
1061 if (!GetFileAttributesExW(path, GetFileExInfoStandard, &info)) {
1062 if (GetLastError() != ERROR_SHARING_VIOLATION) {
1063 /* Protocol violation: we explicitly clear errno, instead of
1064 setting it to a POSIX error. Callers should use GetLastError. */
1065 errno = 0;
1066 return -1;
1067 } else {
1068 /* Could not get attributes on open file. Fall back to reading
1069 the directory. */
1070 if (!attributes_from_dir_w(path, &info)) {
1071 /* Very strange. This should not fail now */
1072 errno = 0;
1073 return -1;
1074 }
1075 }
1076 }
1077 code = attribute_data_to_stat(&info, result);
1078 if (code < 0)
1079 return code;
1080
1081 /* Get WIN32_FIND_DATA structure for the path to determine if
1082 it is a symlink */
1083 if(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1084 find_data_handle = FindFirstFileW(path, &find_data);
1085 if(find_data_handle != INVALID_HANDLE_VALUE) {
1086 if(find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
1087 /* first clear the S_IFMT bits */
1088 result->st_mode ^= (result->st_mode & 0170000);
1089 /* now set the bits that make this a symlink */
1090 result->st_mode |= 0120000;
1091 }
1092 FindClose(find_data_handle);
1093 }
1094 }
1095
1096 /* Set IFEXEC if it is an .exe, .bat, ... */
1097 dot = wcsrchr(path, '.');
1098 if (dot) {
1099 if (_wcsicmp(dot, L".bat") == 0 || _wcsicmp(dot, L".cmd") == 0 ||
1100 _wcsicmp(dot, L".exe") == 0 || _wcsicmp(dot, L".com") == 0)
1101 result->st_mode |= 0111;
1102 }
1103 return code;
1104 }
1105
1106 /* Grab GetFinalPathNameByHandle dynamically from kernel32 */
1107 static int has_GetFinalPathNameByHandle = 0;
1108 static DWORD (CALLBACK *Py_GetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD,
1109 DWORD);
1110 static DWORD (CALLBACK *Py_GetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD,
1111 DWORD);
1112 static int
1113 check_GetFinalPathNameByHandle()
1114 {
1115 HINSTANCE hKernel32;
1116 /* only recheck */
1117 if (!has_GetFinalPathNameByHandle)
1118 {
1119 hKernel32 = GetModuleHandle("KERNEL32");
1120 *(FARPROC*)&Py_GetFinalPathNameByHandleA = GetProcAddress(hKernel32,
1121 "GetFinalPathNameByHandleA");
1122 *(FARPROC*)&Py_GetFinalPathNameByHandleW = GetProcAddress(hKernel32,
1123 "GetFinalPathNameByHandleW");
1124 has_GetFinalPathNameByHandle = Py_GetFinalPathNameByHandleA &&
1125 Py_GetFinalPathNameByHandleW;
1126 }
1127 return has_GetFinalPathNameByHandle;
1128 }
1129
1130 static int
1131 win32_stat(const char* path, struct win32_stat *result)
1132 {
1133 /* Traverse the symlink to the target using
1134 GetFinalPathNameByHandle()
1135 */
1136 int code; 1090 int code;
1137 HANDLE hFile; 1091 HANDLE hFile;
1138 int buf_size; 1092 BY_HANDLE_FILE_INFORMATION info;
1139 char *target_path; 1093 const char *dot;
1140 int result_length;
1141 WIN32_FILE_ATTRIBUTE_DATA info;
1142
1143 if(!check_GetFinalPathNameByHandle()) {
1144 /* if the OS doesn't have GetFinalPathNameByHandle, it doesn't
1145 have symlinks, so just fall back to the traditional behavior
1146 found in lstat. */
1147 return win32_lstat(path, result);
1148 }
1149 1094
1150 hFile = CreateFileA( 1095 hFile = CreateFileA(
1151 path, 1096 path,
1152 0, /* desired access */ 1097 0, /* desired access */
1153 0, /* share mode */ 1098 0, /* share mode */
1154 NULL, /* security attributes */ 1099 NULL, /* security attributes */
1155 OPEN_EXISTING, 1100 OPEN_EXISTING,
1156 /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */ 1101 /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
1157 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 1102 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_ POINT,
1158 NULL); 1103 NULL);
1159 1104
1160 if(hFile == INVALID_HANDLE_VALUE) { 1105 if(hFile == INVALID_HANDLE_VALUE) {
1161 /* Either the target doesn't exist, or we don't have access to 1106 /* Either the target doesn't exist, or we don't have access to
1162 get a handle to it. If the former, we need to return an error. 1107 get a handle to it. If the former, we need to return an error.
1163 If the latter, we can use attributes_from_dir. */ 1108 If the latter, we can use attributes_from_dir. */
1164 if (GetLastError() != ERROR_SHARING_VIOLATION) { 1109 if (GetLastError() != ERROR_SHARING_VIOLATION)
1165 /* Protocol violation: we explicitly clear errno, instead of 1110 goto err;
1166 setting it to a POSIX error. Callers should use GetLastError. */ 1111 else {
1167 errno = 0;
1168 return -1;
1169 } else {
1170 /* Could not get attributes on open file. Fall back to 1112 /* Could not get attributes on open file. Fall back to
1171 reading the directory. */ 1113 reading the directory. */
1172 if (!attributes_from_dir(path, &info)) { 1114 if (!attributes_from_dir(path, &info))
1173 /* Very strange. This should not fail now */ 1115 /* Very strange. This should not fail now */
1174 errno = 0; 1116 goto err;
1175 return -1; 1117 if (traverse && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POIN T)) {
1176 } 1118 /* Should traverse, but cannot open reparse point handle */
1119 SetLastError(ERROR_SHARING_VIOLATION);
1120 goto err;
1121 }
1122 attribute_data_to_stat(&info, result);
1177 } 1123 }
1178 code = attribute_data_to_stat(&info, result);
1179 } 1124 }
1180 else { 1125 else {
1181 /* We have a good handle to the target, use it to determine the target 1126 code = win32_xstat_for_handle(hFile, result, traverse, depth);
1182 path name (then we'll call lstat on it). */ 1127 CloseHandle(hFile);
1183 buf_size = Py_GetFinalPathNameByHandleA(hFile, 0, 0, VOLUME_NAME_DOS); 1128 if (code != 0)
1184 if(!buf_size) return -1; 1129 return code;
1185 /* Due to a slight discrepancy between GetFinalPathNameByHandleA 1130 }
1186 and GetFinalPathNameByHandleW, we must allocate one more byte 1131
1187 than reported. */ 1132 /* Set S_IEXEC if it is an .exe, .bat, ... */
1188 target_path = (char *)malloc((buf_size+2)*sizeof(char)); 1133 dot = strrchr(path, '.');
1189 result_length = Py_GetFinalPathNameByHandleA(hFile, target_path, 1134 if (dot) {
1190 buf_size+1, VOLUME_NAME_DOS ); 1135 if (stricmp(dot, ".bat") == 0 || stricmp(dot, ".cmd") == 0 ||
1191 1136 stricmp(dot, ".exe") == 0 || stricmp(dot, ".com") == 0)
1192 if(!result_length) { 1137 result->st_mode |= 0111;
1193 free(target_path); 1138 }
1194 return -1; 1139 return 0;
1195 } 1140
1196 1141 err:
1197 if(!CloseHandle(hFile)) { 1142 /* Protocol violation: we explicitly clear errno, instead of
1198 free(target_path); 1143 setting it to a POSIX error. Callers should use GetLastError. */
1199 return -1; 1144 errno = 0;
1200 } 1145 return -1;
1201 1146 }
1202 target_path[result_length] = 0; 1147
1203 code = win32_lstat(target_path, result); 1148 static int
1204 free(target_path); 1149 win32_xstat_w(const wchar_t *path, struct win32_stat *result, BOOL traverse, int depth)
1205 } 1150 {
1206
1207 return code;
1208 }
1209
1210 static int
1211 win32_stat_w(const wchar_t* path, struct win32_stat *result)
1212 {
1213 /* Traverse the symlink to the target using GetFinalPathNameByHandle() */
1214 int code; 1151 int code;
1215 HANDLE hFile; 1152 HANDLE hFile;
1216 int buf_size; 1153 BY_HANDLE_FILE_INFORMATION info;
1217 wchar_t *target_path; 1154 const wchar_t *dot;
1218 int result_length;
1219 WIN32_FILE_ATTRIBUTE_DATA info;
1220
1221 if(!check_GetFinalPathNameByHandle()) {
1222 /* If the OS doesn't have GetFinalPathNameByHandle, it doesn't have
1223 symlinks, so just fall back to the traditional behavior found
1224 in lstat. */
1225 return win32_lstat_w(path, result);
1226 }
1227 1155
1228 hFile = CreateFileW( 1156 hFile = CreateFileW(
1229 path, 1157 path,
1230 0, /* desired access */ 1158 0, /* desired access */
1231 0, /* share mode */ 1159 0, /* share mode */
1232 NULL, /* security attributes */ 1160 NULL, /* security attributes */
1233 OPEN_EXISTING, 1161 OPEN_EXISTING,
1234 /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */ 1162 /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */
1235 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 1163 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_ POINT,
1236 NULL); 1164 NULL);
1237 1165
1238 if(hFile == INVALID_HANDLE_VALUE) { 1166 if(hFile == INVALID_HANDLE_VALUE) {
1239 /* Either the target doesn't exist, or we don't have access to 1167 /* Either the target doesn't exist, or we don't have access to
1240 get a handle to it. If the former, we need to return an error. 1168 get a handle to it. If the former, we need to return an error.
1241 If the latter, we can use attributes_from_dir. */ 1169 If the latter, we can use attributes_from_dir. */
1242 if (GetLastError() != ERROR_SHARING_VIOLATION) { 1170 if (GetLastError() != ERROR_SHARING_VIOLATION)
1243 /* Protocol violation: we explicitly clear errno, instead of 1171 goto err;
1244 setting it to a POSIX error. Callers should use GetLastError. */ 1172 else {
1245 errno = 0;
1246 return -1;
1247 } else {
1248 /* Could not get attributes on open file. Fall back to 1173 /* Could not get attributes on open file. Fall back to
1249 reading the directory. */ 1174 reading the directory. */
1250 if (!attributes_from_dir_w(path, &info)) { 1175 if (!attributes_from_dir_w(path, &info))
1251 /* Very strange. This should not fail now */ 1176 /* Very strange. This should not fail now */
1252 errno = 0; 1177 goto err;
1178 if (traverse && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POIN T)) {
1179 /* Should traverse, but cannot open reparse point handle */
1180 SetLastError(ERROR_SHARING_VIOLATION);
1181 goto err;
1182 }
1183 attribute_data_to_stat(&info, result);
1184 }
1185 }
1186 else {
1187 code = win32_xstat_for_handle(hFile, result, traverse, depth);
1188 CloseHandle(hFile);
1189 if (code != 0)
1190 return code;
1191 }
1192
1193 /* Set S_IEXEC if it is an .exe, .bat, ... */
1194 dot = wcsrchr(path, '.');
1195 if (dot) {
1196 if (_wcsicmp(dot, L".bat") == 0 || _wcsicmp(dot, L".cmd") == 0 ||
1197 _wcsicmp(dot, L".exe") == 0 || _wcsicmp(dot, L".com") == 0)
1198 result->st_mode |= 0111;
1199 }
1200 return 0;
1201
1202 err:
1203 /* Protocol violation: we explicitly clear errno, instead of
1204 setting it to a POSIX error. Callers should use GetLastError. */
1205 errno = 0;
1206 return -1;
1207 }
1208
1209 static int
1210 win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, i nt depth)
1211 {
1212 int code;
1213 BOOL reparse_tag;
1214 wchar_t *target_path;
1215 BY_HANDLE_FILE_INFORMATION info;
1216
1217 if (!GetFileInformationByHandle(hFile, &info))
1218 return -1;
1219
1220 if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1221 if (traverse) {
1222 if (depth + 1 > SYMLOOP_MAX) {
1223 SetLastError(ERROR_CANT_RESOLVE_FILENAME); /* XXX: ELOOP? */
1253 return -1; 1224 return -1;
1254 } 1225 }
1226 if (!_Py_ReadLink(hFile, NULL, &target_path))
1227 return -1;
1228 code = win32_xstat_w(target_path, result, traverse, depth + 1);
1229 free(target_path);
1230 return code;
1231 } else {
1232 if (!_Py_ReadLink(hFile, &reparse_tag, NULL))
1233 return -1;
1234 attribute_data_to_stat(&info, result);
1235 if (reparse_tag == IO_REPARSE_TAG_SYMLINK) {
1236 /* first clear the S_IFMT bits */
1237 result->st_mode ^= (result->st_mode & 0170000);
1238 /* now set the bits that make this a symlink */
1239 result->st_mode |= 0120000;
1240 }
1255 } 1241 }
1256 code = attribute_data_to_stat(&info, result); 1242 } else {
1257 } 1243 attribute_data_to_stat(&info, result);
1258 else { 1244 }
1259 /* We have a good handle to the target, use it to determine the target 1245 return 0;
1260 path name (then we'll call lstat on it). */ 1246 }
1261 buf_size = Py_GetFinalPathNameByHandleW(hFile, 0, 0, VOLUME_NAME_DOS); 1247
1262 if(!buf_size) 1248 /* About the following functions: win32_lstat, win32_lstat_w, win32_stat,
1263 return -1; 1249 win32_stat_w
1264 1250
1265 target_path = (wchar_t *)malloc((buf_size+1)*sizeof(wchar_t)); 1251 In Posix, stat automatically traverses symlinks and returns the stat
1266 result_length = Py_GetFinalPathNameByHandleW(hFile, target_path, 1252 structure for the target. In Windows, the equivalent GetFileAttributes by
1267 buf_size, VOLUME_NAME_DOS); 1253 default does not traverse symlinks and instead returns attributes for
1268 1254 the symlink.
1269 if(!result_length) { 1255
1270 free(target_path); 1256 Therefore, win32_lstat will get the attributes traditionally, and
1271 return -1; 1257 win32_stat will first explicitly resolve the symlink target and then will
1272 } 1258 call win32_lstat on that result.
1273 1259
1274 if(!CloseHandle(hFile)) { 1260 The _w represent Unicode equivalents of the aformentioned ANSI functions. */
1275 free(target_path); 1261
1276 return -1; 1262 static int
1277 } 1263 win32_lstat(const char* path, struct win32_stat *result)
1278 1264 {
1279 target_path[result_length] = 0; 1265 return win32_xstat(path, result, FALSE, 0);
1280 code = win32_lstat_w(target_path, result); 1266 }
1281 free(target_path); 1267
1282 } 1268 static int
1283 1269 win32_lstat_w(const wchar_t* path, struct win32_stat *result)
1284 return code; 1270 {
1271 return win32_xstat_w(path, result, FALSE, 0);
1272 }
1273
1274 static int
1275 win32_stat(const char* path, struct win32_stat *result)
1276 {
1277 return win32_xstat(path, result, TRUE, 0);
1278 }
1279
1280 static int
1281 win32_stat_w(const wchar_t* path, struct win32_stat *result)
1282 {
1283 return win32_xstat_w(path, result, TRUE, 0);
1285 } 1284 }
1286 1285
1287 static int 1286 static int
1288 win32_fstat(int file_number, struct win32_stat *result) 1287 win32_fstat(int file_number, struct win32_stat *result)
1289 { 1288 {
1290 BY_HANDLE_FILE_INFORMATION info; 1289 BY_HANDLE_FILE_INFORMATION info;
1291 HANDLE h; 1290 HANDLE h;
1292 int type; 1291 int type;
1293 1292
1294 h = (HANDLE)_get_osfhandle(file_number); 1293 h = (HANDLE)_get_osfhandle(file_number);
1295 1294
1296 /* Protocol violation: we explicitly clear errno, instead of 1295 /* Protocol violation: we explicitly clear errno, instead of
1297 setting it to a POSIX error. Callers should use GetLastError. */ 1296 setting it to a POSIX error. Callers should use GetLastError. */
1298 errno = 0; 1297 errno = 0;
1299 1298
1300 if (h == INVALID_HANDLE_VALUE) { 1299 if (h == INVALID_HANDLE_VALUE) {
1301 /* This is really a C library error (invalid file handle). 1300 /* This is really a C library error (invalid file handle).
1302 We set the Win32 error to the closes one matching. */ 1301 We set the Win32 error to the closes one matching. */
1303 SetLastError(ERROR_INVALID_HANDLE); 1302 SetLastError(ERROR_INVALID_HANDLE);
1304 return -1; 1303 return -1;
1305 } 1304 }
1306 memset(result, 0, sizeof(*result)); 1305 memset(result, 0, sizeof(*result));
1307 1306
1308 type = GetFileType(h); 1307 type = GetFileType(h);
1309 if (type == FILE_TYPE_UNKNOWN) { 1308 if (type == FILE_TYPE_UNKNOWN) {
1310 DWORD error = GetLastError(); 1309 DWORD error = GetLastError();
1311 if (error != 0) { 1310 if (error != 0) {
1312 return -1; 1311 return -1;
1313 } 1312 }
1314 /* else: valid but unknown file */ 1313 /* else: valid but unknown file */
1315 } 1314 }
1316 1315
1317 if (type != FILE_TYPE_DISK) { 1316 if (type != FILE_TYPE_DISK) {
1318 if (type == FILE_TYPE_CHAR) 1317 if (type == FILE_TYPE_CHAR)
1319 result->st_mode = _S_IFCHR; 1318 result->st_mode = _S_IFCHR;
1320 else if (type == FILE_TYPE_PIPE) 1319 else if (type == FILE_TYPE_PIPE)
1321 result->st_mode = _S_IFIFO; 1320 result->st_mode = _S_IFIFO;
1322 return 0; 1321 return 0;
1323 } 1322 }
1324 1323
1325 if (!GetFileInformationByHandle(h, &info)) { 1324 if (!GetFileInformationByHandle(h, &info)) {
1326 return -1; 1325 return -1;
1327 } 1326 }
1328 1327
1329 /* similar to stat() */ 1328 attribute_data_to_stat(&info, result);
1330 result->st_mode = attributes_to_mode(info.dwFileAttributes);
1331 result->st_size = (((__int64)info.nFileSizeHigh)<<32) + info.nFileSizeLow;
1332 FILE_TIME_to_time_t_nsec(&info.ftCreationTime, &result->st_ctime,
1333 &result->st_ctime_nsec);
1334 FILE_TIME_to_time_t_nsec(&info.ftLastWriteTime, &result->st_mtime,
1335 &result->st_mtime_nsec);
1336 FILE_TIME_to_time_t_nsec(&info.ftLastAccessTime, &result->st_atime,
1337 &result->st_atime_nsec);
1338 /* specific to fstat() */ 1329 /* specific to fstat() */
1339 result->st_nlink = info.nNumberOfLinks;
1340 result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow; 1330 result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow;
1341 return 0; 1331 return 0;
1342 } 1332 }
1343 1333
1344 #endif /* MS_WINDOWS */ 1334 #endif /* MS_WINDOWS */
1345 1335
1346 PyDoc_STRVAR(stat_result__doc__, 1336 PyDoc_STRVAR(stat_result__doc__,
1347 "stat_result: Result from stat or lstat.\n\n\ 1337 "stat_result: Result from stat or lstat.\n\n\
1348 This object may be accessed either as a tuple of\n\ 1338 This object may be accessed either as a tuple of\n\
1349 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ 1339 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\
(...skipping 1286 matching lines...) Expand 10 before | Expand all | Expand 10 after
2636 Py_DECREF(opath); 2626 Py_DECREF(opath);
2637 return NULL; 2627 return NULL;
2638 } 2628 }
2639 Py_DECREF(opath); 2629 Py_DECREF(opath);
2640 if (PyUnicode_Check(PyTuple_GetItem(args, 0))) { 2630 if (PyUnicode_Check(PyTuple_GetItem(args, 0))) {
2641 return PyUnicode_Decode(outbuf, strlen(outbuf), 2631 return PyUnicode_Decode(outbuf, strlen(outbuf),
2642 Py_FileSystemDefaultEncoding, NULL); 2632 Py_FileSystemDefaultEncoding, NULL);
2643 } 2633 }
2644 return PyBytes_FromString(outbuf); 2634 return PyBytes_FromString(outbuf);
2645 } /* end of posix__getfullpathname */ 2635 } /* end of posix__getfullpathname */
2636
2637 /* Grab GetFinalPathNameByHandle dynamically from kernel32 */
2638 static int has_GetFinalPathNameByHandle = 0;
2639 static DWORD (CALLBACK *Py_GetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD,
2640 DWORD);
2641 static DWORD (CALLBACK *Py_GetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD,
2642 DWORD);
2643 static int
2644 check_GetFinalPathNameByHandle()
2645 {
2646 HINSTANCE hKernel32;
2647 /* only recheck */
2648 if (!has_GetFinalPathNameByHandle)
2649 {
2650 hKernel32 = GetModuleHandle("KERNEL32");
2651 *(FARPROC*)&Py_GetFinalPathNameByHandleA = GetProcAddress(hKernel32,
2652 "GetFinalPathNameByHandleA");
2653 *(FARPROC*)&Py_GetFinalPathNameByHandleW = GetProcAddress(hKernel32,
2654 "GetFinalPathNameByHandleW");
2655 has_GetFinalPathNameByHandle = Py_GetFinalPathNameByHandleA &&
2656 Py_GetFinalPathNameByHandleW;
2657 }
2658 return has_GetFinalPathNameByHandle;
2659 }
2646 2660
2647 /* A helper function for samepath on windows */ 2661 /* A helper function for samepath on windows */
2648 static PyObject * 2662 static PyObject *
2649 posix__getfinalpathname(PyObject *self, PyObject *args) 2663 posix__getfinalpathname(PyObject *self, PyObject *args)
2650 { 2664 {
2651 HANDLE hFile; 2665 HANDLE hFile;
2652 int buf_size; 2666 int buf_size;
2653 wchar_t *target_path; 2667 wchar_t *target_path;
2654 int result_length; 2668 int result_length;
2655 PyObject *result; 2669 PyObject *result;
(...skipping 2380 matching lines...) Expand 10 before | Expand all | Expand 10 after
5036 { 5050 {
5037 return posix_2str(args, "O&O&:symlink", symlink); 5051 return posix_2str(args, "O&O&:symlink", symlink);
5038 } 5052 }
5039 #endif /* HAVE_SYMLINK */ 5053 #endif /* HAVE_SYMLINK */
5040 5054
5041 #if !defined(HAVE_READLINK) && defined(MS_WINDOWS) 5055 #if !defined(HAVE_READLINK) && defined(MS_WINDOWS)
5042 5056
5043 PyDoc_STRVAR(win_readlink__doc__, 5057 PyDoc_STRVAR(win_readlink__doc__,
5044 "readlink(path) -> path\n\n\ 5058 "readlink(path) -> path\n\n\
5045 Return a string representing the path to which the symbolic link points."); 5059 Return a string representing the path to which the symbolic link points.");
5046
5047 /* The following structure was copied from
5048 http://msdn.microsoft.com/en-us/library/ms791514.aspx as the required
5049 include doesn't seem to be present in the Windows SDK (at least as included
5050 with Visual Studio Express). */
5051 typedef struct _REPARSE_DATA_BUFFER {
5052 ULONG ReparseTag;
5053 USHORT ReparseDataLength;
5054 USHORT Reserved;
5055 union {
5056 struct {
5057 USHORT SubstituteNameOffset;
5058 USHORT SubstituteNameLength;
5059 USHORT PrintNameOffset;
5060 USHORT PrintNameLength;
5061 ULONG Flags;
5062 WCHAR PathBuffer[1];
5063 } SymbolicLinkReparseBuffer;
5064
5065 struct {
5066 USHORT SubstituteNameOffset;
5067 USHORT SubstituteNameLength;
5068 USHORT PrintNameOffset;
5069 USHORT PrintNameLength;
5070 WCHAR PathBuffer[1];
5071 } MountPointReparseBuffer;
5072
5073 struct {
5074 UCHAR DataBuffer[1];
5075 } GenericReparseBuffer;
5076 };
5077 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
5078
5079 #define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER,\
5080 GenericReparseBuffer)
5081
5082 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
5083 5060
5084 /* Windows readlink implementation */ 5061 /* Windows readlink implementation */
5085 static PyObject * 5062 static PyObject *
5086 win_readlink(PyObject *self, PyObject *args) 5063 win_readlink(PyObject *self, PyObject *args)
5087 { 5064 {
5088 wchar_t *path; 5065 wchar_t *path;
5089 DWORD n_bytes_returned; 5066 DWORD n_bytes_returned;
5090 DWORD io_result; 5067 DWORD io_result;
5091 PyObject *result; 5068 PyObject *result;
5092 HANDLE reparse_point_handle; 5069 HANDLE reparse_point_handle;
(...skipping 3291 matching lines...) Expand 10 before | Expand all | Expand 10 after
8384 8361
8385 8362
8386 #endif /* __APPLE__ */ 8363 #endif /* __APPLE__ */
8387 return m; 8364 return m;
8388 8365
8389 } 8366 }
8390 8367
8391 #ifdef __cplusplus 8368 #ifdef __cplusplus
8392 } 8369 }
8393 #endif 8370 #endif
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+