Completed
Pull Request — master (#868)
by
unknown
01:08
created

MOUNT_POINT_REPARSE_BUFFER   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 11
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 10
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A PrintName() 0 5 1
1
# Functions for identifying and following symlinks in Windows
2
# from user `eryksun` http://stackoverflow.com/a/27979160
3
# 16 Jan 2015
4
# See http://stackoverflow.com/a/15259028 for more information
5
6
import os
7
8
from ctypes import *
9
from ctypes.wintypes import *
10
11
kernel32 = WinDLL('kernel32')
12
LPDWORD = POINTER(DWORD)
13
UCHAR = c_ubyte
14
15
GetFileAttributesW = kernel32.GetFileAttributesW
16
GetFileAttributesW.restype = DWORD
17
GetFileAttributesW.argtypes = (LPCWSTR,) #lpFileName In
18
19
INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
20
FILE_ATTRIBUTE_REPARSE_POINT = 0x00400
21
22
CreateFileW = kernel32.CreateFileW
23
CreateFileW.restype = HANDLE
24
CreateFileW.argtypes = (LPCWSTR, #lpFileName In
25
                        DWORD,   #dwDesiredAccess In
26
                        DWORD,   #dwShareMode In
27
                        LPVOID,  #lpSecurityAttributes In_opt
28
                        DWORD,   #dwCreationDisposition In
29
                        DWORD,   #dwFlagsAndAttributes In
30
                        HANDLE)  #hTemplateFile In_opt
31
32
CloseHandle = kernel32.CloseHandle
33
CloseHandle.restype = BOOL
34
CloseHandle.argtypes = (HANDLE,) #hObject In
35
36
INVALID_HANDLE_VALUE = HANDLE(-1).value
37
OPEN_EXISTING = 3
38
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
39
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
40
41
DeviceIoControl = kernel32.DeviceIoControl
42
DeviceIoControl.restype = BOOL
43
DeviceIoControl.argtypes = (HANDLE,  #hDevice In
44
                            DWORD,   #dwIoControlCode In
45
                            LPVOID,  #lpInBuffer In_opt
46
                            DWORD,   #nInBufferSize In
47
                            LPVOID,  #lpOutBuffer Out_opt
48
                            DWORD,   #nOutBufferSize In
49
                            LPDWORD, #lpBytesReturned Out_opt
50
                            LPVOID)  #lpOverlapped Inout_opt
51
52
FSCTL_GET_REPARSE_POINT = 0x000900A8
53
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
54
IO_REPARSE_TAG_SYMLINK = 0xA000000C
55
MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000
56
57
class GENERIC_REPARSE_BUFFER(Structure):
58
    _fields_ = (('DataBuffer', UCHAR * 1),)
59
60
61
class SYMBOLIC_LINK_REPARSE_BUFFER(Structure):
62
    _fields_ = (('SubstituteNameOffset', USHORT),
63
                ('SubstituteNameLength', USHORT),
64
                ('PrintNameOffset', USHORT),
65
                ('PrintNameLength', USHORT),
66
                ('Flags', ULONG),
67
                ('PathBuffer', WCHAR * 1))
68
    @property
69
    def PrintName(self):
70
        arrayt = WCHAR * (self.PrintNameLength // 2)
71
        offset = type(self).PathBuffer.offset + self.PrintNameOffset
72
        return arrayt.from_address(addressof(self) + offset).value
73
74
75
class MOUNT_POINT_REPARSE_BUFFER(Structure):
76
    _fields_ = (('SubstituteNameOffset', USHORT),
77
                ('SubstituteNameLength', USHORT),
78
                ('PrintNameOffset', USHORT),
79
                ('PrintNameLength', USHORT),
80
                ('PathBuffer', WCHAR * 1))
81
    @property
82
    def PrintName(self):
83
        arrayt = WCHAR * (self.PrintNameLength // 2)
84
        offset = type(self).PathBuffer.offset + self.PrintNameOffset
85
        return arrayt.from_address(addressof(self) + offset).value
86
87
88
class REPARSE_DATA_BUFFER(Structure):
89
    class REPARSE_BUFFER(Union):
90
        _fields_ = (('SymbolicLinkReparseBuffer',
91
                        SYMBOLIC_LINK_REPARSE_BUFFER),
92
                    ('MountPointReparseBuffer',
93
                        MOUNT_POINT_REPARSE_BUFFER),
94
                    ('GenericReparseBuffer',
95
                        GENERIC_REPARSE_BUFFER))
96
    _fields_ = (('ReparseTag', ULONG),
97
                ('ReparseDataLength', USHORT),
98
                ('Reserved', USHORT),
99
                ('ReparseBuffer', REPARSE_BUFFER))
100
    _anonymous_ = ('ReparseBuffer',)
101
102
103
def islink(path):
104
    result = GetFileAttributesW(path)
105
    if result == INVALID_FILE_ATTRIBUTES:
106
        raise WinError()
107
    return bool(result & FILE_ATTRIBUTE_REPARSE_POINT)
108
109
def readlink(path):
110
    reparse_point_handle = CreateFileW(path,
111
                                       0,
112
                                       0,
113
                                       None,
114
                                       OPEN_EXISTING,
115
                                       FILE_FLAG_OPEN_REPARSE_POINT |
116
                                       FILE_FLAG_BACKUP_SEMANTICS,
117
                                       None)
118
    if reparse_point_handle == INVALID_HANDLE_VALUE:
119
        raise WinError()
120
    target_buffer = c_buffer(MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
121
    n_bytes_returned = DWORD()
122
    io_result = DeviceIoControl(reparse_point_handle,
123
                                FSCTL_GET_REPARSE_POINT,
124
                                None, 0,
125
                                target_buffer, len(target_buffer),
126
                                byref(n_bytes_returned),
127
                                None)
128
    CloseHandle(reparse_point_handle)
129
    if not io_result:
130
        raise WinError()
131
    rdb = REPARSE_DATA_BUFFER.from_buffer(target_buffer)
132
    if rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK:
133
        return rdb.SymbolicLinkReparseBuffer.PrintName
134
    elif rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT:
135
        return rdb.MountPointReparseBuffer.PrintName
136
    raise ValueError("not a link")
137
138
# symlink function from Stackoverflow user Gian Marco Gherardi, 23 Feb 2013
139
# http://stackoverflow.com/a/15043806
140
def symlink(source, link_name):
141
    csl = kernel32.CreateSymbolicLinkW
142
    csl.argtypes = (c_wchar_p, c_wchar_p, c_uint32)
143
    csl.restype = c_ubyte
144
145
    flags = 1 if os.path.isdir(source) else 0
146
147
    if csl(link_name, source, flags) == 0:
148
        raise WinError()
149