def demo():
    """
    Definition of buffer used with FSCTL_TXFS_CREATE_MINIVERSION:
    typedef struct _TXFS_CREATE_MINIVERSION_INFO{
        USHORT StructureVersion;
        USHORT StructureLength;
        ULONG BaseVersion;
        USHORT MiniVersion;}
    """
    buf_fmt='HHLH0L'   ## buffer size must include struct padding
    buf_size=struct.calcsize(buf_fmt)

    tempdir=win32api.GetTempPath()
    tempfile=win32api.GetTempFileName(tempdir,'cft')[0]
    print("Demonstrating transactions on tempfile", tempfile)
    f=open(tempfile,'w')
    f.write('This is original file.\n')
    f.close()

    trans=win32transaction.CreateTransaction(Description='Test creating miniversions of a file')
    hfile=win32file.CreateFileW(tempfile, win32con.GENERIC_READ|win32con.GENERIC_WRITE,
        win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,
        None, win32con.OPEN_EXISTING, 0 , None, Transaction=trans)

    win32file.WriteFile(hfile, str2bytes('This is first miniversion.\n'))
    buf=win32file.DeviceIoControl(hfile, winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION,None,buf_size,None)
    struct_ver, struct_len, base_ver, ver_1=struct.unpack(buf_fmt, buf)

    win32file.SetFilePointer(hfile, 0, win32con.FILE_BEGIN)
    win32file.WriteFile(hfile, str2bytes('This is second miniversion!\n'))
    buf=win32file.DeviceIoControl(hfile, winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION,None,buf_size,None)
    struct_ver, struct_len, base_ver, ver_2=struct.unpack(buf_fmt, buf)
    hfile.Close()

    ## miniversions can't be opened with write access
    hfile_0=win32file.CreateFileW(tempfile, win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,
        None, win32con.OPEN_EXISTING, 0 , None, Transaction=trans, MiniVersion=base_ver)
    print('version:',base_ver,win32file.ReadFile(hfile_0, 100))
    hfile_0.Close()

    hfile_1=win32file.CreateFileW(tempfile, win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,
        None, win32con.OPEN_EXISTING, 0 , None, Transaction=trans, MiniVersion=ver_1)
    print('version:',ver_1,win32file.ReadFile(hfile_1, 100))
    hfile_1.Close()

    hfile_2=win32file.CreateFileW(tempfile, win32con.GENERIC_READ,
        win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,
        None, win32con.OPEN_EXISTING, 0 , None, Transaction=trans, MiniVersion=ver_2)
    print('version:',ver_2,win32file.ReadFile(hfile_2, 100))
    hfile_2.Close()

    ## MiniVersions are destroyed when transaction is committed or rolled back
    win32transaction.CommitTransaction(trans)

    os.unlink(tempfile)
Example #2
0
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

  try:
    # Open the file correctly depending on the string type.
    if type(fpath) == unicode:
      handle = win32file.CreateFileW(fpath, win32file.GENERIC_READ, 0, None, win32file.OPEN_EXISTING, win32file.FILE_FLAG_OPEN_REPARSE_POINT | win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
    else:
      handle = win32file.CreateFile(fpath, win32file.GENERIC_READ, 0, None, win32file.OPEN_EXISTING, win32file.FILE_FLAG_OPEN_REPARSE_POINT | win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)

    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
    buffer = win32file.DeviceIoControl(handle, winioctlcon.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.
    win32file.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
  except pywintypes.error, e:
    raise OSError(e.winerror, e.strerror, fpath)
Example #3
0
        def __init__(self, path, flags):
            self.flags = flags

            self.handle = None
            self.event = None

            try:
                self.handle = win32file.CreateFileW(
                    path,
                    0x0001,  # FILE_LIST_DIRECTORY
                    win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
                    None,
                    win32con.OPEN_EXISTING,
                    win32con.FILE_FLAG_BACKUP_SEMANTICS
                    | win32con.FILE_FLAG_OVERLAPPED,
                    None)

                self.buffer = win32file.AllocateReadBuffer(8192)
                self.event = win32event.CreateEvent(None, True, False, None)
                self.overlapped = pywintypes.OVERLAPPED()
                self.overlapped.hEvent = self.event
                self._start()
            except:
                self.close()
                raise
Example #4
0
 def __init__(self, path_to_watch, modified_queue):
     Thread.__init__(self, name='TreeWatcher')
     self.modified_queue = modified_queue
     self.path_to_watch = path_to_watch
     self.dir_handle = win32file.CreateFileW(
         path_to_watch, FILE_LIST_DIRECTORY, win32con.FILE_SHARE_READ
         | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE, None,
         win32con.OPEN_EXISTING, win32con.FILE_FLAG_BACKUP_SEMANTICS,
         None)
Example #5
0
def windows_nlinks(path):
    import win32file
    dwFlagsAndAttributes = win32file.FILE_FLAG_BACKUP_SEMANTICS if os.path.isdir(path) else 0
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    handle = win32file.CreateFileW(path, win32file.GENERIC_READ, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, dwFlagsAndAttributes, None)
    try:
        return win32file.GetFileInformationByHandle(handle)[7]
    finally:
        handle.Close()
Example #6
0
    def open(file, flags, mode=0o777, share_flags=None):  # pylint: disable=redefined-builtin,too-many-branches
        """Replacement for os.open() allowing moving or unlinking before closing."""
        if isinstance(file, bytes):
            file = file.decode("mbcs")

        if u"\0" in file:
            raise ValueError("embedded null character in path")

        if not isinstance(flags, int) and flags >= 0:
            raise ValueError("invalid flags: %r" % (flags, ))

        if not isinstance(mode, int) and mode >= 0:
            raise ValueError("invalid mode: %r" % (mode, ))

        if share_flags is None:
            share_flags = winnan.flags.FILE_SHARE_VALID_FLAGS

        if share_flags & ~winnan.flags.FILE_SHARE_VALID_FLAGS:
            raise ValueError("invalid share_flags: %r" % (share_flags, ))

        access_flags = _ACCESS_MAP[flags & _ACCESS_MASK]
        create_flags = _CREATE_MAP[flags & _CREATE_MASK]
        attrib_flags = win32file.FILE_ATTRIBUTE_NORMAL

        if flags & os.O_CREAT and mode & stat.S_IWRITE == 0:
            attrib_flags = win32file.FILE_ATTRIBUTE_READONLY

        if flags & os.O_TEMPORARY:  # pylint: disable=no-member
            share_flags |= win32file.FILE_SHARE_DELETE
            attrib_flags |= win32file.FILE_FLAG_DELETE_ON_CLOSE
            access_flags |= ntsecuritycon.DELETE

        if flags & os.O_SHORT_LIVED:  # pylint: disable=no-member
            attrib_flags |= win32file.FILE_ATTRIBUTE_TEMPORARY

        if flags & os.O_SEQUENTIAL:  # pylint: disable=no-member
            attrib_flags |= win32file.FILE_FLAG_SEQUENTIAL_SCAN

        if flags & os.O_RANDOM:  # pylint: disable=no-member
            attrib_flags |= win32file.FILE_FLAG_RANDOM_ACCESS

        try:
            handle = win32file.CreateFileW(file, access_flags, share_flags,
                                           None, create_flags, attrib_flags,
                                           None)
        except win32file.error as err:
            if err.winerror == winerror.ERROR_FILE_EXISTS:
                raise FileExistsError(errno.EEXIST, "File exists", file)

            raise

        return msvcrt.open_osfhandle(handle.Detach(),
                                     flags | winnan.flags.O_NOINHERIT)
Example #7
0
    def run(self):
        hDir = win32file.CreateFileW(
            self._watch_path,
            FILE_LIST_DIRECTORY,
            win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
            None,
            win32con.OPEN_EXISTING,
            win32con.FILE_FLAG_BACKUP_SEMANTICS |
            win32con.FILE_FLAG_OVERLAPPED,
            None
        )
        while self._windows_sucks_flag:
            buf = win32file.AllocateReadBuffer(1024)
            win32file.ReadDirectoryChangesW(
                hDir,
                buf,
                True,
                win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
                win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
                win32con.FILE_NOTIFY_CHANGE_SIZE |
                win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
                self._overlapped
            )
            result_stack = {}
            rc = win32event.WaitForMultipleObjects((self._wait_stop,
                                                    self._overlapped.hEvent),
                                                   0, win32event.INFINITE)
            if rc == win32event.WAIT_OBJECT_0:
                # Stop event
                break

            data = win32file.GetOverlappedResult(hDir, self._overlapped, True)
            # lets read the data and store it in the results
            results = win32file.FILE_NOTIFY_INFORMATION(buf, data)

            for action, afile in results:
                if action in ACTIONS:
                    full_filename = os.path.join(self._watch_path, afile)
                    result_stack.setdefault(full_filename,
                                            []).append(ACTIONS.get(action))
            keys = list(result_stack.keys())
            while len(keys):
                key = keys.pop(0)
                event = result_stack.pop(key)
                if (ADDED in event) and (DELETED in event):
                    event = [e for e in event if e not in (ADDED, DELETED)]
                noticed = []
                for each_event in event:
                    if each_event not in noticed:
                        self._callback(each_event, full_filename)
                        noticed.append(each_event)
Example #8
0
 def change_created_time(self, filepath: Path, d_ctime: datetime) -> None:
     """Change the created time of a given file."""
     winfile = win32file.CreateFileW(
         str(filepath),
         win32con.GENERIC_WRITE,
         (win32con.FILE_SHARE_READ
          | win32con.FILE_SHARE_WRITE
          | win32con.FILE_SHARE_DELETE),
         None,
         win32con.OPEN_EXISTING,
         win32con.FILE_ATTRIBUTE_NORMAL,
         None,
     )
     win32file.SetFileTime(winfile, d_ctime)
Example #9
0
def windows_get_size(path):
    ''' On windows file sizes are only accurately stored in the actual file,
    not in the directory entry (which could be out of date). So we open the
    file, and get the actual size. '''
    import win32file
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    h = win32file.CreateFileW(
        path, 0, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.FILE_SHARE_DELETE,
        None, win32file.OPEN_EXISTING, 0, None)
    try:
        return win32file.GetFileSize(h)
    finally:
        win32file.CloseHandle(h)
Example #10
0
    def change_file_date(self,
                         filepath: Path,
                         mtime: str = None,
                         ctime: str = None) -> None:
        """
        Change the FS modification and creation dates of a file.

        Since there is no creation time on GNU/Linux, the ctime
        will not be taken into account if running on this platform.

        :param filename: The file to modify
        :param mtime: The modification time
        :param ctime: The creation time
        """
        filepath = safe_long_path(filepath)

        log.debug(
            f"Setting file dates for {filepath!r} (ctime={ctime!r}, mtime={mtime!r})"
        )

        # Set the creation time first as on macOS using touch will change ctime and mtime.
        # The modification time will be updated just after, if needed.
        if ctime:
            d_ctime = datetime.strptime(str(ctime), "%Y-%m-%d %H:%M:%S")

            if MAC:
                cmd = [
                    "touch", "-mt",
                    d_ctime.strftime("%Y%m%d%H%M.%S"),
                    str(filepath)
                ]
                subprocess.check_call(cmd)
            elif WINDOWS:
                winfile = win32file.CreateFileW(
                    str(filepath),
                    win32con.GENERIC_WRITE,
                    (win32con.FILE_SHARE_READ
                     | win32con.FILE_SHARE_WRITE
                     | win32con.FILE_SHARE_DELETE),
                    None,
                    win32con.OPEN_EXISTING,
                    win32con.FILE_ATTRIBUTE_NORMAL,
                    None,
                )
                win32file.SetFileTime(winfile, d_ctime)

        if mtime:
            d_mtime = mktime(strptime(str(mtime), "%Y-%m-%d %H:%M:%S"))
            os.utime(filepath, (d_mtime, d_mtime))
Example #11
0
def windows_get_fileid(path):
    ''' The fileid uniquely identifies actual file contents (it is the same for
    all hardlinks to a file). Similar to inode number on linux. '''
    import win32file
    from pywintypes import error
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    try:
        h = win32file.CreateFileW(path, 0, 0, None, win32file.OPEN_EXISTING,
                win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
        try:
            data = win32file.GetFileInformationByHandle(h)
        finally:
            win32file.CloseHandle(h)
    except (error, EnvironmentError):
        return None
    return data[4], data[8], data[9]
Example #12
0
def windows_open(path):
    if isinstance(path, bytes):
        path = path.decode('mbcs')
    try:
        h = win32file.CreateFileW(
            path,
            win32file.GENERIC_READ |
            win32file.GENERIC_WRITE,  # Open for reading and writing
            0,  # Open exclusive
            None,  # No security attributes, ensures handle is not inherited by children
            win32file.OPEN_ALWAYS,  # If file does not exist, create it
            win32file.FILE_ATTRIBUTE_NORMAL,  # Normal attributes
            None,  # No template file
        )
    except pywintypes.error as err:
        raise WindowsError(err[0], err[2], path)
    fd = msvcrt.open_osfhandle(h.Detach(), 0)
    return os.fdopen(fd, 'r+b')
Example #13
0
def old_windows_get_fileid(path):
    # we dont use this anymore as the win32 implementation reads the windows
    # registry to convert file times which is slow and breaks on systems with
    # registry issues.
    import win32file
    from pywintypes import error
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    try:
        h = win32file.CreateFileW(path, 0, 0, None, win32file.OPEN_EXISTING,
                                  win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
        try:
            data = win32file.GetFileInformationByHandle(h)
        finally:
            win32file.CloseHandle(h)
    except (error, EnvironmentError):
        return None
    return data[4], data[8], data[9]
Example #14
0
def _get_reparse_data(path):
    '''
    Retrieves the reparse point data structure for the given path.

    If the path is not a reparse point, None is returned.

    See http://msdn.microsoft.com/en-us/library/ff552012.aspx for details on the
    REPARSE_DATA_BUFFER structure returned.
    '''
    if sys.getwindowsversion().major < 6:
        raise SaltInvocationError(
            'Symlinks are only supported on Windows Vista or later.')

    # ensure paths are using the right slashes
    path = os.path.normpath(path)

    if not _is_reparse_point(path):
        return None

    fileHandle = None
    try:
        fileHandle = win32file.CreateFileW(
            path,
            0x80000000,  # GENERIC_READ
            1,  # share with other readers
            None,  # no inherit, default security descriptor
            3,  # OPEN_EXISTING
            0x00200000 |
            0x02000000  # FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS
        )

        reparseData = win32file.DeviceIoControl(
            fileHandle,
            0x900a8,  # FSCTL_GET_REPARSE_POINT
            None,  # in buffer
            16384  # out buffer size (MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
        )

    finally:
        if fileHandle:
            win32file.CloseHandle(fileHandle)

    return reparseData
Example #15
0
    def os_open(path, flags, mode=0o777, share_flags=FILE_SHARE_VALID_FLAGS):
        '''
        Replacement for os.open() allowing moving or unlinking before closing
        '''
        if not isinstance(flags, Integral):
            raise TypeError('flags must be an integer')
        if not isinstance(mode, Integral):
            raise TypeError('mode must be an integer')

        if share_flags & ~FILE_SHARE_VALID_FLAGS:
            raise ValueError('bad share_flags: %r' % share_flags)

        access_flags = _ACCESS_MAP[flags & _ACCESS_MASK]
        create_flags = _CREATE_MAP[flags & _CREATE_MASK]
        attrib_flags = FILE_ATTRIBUTE_NORMAL

        if flags & os.O_CREAT and mode & ~0o444 == 0:
            attrib_flags = FILE_ATTRIBUTE_READONLY

        if flags & os.O_TEMPORARY:
            share_flags |= FILE_SHARE_DELETE
            attrib_flags |= FILE_FLAG_DELETE_ON_CLOSE
            access_flags |= DELETE

        if flags & os.O_SHORT_LIVED:
            attrib_flags |= FILE_ATTRIBUTE_TEMPORARY

        if flags & os.O_SEQUENTIAL:
            attrib_flags |= FILE_FLAG_SEQUENTIAL_SCAN

        if flags & os.O_RANDOM:
            attrib_flags |= FILE_FLAG_RANDOM_ACCESS

        try:
            h = win32file.CreateFileW(path, access_flags, share_flags, None,
                                      create_flags, attrib_flags, None)
        except pywintypes.error as e:
            raise_winerror(e)
        ans = msvcrt.open_osfhandle(h, flags | os.O_NOINHERIT)
        h.Detach(
        )  # We dont want the handle to be automatically closed when h is deleted
        return ans
Example #16
0
    def get_read_handle(filename):
        if os.path.isdir(filename):
            dwFlagsAndAttributes = win32file.FILE_FLAG_BACKUP_SEMANTICS
        else:
            dwFlagsAndAttributes = 0

        # CreateFile(fileName, desiredAccess, shareMode, attributes,
        #            CreationDisposition, flagsAndAttributes, hTemplateFile)
        try:
            handle = win32file.CreateFileW(
                filename,  # with 'W' accepts unicode
                win32file.GENERIC_READ,
                win32file.FILE_SHARE_READ,
                None,
                win32file.OPEN_EXISTING,
                dwFlagsAndAttributes,
                None)
            return handle
        except Exception as e:
            raise PygcamException("get_read_handle(%s) failed: %s" %
                                  (filename, e))
def readlink(fpath):
    """
    Parses a symlink to get the target path.

    :param fpath: path to the symlink
    :type  fpath: basestring
    :return: target path
    :rtype: basestring
    """
    if not islink(fpath):
        return None

    # open the file
    handle = win32file.CreateFileW(fpath, win32file.GENERIC_READ, 0, None,
                                   win32file.OPEN_EXISTING,
                                   win32file.FILE_FLAG_OPEN_REPARSE_POINT, 0)

    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
    _buffer = win32file.DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None,
                                        16 * 1024)

    # above will return an ugly string (byte array), so we'll need to parse it
    win32file.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
Example #18
0
def set_file_time(path, creation_time, last_access_time, last_write_time):
    # First windows API call
    handle = win32file.CreateFileW(
        path,
        SYMBOLS.GENERIC_WRITE,
        SYMBOLS.FILE_SHARE_READ | SYMBOLS.FILE_SHARE_WRITE | SYMBOLS.FILE_SHARE_DELETE,
        None,
        SYMBOLS.OPEN_EXISTING,
        SYMBOLS.FILE_FLAG_OPEN_REPARSE_POINT | SYMBOLS.FILE_FLAG_BACKUP_SEMANTICS,
        0,
    )

    # Prepare arguments
    def prepare(x):
        if x == 0:
            return None
        if isinstance(x, int):
            from datetime import timezone

            x = datetime(year=x, month=1, day=1, tzinfo=timezone.utc)
        return x

    creation_time = prepare(creation_time)
    last_access_time = prepare(last_access_time)
    last_write_time = prepare(last_write_time)

    # Second windows API call
    assert handle != win32file.INVALID_HANDLE_VALUE
    win32file.SetFileTime(
        handle, creation_time, last_access_time, last_write_time,
    )

    # Close the handle
    # This is necessary since we use a single process
    # for running all the commands from a single test case
    win32file.CloseHandle(handle)
buf_size = struct.calcsize(buf_fmt)

tempdir = win32api.GetTempPath()
tempfile = win32api.GetTempFileName(tempdir, 'cft')[0]
print tempfile
f = open(tempfile, 'w')
f.write('This is original file.\n')
f.close()

trans = win32transaction.CreateTransaction(
    Description='Test creating miniversions of a file')
hfile = win32file.CreateFileW(tempfile,
                              win32con.GENERIC_READ | win32con.GENERIC_WRITE,
                              win32con.FILE_SHARE_READ
                              | win32con.FILE_SHARE_WRITE,
                              None,
                              win32con.OPEN_EXISTING,
                              0,
                              None,
                              Transaction=trans)

win32file.WriteFile(hfile, 'This is first miniversion.\n')
buf = win32file.DeviceIoControl(hfile,
                                winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION, '',
                                buf_size, None)
struct_ver, struct_len, base_ver, ver_1 = struct.unpack(buf_fmt, buf)

win32file.SetFilePointer(hfile, 0, win32con.FILE_BEGIN)
win32file.WriteFile(hfile, 'This is second miniversion!\n')
buf = win32file.DeviceIoControl(hfile,
                                winioctlcon.FSCTL_TXFS_CREATE_MINIVERSION, '',
Example #20
0
    def __init__(self, path):
        self.handle_map = {}

        import win32file, winerror
        from pywintypes import error
        from collections import defaultdict

        if isbytestring(path):
            path = path.decode(filesystem_encoding)

        if not os.path.exists(path):
            return

        names = os.listdir(path)
        name_to_fileid = {x:windows_get_fileid(os.path.join(path, x)) for x in names}
        fileid_to_names = defaultdict(set)
        for name, fileid in iteritems(name_to_fileid):
            fileid_to_names[fileid].add(name)

        for x in names:
            f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
            if not os.path.isfile(f):
                continue
            try:
                # Ensure the file is not read-only
                win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL)
            except:
                pass

            try:
                h = win32file.CreateFileW(f, win32file.GENERIC_READ,
                        win32file.FILE_SHARE_DELETE, None,
                        win32file.OPEN_EXISTING, win32file.FILE_FLAG_SEQUENTIAL_SCAN, 0)
            except error as e:
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    # The file could be a hardlink to an already opened file,
                    # in which case we use the same handle for both files
                    fileid = name_to_fileid[x]
                    found = False
                    if fileid is not None:
                        for other in fileid_to_names[fileid]:
                            other = os.path.normcase(os.path.abspath(os.path.join(path, other)))
                            if other in self.handle_map:
                                self.handle_map[f] = self.handle_map[other]
                                found = True
                                break
                    if found:
                        continue

                self.close_handles()
                if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
                    err = IOError(errno.EACCES,
                            _('File is open in another process'))
                    err.filename = f
                    raise err
                prints('CreateFile failed for: %r' % f)
                raise
            except:
                self.close_handles()
                prints('CreateFile failed for: %r' % f)
                raise
            self.handle_map[f] = h
Example #21
0
def readlink(path):
    '''
    Return the path that a symlink points to

    This is only supported on Windows Vista or later.

    Inline with *nix behaviour, this function will raise an error if the path is
    not a symlink, however, the error raised will be a SaltInvocationError, not
    an OSError.

    CLI Example:

    .. code-block:: bash

        salt '*' file.readlink /path/to/link
    '''
    if sys.getwindowsversion().major < 6:
        raise SaltInvocationError(
            'Symlinks are only supported on Windows Vista or later.')

    if not os.path.isabs(path):
        raise SaltInvocationError('Path to link must be absolute.')

    # ensure paths are using the right slashes
    path = os.path.normpath(path)

    if not _islink(path):
        raise SaltInvocationError('The path specified is not a symlink.')

    fileHandle = None
    try:
        fileHandle = win32file.CreateFileW(
            path,
            0x80000000,  # GENERIC_READ
            0,  # no sharing
            None,  # no inherit, default security descriptor
            3,  # OPEN_EXISTING
            0x00200000 |
            0x02000000  # FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS
        )

        reparseData = win32file.DeviceIoControl(
            fileHandle,
            0x900a8,  # FSCTL_GET_REPARSE_POINT
            None,  # in buffer
            16384  # out buffer size (MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
        )

    finally:
        if fileHandle:
            win32file.CloseHandle(fileHandle)

    # REPARSE_DATA_BUFFER structure - see
    # http://msdn.microsoft.com/en-us/library/ff552012.aspx

    # parse the structure header to work out which type of reparse point this is
    header_parser = struct.Struct('L')
    ReparseTag, = header_parser.unpack(reparseData[:header_parser.size])
    # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511.aspx
    if not ReparseTag & 0x0000FFFF == 0x0000000C:
        raise SaltInvocationError(
            'The path specified is not a symlink, but another type of reparse point ({0:x}).'
            .format(ReparseTag))

    # parse as a symlink reparse point structure (the structure for other
    # reparse points is different)
    data_parser = struct.Struct('LHHHHHHL')
    ReparseTag, ReparseDataLength, Reserved, SubstituteNameOffset, \
    SubstituteNameLength, PrintNameOffset, \
    PrintNameLength, Flags = data_parser.unpack(reparseData[:data_parser.size])

    path_buffer_offset = data_parser.size
    absolute_substitute_name_offset = path_buffer_offset + SubstituteNameOffset
    target_bytes = reparseData[
        absolute_substitute_name_offset:absolute_substitute_name_offset +
        SubstituteNameLength]
    target = target_bytes.decode('UTF-16')

    if target.startswith('\\??\\'):
        target = target[4:]
    # comes out in 8.3 form; convert it to LFN to make it look nicer
    target = win32file.GetLongPathName(target)

    return target