Symlinks on windows? Symlinks on windows? python python

Symlinks on windows?


NTFS file system has junction points, I think you may use them instead, You can use python win32 API module for that e.g.

import win32filewin32file.CreateSymbolicLink(fileSrc, fileTarget, 1)

If you do not want to rely on win32API module, you can always use ctypes and directly call CreateSymbolicLink win32 API e.g.

import ctypeskdll = 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) says Minimum supported client is Windows Vista

In addition: This also works with directories (indicate that with the third argument). With unicode support it looks like this:

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

also seeCreate NTFS junction point in Python


os.symlink works on Python 3.3 using Windows 8.1 with an NTFS filesystem.


python ntfslink extension

Or if you want to use pywin32, you can use the previously stated method, and to read, 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 functionSYMBOLIC_LINK = 'symbolic'MOUNTPOINT = 'mountpoint'GENERIC = 'generic'def islink(fpath):    """ Windows islink implementation. """    if GetFileAttributes(fpath) & REPARSE_FOLDER == REPARSE_FOLDER:        return True    return Falsedef 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 bufferdef 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 rpathdef 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 fpathdef 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()

Adjust the attributes in the CreateFile to your needs, but for a normal situation, it should work. Feel free to improve on it.

It should also work for folder junctions if you use MOUNTPOINT instead of SYMBOLIC_LINK.

You may way to check that

sys.getwindowsversion()[0] >= 6

if you put this into something you're releasing, since this form of symbolic link is only supported on Vista+.