Есть ли способ создать точку соединения NTFS в Python? Я знаю, что могу вызвать утилиту junction
, но лучше не полагаться на внешние инструменты.
Создание точки соединения NTFS в Python
Ответ 1
Я ответил на это в аналогичном вопросе, поэтому я скопирую свой ответ на это ниже. После написания этого ответа я закончил писать только python (если вы можете вызвать модуль, который использует только модуль ctypes python) для создания, чтения и проверки соединений, которые можно найти в эта папка. Надеюсь, что это поможет.
Кроме того, в отличие от ответа, который использует API CreateSymbolicLinkA, связанная реализация должна работать с любой версией Windows, поддерживающей соединения. CreateSymbolicLinkA поддерживается только в Vista +.
Ответ:
Или, если вы хотите использовать pywin32, вы можете использовать ранее указанный метод и прочитать, используйте:
from win32file import *
from winioctlcon import FSCTL_GET_REPARSE_POINT
__all__ = ['islink', 'readlink']
# Win32file doesn't seem to have this attribute.
FILE_ATTRIBUTE_REPARSE_POINT = 1024
# To make things easier.
REPARSE_FOLDER = (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)
# For the parse_reparse_buffer function
SYMBOLIC_LINK = 'symbolic'
MOUNTPOINT = 'mountpoint'
GENERIC = 'generic'
def islink(fpath):
""" Windows islink implementation. """
if GetFileAttributes(fpath) & REPARSE_FOLDER:
return True
return False
def parse_reparse_buffer(original, reparse_type=SYMBOLIC_LINK):
""" Implementing the below in Python:
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;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
"""
# Size of our data types
SZULONG = 4 # sizeof(ULONG)
SZUSHORT = 2 # sizeof(USHORT)
# Our structure.
# Probably a better way to iterate a dictionary in a particular order,
# but I was in a hurry, unfortunately, so I used pkeys.
buffer = {
'tag' : SZULONG,
'data_length' : SZUSHORT,
'reserved' : SZUSHORT,
SYMBOLIC_LINK : {
'substitute_name_offset' : SZUSHORT,
'substitute_name_length' : SZUSHORT,
'print_name_offset' : SZUSHORT,
'print_name_length' : SZUSHORT,
'flags' : SZULONG,
'buffer' : u'',
'pkeys' : [
'substitute_name_offset',
'substitute_name_length',
'print_name_offset',
'print_name_length',
'flags',
]
},
MOUNTPOINT : {
'substitute_name_offset' : SZUSHORT,
'substitute_name_length' : SZUSHORT,
'print_name_offset' : SZUSHORT,
'print_name_length' : SZUSHORT,
'buffer' : u'',
'pkeys' : [
'substitute_name_offset',
'substitute_name_length',
'print_name_offset',
'print_name_length',
]
},
GENERIC : {
'pkeys' : [],
'buffer': ''
}
}
# Header stuff
buffer['tag'] = original[:SZULONG]
buffer['data_length'] = original[SZULONG:SZUSHORT]
buffer['reserved'] = original[SZULONG+SZUSHORT:SZUSHORT]
original = original[8:]
# Parsing
k = reparse_type
for c in buffer[k]['pkeys']:
if type(buffer[k][c]) == int:
sz = buffer[k][c]
bytes = original[:sz]
buffer[k][c] = 0
for b in bytes:
n = ord(b)
if n:
buffer[k][c] += n
original = original[sz:]
# Using the offset and length grabbed, we'll set the buffer.
buffer[k]['buffer'] = original
return buffer
def readlink(fpath):
""" Windows readlink implementation. """
# This wouldn't return true if the file didn't exist, as far as I know.
if not islink(fpath):
return None
# Open the file correctly depending on the string type.
handle = CreateFileW(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0) \
if type(fpath) == unicode else \
CreateFile(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0)
# MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
buffer = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 16*1024)
# Above will return an ugly string (byte array), so we'll need to parse it.
# But first, we'll close the handle to our file so we're not locking it anymore.
CloseHandle(handle)
# Minimum possible length (assuming that the length of the target is bigger than 0)
if len(buffer) < 9:
return None
# Parse and return our result.
result = parse_reparse_buffer(buffer)
offset = result[SYMBOLIC_LINK]['substitute_name_offset']
ending = offset + result[SYMBOLIC_LINK]['substitute_name_length']
rpath = result[SYMBOLIC_LINK]['buffer'][offset:ending].replace('\x00','')
if len(rpath) > 4 and rpath[0:4] == '\\??\\':
rpath = rpath[4:]
return rpath
def realpath(fpath):
from os import path
while islink(fpath):
rpath = readlink(fpath)
if not path.isabs(rpath):
rpath = path.abspath(path.join(path.dirname(fpath), rpath))
fpath = rpath
return fpath
def example():
from os import system, unlink
system('cmd.exe /c echo Hello World > test.txt')
system('mklink test-link.txt test.txt')
print 'IsLink: %s' % islink('test-link.txt')
print 'ReadLink: %s' % readlink('test-link.txt')
print 'RealPath: %s' % realpath('test-link.txt')
unlink('test-link.txt')
unlink('test.txt')
if __name__=='__main__':
example()
Отрегулируйте атрибуты в CreateFile в соответствии с вашими потребностями, но для нормальной ситуации он должен работать. Не стесняйтесь улучшать его.
Он также должен работать с папками, если вы используете MOUNTPOINT вместо SYMBOLIC_LINK.
Вы можете проверить, что
sys.getwindowsversion()[0] >= 6
если вы поместите это во что-то, что вы выпускаете, поскольку эта форма символической ссылки поддерживается только в Vista +.
Ответ 2
вы можете использовать python win32 API-модули, например.
import win32file
win32file.CreateSymbolicLink(srcDir, targetDir, 1)
см. http://docs.activestate.com/activepython/2.5/pywin32/win32file__CreateSymbolicLink_meth.html для более подробной информации
если вы тоже не хотите полагаться на это, вы всегда можете использовать ctypes и напрямую обращаться к CreateSymbolicLinl win32 API, который в любом случае является простым вызовом
вот пример вызова с использованием ctypes
import ctypes
kdll = ctypes.windll.LoadLibrary("kernel32.dll")
kdll.CreateSymbolicLinkA("d:\testdir", "d:\testdir_link", 1)
MSDN говорит Минимальный поддерживаемый клиент Windows Vistap >
Ответ 3
Так как у Python 3.5 есть функция CreateJunction
в модуле _winapi
.
import _winapi
_winapi.CreateJunction(source, target)
Ответ 4
Вы не хотите полагаться на внешние инструменты, но не против полагаться на конкретную среду? Я думаю, вы могли бы с уверенностью предположить, что если NTFS вы работаете, вероятно, там будет утилита соединения.
Но, если вы имеете в виду, что не хотите вызывать внешнюю программу, я нашел ctypes материал для быть неоценимым. Он позволяет вызывать Windows DLL напрямую из Python. И я уверен, что это в стандартных выпусках Python в наши дни.
Вам просто нужно выяснить, в какую Windows DLL вызывает вызов CreateJunction()
(или любой другой вызов Windows) API, и настроить параметры и вызвать. Удачи вам в этом, Microsoft, похоже, не очень хорошо его поддерживает. Вы можете разобрать программу SysInternals junction
или linkd
или один из других инструментов, чтобы узнать, как они это делают.
Я, я довольно ленив, я бы назвал junction
внешним процессом: -)