[Python-checkins] r86727 - in python/branches/py3k: Misc/NEWS Modules/posixmodule.c

brian.curtin python-checkins at python.org
Wed Nov 24 14:14:06 CET 2010


Author: brian.curtin
Date: Wed Nov 24 14:14:05 2010
New Revision: 86727

Log:
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.



Modified:
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/posixmodule.c

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Wed Nov 24 14:14:05 2010
@@ -10,6 +10,9 @@
 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 #10255: Fix reference leak in Py_InitializeEx().  Patch by Neil

Modified: python/branches/py3k/Modules/posixmodule.c
==============================================================================
--- python/branches/py3k/Modules/posixmodule.c	(original)
+++ python/branches/py3k/Modules/posixmodule.c	Wed Nov 24 14:14:05 2010
@@ -437,6 +437,96 @@
 #define _PyVerify_fd_dup2(A, B) (1)
 #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 */
 #ifdef WITH_NEXT_FRAMEWORK
 /* On Darwin/MacOSX a shared library or framework has no access to
@@ -934,7 +1024,7 @@
 }
 
 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));
     result->st_mode = attributes_to_mode(info->dwFileAttributes);
@@ -942,12 +1032,13 @@
     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);
+    result->st_nlink = info->nNumberOfLinks;
 
     return 0;
 }
 
 static BOOL
-attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad)
+attributes_from_dir(LPCSTR pszFile, BY_HANDLE_FILE_INFORMATION *info)
 {
     HANDLE hFindFile;
     WIN32_FIND_DATAA FileData;
@@ -955,17 +1046,19 @@
     if (hFindFile == INVALID_HANDLE_VALUE)
         return FALSE;
     FindClose(hFindFile);
-    pfad->dwFileAttributes = FileData.dwFileAttributes;
-    pfad->ftCreationTime   = FileData.ftCreationTime;
-    pfad->ftLastAccessTime = FileData.ftLastAccessTime;
-    pfad->ftLastWriteTime  = FileData.ftLastWriteTime;
-    pfad->nFileSizeHigh    = FileData.nFileSizeHigh;
-    pfad->nFileSizeLow     = FileData.nFileSizeLow;
+    memset(info, 0, sizeof(*info));
+    info->dwFileAttributes = FileData.dwFileAttributes;
+    info->ftCreationTime   = FileData.ftCreationTime;
+    info->ftLastAccessTime = FileData.ftLastAccessTime;
+    info->ftLastWriteTime  = FileData.ftLastWriteTime;
+    info->nFileSizeHigh    = FileData.nFileSizeHigh;
+    info->nFileSizeLow     = FileData.nFileSizeLow;
+/*  info->nNumberOfLinks   = 1; */
     return TRUE;
 }
 
 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;
     WIN32_FIND_DATAW FileData;
@@ -973,315 +1066,221 @@
     if (hFindFile == INVALID_HANDLE_VALUE)
         return FALSE;
     FindClose(hFindFile);
-    pfad->dwFileAttributes = FileData.dwFileAttributes;
-    pfad->ftCreationTime   = FileData.ftCreationTime;
-    pfad->ftLastAccessTime = FileData.ftLastAccessTime;
-    pfad->ftLastWriteTime  = FileData.ftLastWriteTime;
-    pfad->nFileSizeHigh    = FileData.nFileSizeHigh;
-    pfad->nFileSizeLow     = FileData.nFileSizeLow;
+    memset(info, 0, sizeof(*info));
+    info->dwFileAttributes = FileData.dwFileAttributes;
+    info->ftCreationTime   = FileData.ftCreationTime;
+    info->ftLastAccessTime = FileData.ftLastAccessTime;
+    info->ftLastWriteTime  = FileData.ftLastWriteTime;
+    info->nFileSizeHigh    = FileData.nFileSizeHigh;
+    info->nFileSizeLow     = FileData.nFileSizeLow;
+/*  info->nNumberOfLinks   = 1; */
     return TRUE;
 }
 
-/* About the following functions: win32_lstat, 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
-   default does not traverse symlinks and instead returns attributes for
-   the symlink.
-
-   Therefore, win32_lstat will get the attributes traditionally, and
-   win32_stat will first explicitly resolve the symlink target and then will
-   call win32_lstat on that result.
+#ifndef SYMLOOP_MAX
+#define SYMLOOP_MAX ( 88 )
+#endif
 
-   The _w represent Unicode equivalents of the aformentioned ANSI functions. */
+static int
+win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, int depth);
 
-static int 
-win32_lstat(const char* path, struct win32_stat *result)
+static int
+win32_xstat(const char *path, struct win32_stat *result, BOOL traverse, int depth)
 {
-    WIN32_FILE_ATTRIBUTE_DATA info;
     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 {
+    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)) {
+            if (!attributes_from_dir(path, &info))
                 /* Very strange. This should not fail now */
-                errno = 0;
-                return -1;
+                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);
         }
     }
-
-    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);
-        }
+    else {
+        code = win32_xstat_for_handle(hFile, result, traverse, depth);
+        CloseHandle(hFile);
+        if (code != 0)
+            return code;
     }
 
-    /* Set S_IFEXEC if it is an .exe, .bat, ... */
+    /* 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 code;
-}
-
-static int
-win32_lstat_w(const wchar_t* path, struct win32_stat *result)
-{
-    int code;
-    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;
-}
+    return 0;
 
-/* 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;
+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_stat(const char* path, struct win32_stat *result)
+win32_xstat_w(const wchar_t *path, struct win32_stat *result, BOOL traverse, int depth)
 {
-    /* Traverse the symlink to the target using
-    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);
-    }
+    BY_HANDLE_FILE_INFORMATION info;
+    const wchar_t *dot;
 
-    hFile = CreateFileA(
+    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_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) {
-            /* Protocol violation: we explicitly clear errno, instead of
-               setting it to a POSIX error. Callers should use GetLastError. */
-            errno = 0;
-            return -1;
-        } else {
+        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)) {
+            if (!attributes_from_dir_w(path, &info))
                 /* Very strange. This should not fail now */
-                errno = 0;
-                return -1;
+                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);
         }
-        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;
-        }
+        code = win32_xstat_for_handle(hFile, result, traverse, depth);
+        CloseHandle(hFile);
+        if (code != 0)
+            return code;
+    }
 
-        target_path[result_length] = 0;
-        code = win32_lstat(target_path, result);
-        free(target_path);
+    /* 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 code;
+    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_stat_w(const wchar_t* path, struct win32_stat *result)
+static int
+win32_xstat_for_handle(HANDLE hFile, struct win32_stat *result, BOOL traverse, int depth)
 {
-    /* Traverse the symlink to the target using GetFinalPathNameByHandle() */
     int code;
-    HANDLE hFile;
-    int buf_size;
+    BOOL reparse_tag;
     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);
-    }
+    BY_HANDLE_FILE_INFORMATION info;
 
-    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 (!GetFileInformationByHandle(hFile, &info))
+        return -1;
 
-    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;
+    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 {
-            /* 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;
+            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;
             }
         }
-        code = attribute_data_to_stat(&info, result);
+    } else {
+        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;
+    return 0;
+}
 
-        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;
-        }
+/* About the following functions: win32_lstat, win32_lstat_w, win32_stat,
+   win32_stat_w
 
-        if(!CloseHandle(hFile)) {
-            free(target_path);
-            return -1;
-        }
+   In Posix, stat automatically traverses symlinks and returns the stat
+   structure for the target.  In Windows, the equivalent GetFileAttributes by
+   default does not traverse symlinks and instead returns attributes for
+   the symlink.
 
-        target_path[result_length] = 0;
-        code = win32_lstat_w(target_path, result);
-        free(target_path);
-    }
-    
-    return code;
+   Therefore, win32_lstat will get the attributes traditionally, and
+   win32_stat will first explicitly resolve the symlink target and then will
+   call win32_lstat on that result.
+
+   The _w represent Unicode equivalents of the aformentioned ANSI functions. */
+
+static int 
+win32_lstat(const char* path, struct win32_stat *result)
+{
+    return win32_xstat(path, result, FALSE, 0);
+}
+
+static int
+win32_lstat_w(const wchar_t* path, struct win32_stat *result)
+{
+    return win32_xstat_w(path, result, FALSE, 0);
+}
+
+static int
+win32_stat(const char* path, struct win32_stat *result)
+{
+    return win32_xstat(path, result, TRUE, 0);
+}
+
+static int 
+win32_stat_w(const wchar_t* path, struct win32_stat *result)
+{
+    return win32_xstat_w(path, result, TRUE, 0);
 }
 
 static int
@@ -1309,7 +1308,7 @@
     if (type == FILE_TYPE_UNKNOWN) {
         DWORD error = GetLastError();
         if (error != 0) {
-        return -1;
+            return -1;
         }
         /* else: valid but unknown file */
     }
@@ -1326,17 +1325,8 @@
         return -1;
     }
 
-    /* similar to stat() */
-    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);
+    attribute_data_to_stat(&info, result);
     /* specific to fstat() */
-    result->st_nlink = info.nNumberOfLinks;
     result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow;
     return 0;
 }
@@ -2644,6 +2634,30 @@
     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 *
 posix__getfinalpathname(PyObject *self, PyObject *args)
@@ -5044,43 +5058,6 @@
 "readlink(path) -> path\n\n\
 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 */
 static PyObject *
 win_readlink(PyObject *self, PyObject *args)


More information about the Python-checkins mailing list