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)
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__()
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