カテゴリー
Uncategorized

[Python] [Windows] NTFS のシンボリックリンク、ハードリンク、ジャンクションの情報

NTFS においてシンボリックリンクやらハードリンクやらはリパースポイントという機能で実装されています。

パスがリパースポイントかどうか判別する

from ctypes import *
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
kernel = windll.kernel32
GetFileAttributes = kernel.GetFileAttributesW
GetFileAttributes.argtypes = [c_wchar_p]
def islink(path):
attributes = GetFileAttributes(path)
return attributes != -1 and bool(attributes & FILE_ATTRIBUTE_REPARSE_POINT)

リンク先を取得する

from os.path import *
from ctypes import *
from ctypes.wintypes import *
OPEN_EXISTING = 0x3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FSCTL_GET_REPARSE_POINT = 0x000900A8
MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003L
IO_REPARSE_TAG_SYMLINK = 0xA000000CL
class REPARSE_DATA_BUFFER(Structure):
class Data(Union):
class SymbolicLinkReparseBuffer(Structure):
_fields_ = [
('SubstituteNameOffset', WORD, ),
('SubstituteNameLength', WORD, ),
('PrintNameOffset', WORD, ),
('PrintNameLength', WORD, ),
('Flags', ULONG, ),
('PathBuffer', WCHAR*MAXIMUM_REPARSE_DATA_BUFFER_SIZE, ),
]
class MountPointReparseBuffer(Structure):
_fields_ = [
('SubstituteNameOffset', WORD, ),
('SubstituteNameLength', WORD, ),
('PrintNameOffset', WORD, ),
('PrintNameLength', WORD, ),
('PathBuffer', WCHAR*MAXIMUM_REPARSE_DATA_BUFFER_SIZE, ),
]
class GenericReparseBuffer(Structure):
_fields_ = [
('DataBuffer', BYTE*MAXIMUM_REPARSE_DATA_BUFFER_SIZE, ),
]
_fields_ = [
('SymbolicLinkReparseBuffer', SymbolicLinkReparseBuffer, ),
('MountPointReparseBuffer', MountPointReparseBuffer, ),
('GenericReparseBuffer', GenericReparseBuffer, ),
]
_fields_ = [
('ReparseTag', DWORD, ),
('ReparseDataLength', WORD, ),
('Reserved', WORD, ),
('Data', Data, ),
]
_anonymous_ = ('Data', )
kernel = windll.kernel32
CreateFile = kernel.CreateFileW
CloseHandle = kernel.CloseHandle
DeviceIoControl = kernel.DeviceIoControl
GetVolumeNameForVolumeMountPoint = kernel.GetVolumeNameForVolumeMountPointW
GetVolumeNameForVolumeMountPoint.argtypes = [c_wchar_p, c_wchar_p, c_ulong, ]
GetVolumeNameForVolumeMountPoint.restype = bool
def GetVolumeNameForVolumeMountPoint(path):
if not path.endswith(sep): path += sep
volume = (c_wchar * 1024)()
if kernel.GetVolumeNameForVolumeMountPointW(path, volume, len(volume)):
return volume.value
else:
return u''
def ismount(path):
return bool(GetVolumeNameForVolumeMountPoint(path))
def readlink(path):
if ismount(path):
return path
data = REPARSE_DATA_BUFFER()
handle = CreateFile(c_wchar_p(path), 0, 0, None, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, None)
try:
DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 0,
byref(data), 1024, byref(c_ulong()), None)
finally:
CloseHandle(handle)
if data.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT:
info = data.MountPointReparseBuffer
start = info.SubstituteNameOffset / sizeof(c_wchar)
end = start + info.SubstituteNameLength / sizeof(c_wchar)
src = info.PathBuffer[start:end]
elif data.ReparseTag == IO_REPARSE_TAG_SYMLINK:
info = data.SymbolicLinkReparseBuffer
start = info.SubstituteNameOffset / sizeof(c_wchar)
end = start + info.SubstituteNameLength / sizeof(c_wchar)
src = info.PathBuffer[start:end]
#info.Flags    # 0x0: abs, 0x1: rel
else:
raise IOError(0, 'Target object is not link object.', path)
if src.startswith(u'\\??\\'):
src = src[4:]
return normpath(src)

リンク先取得するだけで DeviceIoControl かよ……

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です