Ejemplo n.º 1
0
def _resolve_dfs(raw_io):
    """
    Resolves a DFS path for a failed Open request.

    :param raw_io: The SMBRawIO to resolve the DFS path for.
    :return: A new Open for each DFS target that was resolved.
    """
    if not raw_io.fd.tree_connect.is_dfs_share:
        return

    # Path is on a DFS root that is linked to another server.
    client_config = ClientConfig()
    raw_path = raw_io.name

    referral = dfs_request(raw_io.fd.tree_connect, raw_path[1:])
    client_config.cache_referral(referral)
    info = client_config.lookup_referral(
        [p for p in raw_path.split("\\") if p])
    connection_kwargs = getattr(raw_io, '_%s__kwargs' % type(raw_io).__name__,
                                {})

    for target in info:
        new_path = raw_path.replace(info.dfs_path, target.target_path, 1)

        try:
            tree, fd_path = get_smb_tree(new_path, **connection_kwargs)

        except SMBResponseException as link_exc:
            log.warning("Failed to connect to DFS link target %s: %s" %
                        (str(target), link_exc))
            continue

        # Record the target that worked for future reference.
        info.target_hint = target
        yield Open(tree, fd_path)
Ejemplo n.º 2
0
    def __init__(self,
                 path,
                 mode='r',
                 share_access=None,
                 desired_access=None,
                 file_attributes=None,
                 create_options=0,
                 buffer_size=MAX_PAYLOAD_SIZE,
                 **kwargs):
        tree, fd_path = get_smb_tree(path, **kwargs)
        self.share_access = share_access
        self.fd = Open(tree, fd_path)
        self._mode = mode
        self._name = path
        self._offset = 0
        self._flush = False
        self._buffer_size = buffer_size
        self.__kwargs = kwargs  # Used in open for DFS referrals

        if desired_access is None:
            desired_access = 0

            # While we can open a directory, the values for FilePipePrinterAccessMask also apply to Dirs so just use
            # the same enum to simplify code.
            if 'r' in self.mode or '+' in self.mode:
                desired_access |= FilePipePrinterAccessMask.FILE_READ_DATA | \
                                  FilePipePrinterAccessMask.FILE_READ_ATTRIBUTES | \
                                  FilePipePrinterAccessMask.FILE_READ_EA
            if 'w' in self.mode or 'x' in self.mode or 'a' in self.mode or '+' in self.mode:
                desired_access |= FilePipePrinterAccessMask.FILE_WRITE_DATA | \
                                  FilePipePrinterAccessMask.FILE_WRITE_ATTRIBUTES | \
                                  FilePipePrinterAccessMask.FILE_WRITE_EA
        self._desired_access = desired_access

        if file_attributes is None:
            file_attributes = FileAttributes.FILE_ATTRIBUTE_DIRECTORY if self.FILE_TYPE == 'dir' \
                else FileAttributes.FILE_ATTRIBUTE_NORMAL
        self._file_attributes = file_attributes

        self._create_options = create_options
        self._create_options |= {
            'dir': CreateOptions.FILE_DIRECTORY_FILE,
            'file': CreateOptions.FILE_NON_DIRECTORY_FILE,
        }.get(self.FILE_TYPE, 0)

        super(SMBRawIO, self).__init__()
Ejemplo n.º 3
0
    def open(self, transaction=None):
        if not self.closed:
            return

        share_access = _parse_share_access(self.share_access, self.mode)
        create_disposition = _parse_mode(self.mode, invalid=self._INVALID_MODE)

        try:
            # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wpo/feeb3122-cfe0-4b34-821d-e31c036d763c
            # Impersonation on SMB has little meaning when opening files but is important if using RPC so set to a sane
            # default of Impersonation.
            open_result = self.fd.create(
                ImpersonationLevel.Impersonation,
                self._desired_access,
                self._file_attributes,
                share_access,
                create_disposition,
                self._create_options,
                send=(transaction is None),
            )
        except (PathNotCovered, ObjectNameNotFound, ObjectPathNotFound) as exc:
            # The MS-DFSC docs status that STATUS_PATH_NOT_COVERED is used when encountering a link to a different
            # server but Samba seems to return the generic name or path not found.
            if not self.fd.tree_connect.is_dfs_share:
                raise SMBOSError(exc.status, self.name)

            # Path is on a DFS root that is linked to another server.
            client_config = ClientConfig()
            referral = dfs_request(self.fd.tree_connect, self.name[1:])
            client_config.cache_referral(referral)
            info = client_config.lookup_referral([p for p in self.name.split("\\") if p])

            for target in info:
                new_path = self.name.replace(info.dfs_path, target.target_path, 1)

                try:
                    tree, fd_path = get_smb_tree(new_path, **self.__kwargs)
                    self.fd = Open(tree, fd_path)
                    self.open(transaction=transaction)

                except SMBResponseException as link_exc:
                    log.warning("Failed to connect to DFS link target %s: %s" % (str(target), link_exc))

                else:
                    # Record the target that worked for future reference.
                    info.target_hint = target
                    break

            else:
                # None of the targets worked so raise the original error.
                raise SMBOSError(exc.status, self.name)

            return

        except SMBResponseException as exc:
            raise SMBOSError(exc.status, self.name)

        if transaction is not None:
            transaction += open_result
        elif 'a' in self.mode and self.FILE_TYPE != 'pipe':
            self._offset = self.fd.end_of_file