symbolic links link hard create python winapi symlink pywin32

python - hard - Symlinks en windows?



windows create link to directory (8)

El código de Juntalis no maneja Unicode, así que lo modifiqué para usar ctypes y también lo simplifiqué usando struct. También consulté el código de Uso de una estructura como argumento de función con el módulo python ctypes

import os, ctypes, struct from ctypes import windll, wintypes FSCTL_GET_REPARSE_POINT = 0x900a8 FILE_ATTRIBUTE_READONLY = 0x0001 FILE_ATTRIBUTE_HIDDEN = 0x0002 FILE_ATTRIBUTE_DIRECTORY = 0x0010 FILE_ATTRIBUTE_NORMAL = 0x0080 FILE_ATTRIBUTE_REPARSE_POINT = 0x0400 GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 OPEN_EXISTING = 3 FILE_READ_ATTRIBUTES = 0x80 FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF FILE_FLAG_OPEN_REPARSE_POINT = 2097152 FILE_FLAG_BACKUP_SEMANTICS = 33554432 # FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTI FILE_FLAG_REPARSE_BACKUP = 35651584 GetFileAttributes = windll.kernel32.GetFileAttributesW _CreateFileW = windll.kernel32.CreateFileW _DevIoCtl = windll.kernel32.DeviceIoControl _DevIoCtl.argtypes = [ wintypes.HANDLE, #HANDLE hDevice wintypes.DWORD, #DWORD dwIoControlCode wintypes.LPVOID, #LPVOID lpInBuffer wintypes.DWORD, #DWORD nInBufferSize wintypes.LPVOID, #LPVOID lpOutBuffer wintypes.DWORD, #DWORD nOutBufferSize ctypes.POINTER(wintypes.DWORD), #LPDWORD lpBytesReturned wintypes.LPVOID] #LPOVERLAPPED lpOverlapped _DevIoCtl.restype = wintypes.BOOL def islink(path): assert os.path.isdir(path), path if GetFileAttributes(path) & FILE_ATTRIBUTE_REPARSE_POINT: return True else: return False def DeviceIoControl(hDevice, ioControlCode, input, output): # DeviceIoControl Function # http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx if input: input_size = len(input) else: input_size = 0 if isinstance(output, int): output = ctypes.create_string_buffer(output) output_size = len(output) assert isinstance(output, ctypes.Array) bytesReturned = wintypes.DWORD() status = _DevIoCtl(hDevice, ioControlCode, input, input_size, output, output_size, bytesReturned, None) print "status(%d)" % status if status != 0: return output[:bytesReturned.value] else: return None def CreateFile(path, access, sharemode, creation, flags): return _CreateFileW(path, access, sharemode, None, creation, flags, None) SymbolicLinkReparseFormat = "LHHHHHHL" SymbolicLinkReparseSize = struct.calcsize(SymbolicLinkReparseFormat); def readlink(path): """ Windows readlink implementation. """ # This wouldn''t return true if the file didn''t exist, as far as I know. assert islink(path) assert type(path) == unicode # Open the file correctly depending on the string type. hfile = CreateFile(path, GENERIC_READ, 0, OPEN_EXISTING, FILE_FLAG_REPARSE_BACKUP) # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024) buffer = DeviceIoControl(hfile, FSCTL_GET_REPARSE_POINT, None, 16384) CloseHandle(hfile) # Minimum possible length (assuming length of the target is bigger than 0) if not buffer or len(buffer) < 9: return None # Parse and return our result. # 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; # Only handle SymbolicLinkReparseBuffer (tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength, PrintNameOffset, PrintNameLength, Flags) = struct.unpack(SymbolicLinkReparseFormat, buffer[:SymbolicLinkReparseSize]) print tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength start = SubstituteNameOffset + SymbolicLinkReparseSize actualPath = buffer[start : start + SubstituteNameLength].decode("utf-16") # This utf-16 string is null terminated index = actualPath.find(u"/0") assert index > 0 if index > 0: actualPath = actualPath[:index] if actualPath.startswith(u"?//"): return actualPath[2:] else: return actualPath

¿Alguien sabe de una forma de hacer / leer enlaces simbólicos en las versiones de Win32 de Python? Idealmente, debería haber una cantidad mínima de código específico de la plataforma, ya que necesito que mi aplicación sea multiplataforma.


El problema es, como se explica here , que el propio soporte de Windows para la funcionalidad de los enlaces simbólicos varía según las versiones de Windows, de modo que, por ejemplo, en Vista (con mucho trabajo) puede obtener más funcionalidades que en XP o 2000 (nada AFAIK en otro versiones win32). O podría tener atajos en su lugar, que por supuesto tienen su propio conjunto de limitaciones y no son "realmente" equivalentes a los enlaces simbólicos de Unix. Entonces, debe especificar exactamente qué funcionalidades necesita, cuánto de las que está dispuesto a sacrificar en el altar de la operación cross-win32, etc. - ENTONCES, podemos encontrar la forma de implementar el compromiso que ha elegido en términos de ctypes o llamadas de win32all ... eso es lo de menos, en cierto sentido.


El sistema de archivos NTFS tiene puntos de unión, creo que puedes usarlos en su lugar, puedes usar el módulo API de Python Win32 para eso, por ejemplo

import win32file win32file.CreateSymbolicLink(fileSrc, fileTarget, 1)

Si no quiere confiar en el módulo win32API, siempre puede usar ctypes y llamar directamente a la API CreateSymbolicLink win32, por ej.

import ctypes kdll = ctypes.windll.LoadLibrary("kernel32.dll") kdll.CreateSymbolicLinkA("d://test.txt", "d://test_link.txt", 0)

MSDN ( http://msdn.microsoft.com/en-us/library/aa363866(VS.85).aspx ) dice que el cliente mínimo compatible es Windows Vista

Además : Esto también funciona con directorios (indíquelo con el tercer argumento). Con compatibilidad Unicode se ve así:

kdll.CreateSymbolicLinkW(UR"D:/testdirLink", UR"D:/testdir", 1)

también vea Crear punto de unión NTFS en Python


Puse lo siguiente en Lib / site-packages / sitecustomize.py

import os __CSL = None def symlink(source, link_name): ''''''symlink(source, link_name) Creates a symbolic link pointing to source named link_name'''''' global __CSL if __CSL is None: import ctypes csl = ctypes.windll.kernel32.CreateSymbolicLinkW csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32) csl.restype = ctypes.c_ubyte __CSL = csl flags = 0 if source is not None and os.path.isdir(source): flags = 1 if __CSL(link_name, source, flags) == 0: raise ctypes.WinError() os.symlink = symlink


Usando el comando mklink en el subproceso crear enlace.

from subprocess import call call([''mklink'', ''LINK'', ''TARGET''], shell=True)


aquí está el enlace que contiene todos los métodos de kernel32.dll

http://www.geoffchappell.com/studies/windows/win32/kernel32/api/

Utilicé CreateHardLinkA en Windows XP Sp3, ¡funcionó!

import ctypes si os.path.exists (link_file): os.remove (link_file)

dll = ctypes.windll.LoadLibrary("kernel32.dll") dll.CreateHardLinkA(link_file, _log_filename, 0)


os.symlink funciona en Python 3.3 usando Windows 8.1 con un sistema de archivos NTFS.


extensión python ntfslink

O si desea utilizar pywin32, puede usar el método indicado anteriormente, y para leer, use:

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 == 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''s 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()

Ajuste los atributos en CreateFile a sus necesidades, pero para una situación normal, debería funcionar. Siéntase libre de mejorarlo.

También debería funcionar para uniones de carpetas si usa MOUNTPOINT en lugar de SYMBOLIC_LINK.

Puede manera de verificar que

sys.getwindowsversion()[0] >= 6

si pones esto en algo que estás liberando, ya que esta forma de enlace simbólico solo se admite en Vista +.