Exemplo n.º 1
0
    def open(self, desired_access=None, shared_access=None, open_options=None):
        """Open file.

        :param desired_access: desired access
            (see e3.os.windows.native_api.Access)
        :type desired_access: int
        :param shared_access: sharing parameters
            (see e3.os.windows.native_api.Shared)
        :type shared_access: int
        :param open_options: open options
            (see e3.os.windows.native_api.OpenOptions)
        :type open_options: int
        """
        if desired_access is None:
            desired_access = self.desired_access
        if shared_access is None:
            shared_access = self.shared_access
        if open_options is None:
            open_options = self.open_options

        self.handle = HANDLE()
        status = NT.OpenFile(
            pointer(self.handle),
            desired_access,
            pointer(self.attr),
            pointer(self.io_status),
            shared_access,
            open_options,
        )
        if status < 0:
            raise NTException(
                status=status,
                message="cannot open file %s" % self.path,
                origin="NTFile.open",
            )
Exemplo n.º 2
0
    def uid(self):
        """Retrieve the ID of the file.

        On NTFS system we are sure that this ID is unique on the given volume

        :return: the uid
        :rtype: int
        ;raise: NTException
        """
        result = FileInfo.Internal()
        status = NT.QueryInformationFile(
            self.handle,
            pointer(self.io_status),
            pointer(result),
            sizeof(result),
            FileInfo.Internal.class_id,
        )
        if status < 0:  # defensive code
            # we should already have raised an error here when trying
            # to open the file
            raise NTException(status=status,
                              message="cannot find file uid",
                              origin="NTFile.uid")

        return result.index_number
Exemplo n.º 3
0
    def read_attributes(self):
        """Retrieve file basic information.

        It updates the basic_info attribute including timestamp information

        :raise: NTException
        """
        result = FileInfo.Basic()
        status = NT.QueryInformationFile(
            self.handle,
            pointer(self.io_status),
            pointer(result),
            sizeof(result),
            FileInfo.Basic.class_id,
        )
        if status < 0:  # defensive code
            # we should already have raised an error here when trying
            # to open the file
            raise NTException(
                status=status,
                message="cannot read attributes",
                origin="NTFile.read_attributes",
            )
        self.basic_info = result
        return result
Exemplo n.º 4
0
    def rename(self, filename: str, replace: bool = False) -> None:
        """Move file.

        :param filename: target location
        :param replace: if True replace the target file if it exists
        :raise: NTException
        """
        file_target = "\\??\\%s" % os.path.abspath(filename)
        target = file_target.encode("utf_16_le")
        s = "?PL%ss" % len(target)
        b = create_string_buffer(struct.calcsize(s))
        b.raw = struct.pack(s, replace, 0, len(target), target)

        set_infofile: Callable = NT.SetInformationFile  # type: ignore

        status = set_infofile(
            self.handle,
            pointer(self.io_status),
            b,
            struct.calcsize(s),
            FileInfo.Rename.class_id,
        )
        if status < 0:
            raise NTException(
                status=status,
                message=f"move of {self.path} to {filename} failed",
                origin="NTFile.rename",
            )
Exemplo n.º 5
0
    def rename(self, filename, replace=False):
        """Move file.

        :param filename: target location
        :type filename: unicode
        :param replace: if True replace the target file if it exists
        :type replace: bool
        :raise: NTException
        """
        target = "\\??\\%s" % os.path.abspath(filename)
        target = target.encode("utf_16_le")
        s = "?PL%ss" % len(target)
        b = create_string_buffer(struct.calcsize(s))
        b.raw = struct.pack(s, replace, 0, len(target), target)
        status = NT.SetInformationFile(
            self.handle,
            pointer(self.io_status),
            b,
            struct.calcsize(s),
            FileInfo.Rename.class_id,
        )
        if status < 0:
            raise NTException(
                status=status,
                message="move of %s to %s failed" % (self.path, filename),
                origin="NTFile.rename",
            )
Exemplo n.º 6
0
    def volume_path(self):
        """Retrieve path to the volume containing the file.

        :return: path to a windows volume after junction resolution
        :rtype: unicode
        :raise: NTException
        """
        result = create_unicode_buffer(1024)
        status = NT.GetVolumePathName(c_wchar_p(self.path), result, 1024)
        if not status:
            raise NTException(status=status,
                              message="cannot find volume for %s" % self.path,
                              origin="NTFile.volume_path")
        return result.value
Exemplo n.º 7
0
    def dispose(self):
        """Remove the file (low level).

        The remove is effective on call to close method
        """
        fd = FileInfo.Disposition(1)
        status = NT.SetInformationFile(self.handle, pointer(self.io_status),
                                       pointer(fd),
                                       sizeof(FileInfo.Disposition),
                                       FileInfo.Disposition.class_id)
        if status < 0:
            raise NTException(status=status,
                              message="cannot dispose",
                              origin="NTFile.dispose")
Exemplo n.º 8
0
    def write_attributes(self):
        """Update file attributes.

        :raise: NTException
        """
        status = NT.SetInformationFile(self.handle, pointer(self.io_status),
                                       pointer(self.basic_info),
                                       sizeof(self.basic_info),
                                       FileInfo.Basic.class_id)
        if status < 0:
            raise NTException(status=status,
                              message='cannot write attributes to %s' %
                              self.path,
                              origin='NTFile.write_attributes')
        self.read_attributes()
Exemplo n.º 9
0
    def read_attributes(self):
        """Retrieve file basic information.

        It updates the basic_info attributes

        :raise: NTException
        """
        status = NT.QueryAttributesFile(pointer(self.attr),
                                        pointer(self.basic_info))
        logger.debug('read_attributes status: %d', status)
        if status < 0:
            raise NTException(status=status,
                              message="cannot query attributes %s" % self.path,
                              origin="NTFile.read_attributes")
        return self.basic_info
Exemplo n.º 10
0
    def read_attributes_internal(self):
        """Retrieve file basic attributes (internal function).

        The function is used internally to check file basic attributes
        (kind of entry and windows attributes such as readonly). Retrieved
        attributes are stored in basic_info Python attribute. It requires
        less rights than the read_attributes method.
        """
        status = NT.QueryAttributesFile(pointer(self.attr),
                                        pointer(self.basic_info))
        if status < 0:
            raise NTException(
                status=status,
                message="cannot query attributes %s" % self.path,
                origin="NTFile.read_attributes_internal",
            )
Exemplo n.º 11
0
    def iterate_on_dir(self, fun, default_result=None):
        """Iterate on directory.

        :param fun: function called on each entry (. are .. are skipped)
        :param default_result: default return value
        :return: last return value or fun or default_result
        """
        result = default_result
        s_size = struct.calcsize("LLL")
        b_size = 100 * 1024
        b = create_string_buffer(b_size)
        status = NT.QueryDirectoryFile(self.handle, None, None, None,
                                       pointer(self.io_status), b, b_size,
                                       FileInfo.Names.class_id, False, None,
                                       True)
        if status == Status.NO_MORE_FILES:  # defensive code
            # In theory this case should not occurs at it means that the
            # directory does not even have the . and .. entries. In practice
            # it can occurs (probably because of an intermediate state).
            # In that case behave as if the directory is empty
            return result

        if status < 0:
            raise NTException(status=status,
                              message="can't read dir %s" % self.path,
                              origin="NTFile.iterate_on_dir")

        while status >= 0 and status != Status.NO_MORE_FILES:
            pos = 0
            while True:
                off, _, size = struct.unpack_from("LLL", b.raw, pos)
                name = b.raw[pos + s_size:pos + s_size +
                             size].decode('utf-16-le')
                if name != u"." and name != "..":
                    result, should_exit = fun(name, self)
                    if should_exit:
                        return result

                if off == 0:
                    break
                pos += off

            status = NT.QueryDirectoryFile(self.handle, None, None, None,
                                           pointer(self.io_status), b, b_size,
                                           FileInfo.Names.class_id, False,
                                           None, False)
        return result
Exemplo n.º 12
0
    def volume_path(self) -> str:
        """Retrieve path to the volume containing the file.

        :return: path to a windows volume after junction resolution
        :raise: NTException
        """
        result = create_unicode_buffer(1024)

        get_vol_name: Callable = NT.GetVolumePathName  # type: ignore
        status = get_vol_name(c_wchar_p(self.path), result, 1024)
        if not status:
            raise NTException(
                status=status,
                message=f"cannot find volume for {self.path}",
                origin="NTFile.volume_path",
            )
        return result.value
Exemplo n.º 13
0
    def iterate_on_dir(self, fun, default_result=None):
        """Iterate on directory.

        :param fun: function called on each entry (. are .. are skipped)
        :param default_result: default return value
        :return: last return value or fun or default_result
        """
        result = default_result
        s_size = struct.calcsize("LLL")
        b_size = 100 * 1024
        b = create_string_buffer(b_size)
        status = NT.QueryDirectoryFile(self.handle, None, None, None,
                                       pointer(self.io_status), b, b_size,
                                       FileInfo.Names.class_id, False, None,
                                       True)
        if status == Status.NO_MORE_FILES:
            return result

        if status < 0:
            raise NTException(status=status,
                              message="can't read dir %s" % self.path,
                              origin="NTFile.iterate_on_dir")

        while status >= 0 and status != Status.NO_MORE_FILES:
            pos = 0
            while True:
                off, _, size = struct.unpack_from("LLL", b.raw, pos)
                name = b.raw[pos + s_size:pos + s_size +
                             size].decode('utf-16-le')
                if name != u"." and name != "..":
                    result, should_exit = fun(name, self)
                    if should_exit:
                        return result

                if off == 0:
                    break
                pos += off

            status = NT.QueryDirectoryFile(self.handle, None, None, None,
                                           pointer(self.io_status), b, b_size,
                                           FileInfo.Names.class_id, False,
                                           None, False)
        return result
Exemplo n.º 14
0
    def uid(self):
        """Retrieve the ID of the file.

        On NTFS system we are sure that this ID is unique on the given volume

        :return: the uid
        :rtype: int
        ;raise: NTException
        """
        result = FileInfo.Internal()
        status = NT.QueryInformationFile(self.handle, pointer(self.io_status),
                                         pointer(result), sizeof(result),
                                         FileInfo.Internal.class_id)
        if status < 0:
            raise NTException(status=status,
                              message='cannot find file uid',
                              origin="NTFile.uid")

        return result.index_number
Exemplo n.º 15
0
    def write_attributes(self) -> None:
        """Update file attributes.

        :raise: NTException
        """
        set_infofile: Callable = NT.SetInformationFile  # type: ignore
        status = set_infofile(
            self.handle,
            pointer(self.io_status),
            pointer(self.basic_info),
            sizeof(self.basic_info),
            FileInfo.Basic.class_id,
        )
        if status < 0:
            raise NTException(
                status=status,
                message=f"cannot write attributes to {self.path}",
                origin="NTFile.write_attributes",
            )
        self.read_attributes()
Exemplo n.º 16
0
    def open(
        self,
        desired_access: Optional[int] = None,
        shared_access: Optional[int] = None,
        open_options: Optional[int] = None,
    ) -> None:
        """Open file.

        :param desired_access: desired access
            (see e3.os.windows.native_api.Access)
        :param shared_access: sharing parameters
            (see e3.os.windows.native_api.Shared)
        :param open_options: open options
            (see e3.os.windows.native_api.OpenOptions)
        """
        if desired_access is None:
            desired_access = self.desired_access
        if shared_access is None:
            shared_access = self.shared_access
        if open_options is None:
            open_options = self.open_options

        self.handle = HANDLE()

        open_file: Callable = NT.OpenFile  # type: ignore

        status = open_file(
            pointer(self.handle),
            desired_access,
            pointer(self.attr),
            pointer(self.io_status),
            shared_access,
            open_options,
        )
        if status < 0:
            raise NTException(
                status=status,
                message=f"cannot open file {self.path}",
                origin="NTFile.open",
            )
Exemplo n.º 17
0
    def unlink(self):
        """Remove file safely.

        :raise: NTException
        """
        open_options = self.open_options
        is_in_trash = False

        # First we need to check that file is not mark readonly
        try:
            self.read_attributes_internal()
        except NTException as e:
            if e.status == Status.OBJECT_NAME_NOT_FOUND:
                return
            elif e.status == Status.DELETE_PENDING:
                return
            else:
                raise

        if self.is_readonly:
            # Try to remove the readonly flag
            self.basic_info.file_attributes.attr &= ~FileAttribute.READONLY
            self.write_attributes()

        # set our access modes
        desired_access = Access.DELETE
        shared_access = Share.DELETE
        if self.is_dir:
            desired_access |= Access.LIST_DIRECTORY | Access.SYNCHRONIZE
            open_options |= OpenOptions.SYNCHRONOUS_IO_NON_ALERT

        try_counter = 10
        # Open the file for deletion
        while try_counter > 0:
            try:
                self.open(desired_access, shared_access, open_options)
                break
            except NTException as e:
                if e.status == Status.SHARING_VIOLATION:
                    # File is already open elsewhere for a non delete operation
                    # Try a few times to open it with relaxed share settings
                    shared_access = Share.ALL
                elif e.status == Status.DELETE_PENDING:  # defensive code
                    # file is already pending deletion (just after our call
                    # to read_attributes) so consider the deletion
                    # is done and return
                    return
                else:  # defensive code
                    # We don't know what to do here so just fail
                    raise

            # Wait a few ms before attempting again to open the file
            NT.Sleep(5)
            try_counter -= 1

        if try_counter == 0:
            raise NTException(
                status=1,
                message="cannot open file %s for deletion" % self.path,
                origin="NTFile.unlink",
            )

        # From there we assume that the file has been opened
        try:
            if shared_access == Share.ALL:
                # The file is also opened elsewhere for a non delete operation
                # In that case we will try to move it to the trash
                # first check that the directory is empty
                if self.is_dir and not self.is_dir_empty:
                    raise NTException(
                        status=1,
                        message="directory not empty: %s" % self.path,
                        origin="NTFile.unlink",
                    )

                self.move_to_trash()
                is_in_trash = True

            # If the file has been moved away then we try to delete it in the
            # trash but it is not necessary to try as hard as the goal is to
            # just keep the trash space used as low as possible.
            if is_in_trash:
                try_counter = 5
            else:
                try_counter = 20

            while try_counter > 0:
                try:
                    self.dispose()
                    break
                except NTException as e:
                    if e.status == Status.DIRECTORY_NOT_EMPTY:
                        # The directory is not empty but that might be because
                        # of remaining files in PENDING_DELETE status. Our
                        # is_dir_empty method return empty if the directory
                        # contains only files pending for deletion
                        if not self.is_dir_empty:
                            raise NTException(
                                status=e.status,
                                message="dir %s is not empty" % self.path,
                                origin="NTFile.unlink",
                            )
                    elif e.status == Status.CANNOT_DELETE:  # defensive code
                        # At this stage we are sure that the file is not
                        # read_only but it seems that we can get this error
                        # when the file has been mapped to memory.
                        if not is_in_trash:
                            try:
                                self.move_to_trash()
                                is_in_trash = True
                                try_counter = min(try_counter, 5)
                            except NTException:
                                pass
                    else:  # defensive code
                        # Unknown error. If the file has been moved away
                        # consider it success. Otherwise reraise exception
                        if is_in_trash:
                            break
                        raise

                NT.Sleep(5)
                try_counter -= 1

        finally:
            self.close()