Python test for junction point target
Question: Under Windows with Python 2.7 is there a way to check if a folder is the
**target** of any junction points? And if so, find which symlink leads to it?

For example, in a dos shell create a junction point using mklink

    
    
    C:\>mklink /J C:\junction C:\Users
    Junction created for C:\junction <<===>> C:\Users
    

And in python (assuming no prior knowledge that this junction exists) test
"C:\Users" if it is the target of any junction points, returning a list of
symbolic links if True, in this case: ['C:\junction']


Answer: Here's something I put together using some of the code in an ActiveState
recipe titled [_Windows directory walk using
ctypes_](http://code.activestate.com/recipes/578629-windows-directory-walk-
using-ctypes/). There's probably a more direct way to do it than using the
win32 `FindFirstFile` and `FindNextFile` functions, but this seems to work in
my limited testing.

    
    
    import os
    import sys
    import ctypes
    from ctypes import Structure
    from ctypes import byref
    import ctypes.wintypes as wintypes
    from ctypes import addressof
    
    FILE_ATTRIBUTE_DIRECTORY = 16  # (0x10)
    FILE_ATTRIBUTE_REPARSE_POINT = 1024  # (0x400)
    MAX_PATH = 260
    
    GetLastError = ctypes.windll.kernel32.GetLastError
    
    class FILETIME(Structure):
        _fields_ = [("dwLowDateTime", wintypes.DWORD),
                    ("dwHighDateTime", wintypes.DWORD)]
    
    class WIN32_FIND_DATAW(Structure):
        _fields_ = [("dwFileAttributes", wintypes.DWORD),
                    ("ftCreationTime", FILETIME),
                    ("ftLastAccessTime", FILETIME),
                    ("ftLastWriteTime", FILETIME),
                    ("nFileSizeHigh", wintypes.DWORD),
                    ("nFileSizeLow", wintypes.DWORD),
                    ("dwReserved0", wintypes.DWORD),
                    ("dwReserved1", wintypes.DWORD),
                    ("cFileName", wintypes.WCHAR * MAX_PATH),
                    ("cAlternateFileName", wintypes.WCHAR * 20)]
    
    def find_junctions(folder):
        """ Return a list of subdirectories in folder which are junction points """
        if not os.path.isdir(folder):
            return False
        folder = unicode(folder)
        if not folder.startswith(u'\\\\?\\'):
            if folder.startswith(u'\\\\'):
                # network drive
                folder = u'\\\\?\\UNC' + folder[1:]
            else:
                # local drive
                folder = u'\\\\?\\' + folder
    
        junction_points = []
        data = WIN32_FIND_DATAW()
        h = ctypes.windll.kernel32.FindFirstFileW(os.path.join(folder, u'*'),
                                                  byref(data))
        last_error = ctypes.windll.kernel32.GetLastError()
        if h < 0:
            ctypes.windll.kernel32.FindClose(h)
            if not sys.stderr.isatty():
                print >> sys.stderr, ('Failed to find first file %s' %
                                       os.path.join(folder, u'*'))
            if last_error != 5:  # access denied.
                raise WindowsError('FindFirstFileW %s, Error: %d' %
                                   (folder, ctypes.windll.kernel32.GetLastError()))
            return []
    
        if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY and
            data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT):
            if data.cFileName not in (u'.', u'..'):
                junction_points.append(data.cFileName[:])
    
        try:
            while ctypes.windll.kernel32.FindNextFileW(h, byref(data)):
                if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY and
                    data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT):
                    if data.cFileName not in (u'.', u'..'):
                        junction_points.append(data.cFileName[:])
        except WindowsError as e:
            if not sys.stderr.isatty():
                print >> sys.stderr, (
                    'Failed to find next file %s, handle %d, buff addr: 0x%x' %
                    (os.path.join(folder, u'*'), h, addressof(data)))
    
        ctypes.windll.kernel32.FindClose(h)
        return junction_points
    
    def is_junction_point(folder):
        dirpath, folder = os.path.split(os.path.abspath(folder))
        return unicode(folder) in find_junctions(dirpath)
    

