Commit 4515866d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.7-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - use after free fix in releasing multichannel interfaces

 - fixes for special file types (report char, block, FIFOs properly when
   created e.g. by NFS to Windows)

 - fixes for reporting various special file types and symlinks properly
   when using SMB1

* tag '6.7-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: introduce cifs_sfu_make_node()
  smb: client: set correct file type from NFS reparse points
  smb: client: introduce ->parse_reparse_point()
  smb: client: implement ->query_reparse_point() for SMB1
  cifs: fix use after free for iface while disabling secondary channels
parents 090472ed b0348e45
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -191,7 +191,13 @@ struct cifs_open_info_data {
		bool reparse_point;
		bool symlink;
	};
	__u32 reparse_tag;
	struct {
		__u32 tag;
		union {
			struct reparse_data_buffer *buf;
			struct reparse_posix_data *posix;
		};
	} reparse;
	char *symlink_target;
	union {
		struct smb2_file_all_info fi;
@@ -395,8 +401,7 @@ struct smb_version_operations {
			     struct cifs_tcon *tcon,
			     struct cifs_sb_info *cifs_sb,
			     const char *full_path,
			     char **target_path,
			     struct kvec *rsp_iov);
			     char **target_path);
	/* open a file for non-posix mounts */
	int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
		    void *buf);
@@ -551,6 +556,9 @@ struct smb_version_operations {
	bool (*is_status_io_timeout)(char *buf);
	/* Check for STATUS_NETWORK_NAME_DELETED */
	bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
	int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
				   struct kvec *rsp_iov,
				   struct cifs_open_info_data *data);
};

struct smb_version_values {
+2 −2
Original line number Diff line number Diff line
@@ -1356,7 +1356,7 @@ typedef struct smb_com_transaction_ioctl_rsp {
	__le32 DataDisplacement;
	__u8 SetupCount;	/* 1 */
	__le16 ReturnedDataLen;
	__u16 ByteCount;
	__le16 ByteCount;
} __attribute__((packed)) TRANSACT_IOCTL_RSP;

#define CIFS_ACL_OWNER 1
@@ -1509,7 +1509,7 @@ struct reparse_posix_data {
	__le16	ReparseDataLength;
	__u16	Reserved;
	__le64	InodeType; /* LNK, FIFO, CHR etc. */
	char	PathBuffer[];
	__u8	DataBuffer[];
} __attribute__((packed));

struct cifs_quota_data {
+13 −1
Original line number Diff line number Diff line
@@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
			const struct cifs_fid *fid);
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
				 struct cifs_fattr *fattr,
				 u32 tag);
				 struct cifs_open_info_data *data);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
			struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode,
@@ -458,6 +458,12 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
			struct cifs_tcon *tcon,
			const unsigned char *searchName, char **syminfo,
			const struct nls_table *nls_codepage, int remap);
extern int cifs_query_reparse_point(const unsigned int xid,
				    struct cifs_tcon *tcon,
				    struct cifs_sb_info *cifs_sb,
				    const char *full_path,
				    u32 *tag, struct kvec *rsp,
				    int *rsp_buftype);
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
			       __u16 fid, char **symlinkinfo,
			       const struct nls_table *nls_codepage);
@@ -659,6 +665,12 @@ void cifs_put_tcp_super(struct super_block *sb);
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
int parse_reparse_point(struct reparse_data_buffer *buf,
			u32 plen, struct cifs_sb_info *cifs_sb,
			bool unicode, struct cifs_open_info_data *data);
int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
		       struct dentry *dentry, struct cifs_tcon *tcon,
		       const char *full_path, umode_t mode, dev_t dev);

#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
+77 −116
Original line number Diff line number Diff line
@@ -2690,136 +2690,97 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
	return rc;
}

/*
 *	Recent Windows versions now create symlinks more frequently
 *	and they use the "reparse point" mechanism below.  We can of course
 *	do symlinks nicely to Samba and other servers which support the
 *	CIFS Unix Extensions and we can also do SFU symlinks and "client only"
 *	"MF" symlinks optionally, but for recent Windows we really need to
 *	reenable the code below and fix the cifs_symlink callers to handle this.
 *	In the interim this code has been moved to its own config option so
 *	it is not compiled in by default until callers fixed up and more tested.
 */
int
CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
		    __u16 fid, char **symlinkinfo,
		    const struct nls_table *nls_codepage)
int cifs_query_reparse_point(const unsigned int xid,
			     struct cifs_tcon *tcon,
			     struct cifs_sb_info *cifs_sb,
			     const char *full_path,
			     u32 *tag, struct kvec *rsp,
			     int *rsp_buftype)
{
	int rc = 0;
	int bytes_returned;
	struct smb_com_transaction_ioctl_req *pSMB;
	struct smb_com_transaction_ioctl_rsp *pSMBr;
	bool is_unicode;
	unsigned int sub_len;
	char *sub_start;
	struct reparse_symlink_data *reparse_buf;
	struct reparse_posix_data *posix_buf;
	struct cifs_open_parms oparms;
	TRANSACT_IOCTL_REQ *io_req = NULL;
	TRANSACT_IOCTL_RSP *io_rsp = NULL;
	struct cifs_fid fid;
	__u32 data_offset, data_count;
	char *end_of_smb;
	__u8 *start, *end;
	int io_rsp_len;
	int oplock = 0;
	int rc;

	cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);

	if (cap_unix(tcon->ses))
		return -EOPNOTSUPP;

	oparms = (struct cifs_open_parms) {
		.tcon = tcon,
		.cifs_sb = cifs_sb,
		.desired_access = FILE_READ_ATTRIBUTES,
		.create_options = cifs_create_options(cifs_sb,
						      OPEN_REPARSE_POINT),
		.disposition = FILE_OPEN,
		.path = full_path,
		.fid = &fid,
	};

	rc = CIFS_open(xid, &oparms, &oplock, NULL);
	if (rc)
		return rc;

	pSMB->TotalParameterCount = 0 ;
	pSMB->TotalDataCount = 0;
	pSMB->MaxParameterCount = cpu_to_le32(2);
	/* BB find exact data count max from sess structure BB */
	pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
	pSMB->MaxSetupCount = 4;
	pSMB->Reserved = 0;
	pSMB->ParameterOffset = 0;
	pSMB->DataCount = 0;
	pSMB->DataOffset = 0;
	pSMB->SetupCount = 4;
	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
	pSMB->ParameterCount = pSMB->TotalParameterCount;
	pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
	pSMB->IsFsctl = 1; /* FSCTL */
	pSMB->IsRootFlag = 0;
	pSMB->Fid = fid; /* file handle always le */
	pSMB->ByteCount = 0;
	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon,
		      (void **)&io_req, (void **)&io_rsp);
	if (rc)
		goto error;

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
		goto qreparse_out;
	}
	io_req->TotalParameterCount = 0;
	io_req->TotalDataCount = 0;
	io_req->MaxParameterCount = cpu_to_le32(2);
	/* BB find exact data count max from sess structure BB */
	io_req->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
	io_req->MaxSetupCount = 4;
	io_req->Reserved = 0;
	io_req->ParameterOffset = 0;
	io_req->DataCount = 0;
	io_req->DataOffset = 0;
	io_req->SetupCount = 4;
	io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
	io_req->ParameterCount = io_req->TotalParameterCount;
	io_req->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
	io_req->IsFsctl = 1;
	io_req->IsRootFlag = 0;
	io_req->Fid = fid.netfid;
	io_req->ByteCount = 0;

	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)io_req,
			 (struct smb_hdr *)io_rsp, &io_rsp_len, 0);
	if (rc)
		goto error;

	data_offset = le32_to_cpu(pSMBr->DataOffset);
	data_count = le32_to_cpu(pSMBr->DataCount);
	if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
		/* BB also check enough total bytes returned */
		rc = -EIO;	/* bad smb */
		goto qreparse_out;
	}
	if (!data_count || (data_count > 2048)) {
		rc = -EIO;
		cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
		goto qreparse_out;
	}
	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
	reparse_buf = (struct reparse_symlink_data *)
				((char *)&pSMBr->hdr.Protocol + data_offset);
	if ((char *)reparse_buf >= end_of_smb) {
	data_offset = le32_to_cpu(io_rsp->DataOffset);
	data_count = le32_to_cpu(io_rsp->DataCount);
	if (get_bcc(&io_rsp->hdr) < 2 || data_offset > 512 ||
	    !data_count || data_count > 2048) {
		rc = -EIO;
		goto qreparse_out;
		goto error;
	}
	if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) {
		cifs_dbg(FYI, "NFS style reparse tag\n");
		posix_buf =  (struct reparse_posix_data *)reparse_buf;

		if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) {
			cifs_dbg(FYI, "unsupported file type 0x%llx\n",
				 le64_to_cpu(posix_buf->InodeType));
			rc = -EOPNOTSUPP;
			goto qreparse_out;
		}
		is_unicode = true;
		sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
		if (posix_buf->PathBuffer + sub_len > end_of_smb) {
			cifs_dbg(FYI, "reparse buf beyond SMB\n");
	end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount;
	start = (__u8 *)&io_rsp->hdr.Protocol + data_offset;
	if (start >= end) {
		rc = -EIO;
			goto qreparse_out;
		goto error;
	}
		*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
				sub_len, is_unicode, nls_codepage);
		goto qreparse_out;
	} else if (reparse_buf->ReparseTag !=
			cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
		rc = -EOPNOTSUPP;
		goto qreparse_out;
	}

	/* Reparse tag is NTFS symlink */
	sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) +
				reparse_buf->PathBuffer;
	sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
	if (sub_start + sub_len > end_of_smb) {
		cifs_dbg(FYI, "reparse buf beyond SMB\n");
		rc = -EIO;
		goto qreparse_out;
	}
	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
		is_unicode = true;
	else
		is_unicode = false;

	/* BB FIXME investigate remapping reserved chars here */
	*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
					       nls_codepage);
	if (!*symlinkinfo)
		rc = -ENOMEM;
qreparse_out:
	cifs_buf_release(pSMB);
	*tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag);
	rsp->iov_base = io_rsp;
	rsp->iov_len = io_rsp_len;
	*rsp_buftype = CIFS_LARGE_BUFFER;
	CIFSSMBClose(xid, tcon, fid.netfid);
	return 0;

	/*
	 * Note: On -EAGAIN error only caller can retry on handle based calls
	 * since file handle passed in no longer valid.
	 */
error:
	cifs_buf_release(io_req);
	CIFSSMBClose(xid, tcon, fid.netfid);
	return rc;
}

+60 −14
Original line number Diff line number Diff line
@@ -459,8 +459,7 @@ static int cifs_get_unix_fattr(const unsigned char *full_path,
			return -EOPNOTSUPP;
		rc = server->ops->query_symlink(xid, tcon,
						cifs_sb, full_path,
						&fattr->cf_symlink_target,
						NULL);
						&fattr->cf_symlink_target);
		cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
	}
	return rc;
@@ -722,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
		fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
}

static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
{
	u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);

	return MKDEV(v >> 32, v & 0xffffffff);
}

bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
				 struct cifs_fattr *fattr,
				 u32 tag)
				 struct cifs_open_info_data *data)
{
	struct reparse_posix_data *buf = data->reparse.posix;
	u32 tag = data->reparse.tag;

	if (tag == IO_REPARSE_TAG_NFS && buf) {
		switch (le64_to_cpu(buf->InodeType)) {
		case NFS_SPECFILE_CHR:
			fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_CHR;
			fattr->cf_rdev = nfs_mkdev(buf);
			break;
		case NFS_SPECFILE_BLK:
			fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_BLK;
			fattr->cf_rdev = nfs_mkdev(buf);
			break;
		case NFS_SPECFILE_FIFO:
			fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_FIFO;
			break;
		case NFS_SPECFILE_SOCK:
			fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_SOCK;
			break;
		case NFS_SPECFILE_LNK:
			fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_LNK;
			break;
		default:
			WARN_ON_ONCE(1);
			return false;
		}
		return true;
	}

	switch (tag) {
	case IO_REPARSE_TAG_LX_SYMLINK:
		fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
@@ -791,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);

	if (cifs_open_data_reparse(data) &&
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
		goto out_reparse;

	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
@@ -856,7 +896,7 @@ cifs_get_file_info(struct file *filp)
		data.adjust_tz = false;
		if (data.symlink_target) {
			data.symlink = true;
			data.reparse_tag = IO_REPARSE_TAG_SYMLINK;
			data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
		}
		cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
		break;
@@ -1025,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
	struct kvec rsp_iov, *iov = NULL;
	int rsp_buftype = CIFS_NO_BUFFER;
	u32 tag = data->reparse_tag;
	u32 tag = data->reparse.tag;
	int rc = 0;

	if (!tag && server->ops->query_reparse_point) {
@@ -1035,22 +1075,28 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
		if (!rc)
			iov = &rsp_iov;
	}
	switch ((data->reparse_tag = tag)) {

	rc = -EOPNOTSUPP;
	switch ((data->reparse.tag = tag)) {
	case 0: /* SMB1 symlink */
		iov = NULL;
		fallthrough;
	case IO_REPARSE_TAG_NFS:
	case IO_REPARSE_TAG_SYMLINK:
		if (!data->symlink_target && server->ops->query_symlink) {
		if (server->ops->query_symlink) {
			rc = server->ops->query_symlink(xid, tcon,
							cifs_sb, full_path,
							&data->symlink_target,
							iov);
							&data->symlink_target);
		}
		break;
	case IO_REPARSE_TAG_MOUNT_POINT:
		cifs_create_junction_fattr(fattr, sb);
		rc = 0;
		goto out;
	default:
		if (data->symlink_target) {
			rc = 0;
		} else if (server->ops->parse_reparse_point) {
			rc = server->ops->parse_reparse_point(cifs_sb,
							      iov, data);
		}
		break;
	}

	cifs_open_info_to_fattr(fattr, data, sb);
Loading