2009-07-17 29 views

Respuesta

7

Respondí esto en un similar question, así que copiaré mi respuesta al siguiente. Desde que escribí esa respuesta, terminé escribiendo un módulo de solo python (si puede llamar a un módulo que usa ctypes solo-pitón) para crear, leer y verificar uniones que se pueden encontrar en this folder. Espero que ayude.

Además, a diferencia de la respuesta que utiliza la API CreateSymbolicLinkA, la implementación vinculada debería funcionar en cualquier versión de Windows que admita uniones. CreateSymbolicLinkA solo es compatible con Vista +.

Respuesta:

python ntfslink extension

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

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

Ajustar los atributos de 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 carpeta si usa MOUNTPOINT en lugar de SYMBOLIC_LINK.

Es posible que manera de comprobar que

sys.getwindowsversion()[0] >= 6 

si se pone esto en algo que está liberando, ya que esta forma de enlace simbólico es compatible únicamente con Vista +.

+1

Tenga en cuenta que la extensión ntfslink está actualmente rota bajo Python3 – CharlesB

+0

Vea mi respuesta más abajo para Python> = 3.5 – CharlesB

0

¿No quiere confiar en herramientas externas pero no le importa confiar en el entorno específico? Creo que podría suponer con seguridad que, si se está ejecutando NTFS, la herramienta de unión probablemente estará allí.

Pero, si quiere decir que prefiere no llamar a un programa externo, he encontrado que las cosas de ctypes son invaluables. Le permite llamar a las DLL de Windows directamente desde Python. Y estoy bastante seguro de que está en los lanzamientos estándar de Python hoy en día.

Simplemente tendrías que averiguar en qué DLL de Windows está la llamada de la API CreateJunction() (o como Windows lo llama) y configurar los parámetros y la llamada. La mejor de las suertes con eso, Microsoft no parece apoyarlo muy bien. Usted podría desmontar el programa SysInternals junction o linkd o una de las otras herramientas para averiguar cómo lo hacen.

mí, soy un poco vaga, que acababa de llamar junction como un proceso externo :-)

+0

ctypes está incluido en Python desde 2.5 en adelante. –

+3

El comando de unión no existe en Vista y Win7. Ha sido reemplazado por mklink. –

+0

Existe como una unión de herramienta Sysinternals. – Nux

8

puede utilizar módulos API Win32 por ejemplo pitón

import win32file 

win32file.CreateSymbolicLink(srcDir, targetDir, 1) 

ver http://docs.activestate.com/activepython/2.5/pywin32/win32file__CreateSymbolicLink_meth.html para más detalles

si no quiere depender de eso también, siempre se puede utilizar ctypes y llamar directamente CreateSymbolicLinl API de Win32, que es de todos modos una simple llamada

aquí es ejemplo llamada utilizando ctypes

import ctypes 

kdll = ctypes.windll.LoadLibrary("kernel32.dll") 

kdll.CreateSymbolicLinkA("d:\testdir", "d:\testdir_link", 1) 

MSDN dice mínima soportada cliente de Windows Vista

+2

Creo que las uniones fueron desde Win2K en adelante pero no oficialmente (o bien) apoyado por MS, dada la escasez de documentos sobre cómo hacerlo. Los nuevos enlaces simbólicos se ven mucho mejor, especialmente porque puedes hacerlos a los archivos y (creo) ahora pueden cruzar redes. – paxdiablo

+1

sí las uniones son un subconjunto de enlaces simbólicos –

+7

Las uniones son _no_ un subconjunto de enlaces simbólicos. Las uniones solo se aplican a los directorios. Esta respuesta es incorrecta y crea un enlace simbólico para los archivos (que solo funciona en Vista y arriba) en lugar de un cruce para los directorios (que funciona en NTFS en Windows 2000) y más. Desafortunadamente, no hay una manera realmente fácil de hacer esto en Python. –

4

Desde Python 3.5 hay una función CreateJunction en el módulo _winapi.

import _winapi 
_winapi.CreateJunction(source, target) 
Cuestiones relacionadas