2009-07-17 8 views

risposta

7

Ho risposto a questo in un similar question, quindi copierò la mia risposta a quella di seguito. Da quando ho scritto quella risposta, ho finito per scrivere un modulo solo per Python (se puoi chiamare un modulo che usa solo ctype python) per creare, leggere e controllare i junctions che possono essere trovati in this folder. Spero possa aiutare.

Inoltre, a differenza della risposta che utilizza l'API CreateSymbolicLinkA, l'implementazione collegata dovrebbe funzionare su qualsiasi versione di Windows che supporti i junction. CreateSymbolicLinkA è supportato solo in Vista +.

Risposta:

python ntfslink extension

Oppure, se si desidera utilizzare pywin32, è possibile utilizzare il metodo affermato in precedenza, e di leggere, uso:

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

Regolare gli attributi CreateFile alle tue esigenze, ma per una situazione normale, dovrebbe funzionare. Sentiti libero di migliorare.

Dovrebbe funzionare anche per i raccoglitori di cartelle se si utilizza MOUNTPOINT anziché SYMBOLIC_LINK.

'possibile modo per verificare che

sys.getwindowsversion()[0] >= 6 

se si mette questo in qualcosa si sta liberando, dal momento che questa forma di link simbolico è supportato solo su Vista +.

+1

Si noti che l'estensione ntfslink è attualmente danneggiata in Python3 – CharlesB

+0

Vedere la mia risposta qui sotto per Python> = 3.5 – CharlesB

0

Non si vuole fare affidamento su strumenti esterni, ma non ti importa fare affidamento sull'ambiente specifico? Penso che si possa tranquillamente supporre che, se si tratta di NTFS su cui si sta lavorando, l'utilità di junction sarà probabilmente presente.

Ma, se vuoi dire che preferiresti non chiamare un programma esterno, ho trovato che il materiale ctypes ha un valore inestimabile. Permette di chiamare DLL Windows direttamente da Python. E sono abbastanza sicuro che sia nelle versioni standard di Python al giorno d'oggi.

Dovresti solo capire quale DLL di Windows è la chiamata all'API CreateJunction() (o qualsiasi altra chiavetta di Windows) e imposta i parametri e chiama. Buona fortuna, Microsoft non sembra supportarla molto bene. È potrebbe smontare il programma SysInternals junction o linkd o uno degli altri strumenti per scoprire come lo fanno.

Me, io sono abbastanza pigro, mi basta chiamare junction come un processo esterno :-)

+0

ctypes è incluso in Python dalla versione 2.5 in poi. –

+3

Il comando di giunzione non esiste su Vista e Win7. È stato sostituito da mklink. –

+0

Esiste come una giunzione di strumento Sysinternals. – Nux

8

è possibile utilizzare moduli Python API Win32 per esempio

import win32file 

win32file.CreateSymbolicLink(srcDir, targetDir, 1) 

vedere http://docs.activestate.com/activepython/2.5/pywin32/win32file__CreateSymbolicLink_meth.html per maggiori dettagli

se non si vuole fare affidamento anche su questo, si può sempre usare ctypes e chiamare direttamente CreateSymbolicLinl Win32 API, che è in ogni caso una semplice chiamata

qui è esempio chiamata utilizzando ctypes

import ctypes 

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

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

MSDN dice minima supportata client Windows Vista

012.
+2

Penso che le giunzioni fossero da Win2K in poi ma non ufficialmente (o bene) sostenuta dagli Stati membri, vista la scarsità di documenti su come farlo. I nuovi collegamenti simbolici sono molto più interessanti, soprattutto perché puoi applicarli ai file e (credo) che ora possono attraversare le reti. – paxdiablo

+1

yes junctions sono sottoinsieme di collegamenti simbolici –

+7

Le giunzioni sono _non_ un sottoinsieme di collegamenti simbolici. Le giunzioni si applicano solo alle directory. Questa risposta non è corretta e crea un collegamento simbolico per i file (che funziona solo su Vista e sopra) piuttosto che un bivio per le directory (che funziona su NTFS in Windows 2000) e sopra. Sfortunatamente, non esiste un vero e semplice modo per farlo in Python. –

4

Da Python 3.5 esiste una funzione CreateJunction nel modulo _winapi.

import _winapi 
_winapi.CreateJunction(source, target) 
Problemi correlati