Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b244081 authored by Steve French's avatar Steve French
Browse files

Do not attempt to do cifs operations reading symlinks with SMB2



When use of symlinks is enabled (mounting with mfsymlinks option) to
non-Samba servers, we always tried to use cifs, even when we
were mounted with SMB2 or SMB3, which causes the server to drop the
network connection.

This patch separates out the protocol specific operations for cifs from
the code which recognizes symlinks, and fixes the problem where
with SMB2 mounts we attempt cifs operations to open and read
symlinks.  The next patch will add support for SMB2 for opening
and reading symlinks.  Additional followon patches will address
the similar problem creating symlinks.

Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent 057d6332
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -370,6 +370,9 @@ struct smb_version_operations {
	void (*generate_signingkey)(struct TCP_Server_Info *server);
	int (*calc_signature)(struct smb_rqst *rqst,
				   struct TCP_Server_Info *server);
	int (*query_mf_symlink)(const unsigned char *path, char *pbuf,
			unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
			unsigned int xid);
};

struct smb_version_values {
+3 −1
Original line number Diff line number Diff line
@@ -497,5 +497,7 @@ void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
						work_func_t complete);
void cifs_writedata_release(struct kref *refcount);

int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
			unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
			unsigned int xid);
#endif			/* _CIFSPROTO_H */
+53 −31
Original line number Diff line number Diff line
@@ -305,67 +305,89 @@ CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
}

int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
		   const unsigned char *path,
		   struct cifs_sb_info *cifs_sb, unsigned int xid)
open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
			unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
			unsigned int xid)
{
	int rc;
	int oplock = 0;
	__u16 netfid = 0;
	struct tcon_link *tlink;
	struct cifs_tcon *pTcon;
	struct cifs_tcon *ptcon;
	struct cifs_io_parms io_parms;
	u8 *buf;
	char *pbuf;
	unsigned int bytes_read = 0;
	int buf_type = CIFS_NO_BUFFER;
	unsigned int link_len = 0;
	FILE_ALL_INFO file_info;

	if (!CIFSCouldBeMFSymlink(fattr))
		/* it's not a symlink */
		return 0;

	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
	pTcon = tlink_tcon(tlink);
	ptcon = tlink_tcon(tlink);

	rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
	rc = CIFSSMBOpen(xid, ptcon, path, FILE_OPEN, GENERIC_READ,
			 CREATE_NOT_DIR, &netfid, &oplock, &file_info,
			 cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags &
				CIFS_MOUNT_MAP_SPECIAL_CHR);
	if (rc != 0)
		goto out;
	if (rc != 0) {
		cifs_put_tlink(tlink);
		return rc;
	}

	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
		CIFSSMBClose(xid, pTcon, netfid);
		CIFSSMBClose(xid, ptcon, netfid);
		cifs_put_tlink(tlink);
		/* it's not a symlink */
		goto out;
		return rc;
	}

	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
	if (!buf) {
		rc = -ENOMEM;
		goto out;
	}
	pbuf = buf;
	io_parms.netfid = netfid;
	io_parms.pid = current->tgid;
	io_parms.tcon = pTcon;
	io_parms.tcon = ptcon;
	io_parms.offset = 0;
	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;

	rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
	CIFSSMBClose(xid, pTcon, netfid);
	if (rc != 0) {
		kfree(buf);
	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
	CIFSSMBClose(xid, ptcon, netfid);
	cifs_put_tlink(tlink);
	return rc;
}


int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
		   const unsigned char *path,
		   struct cifs_sb_info *cifs_sb, unsigned int xid)
{
	int rc = 0;
	u8 *buf = NULL;
	unsigned int link_len = 0;
	unsigned int bytes_read = 0;
	struct cifs_tcon *ptcon;

	if (!CIFSCouldBeMFSymlink(fattr))
		/* it's not a symlink */
		return 0;

	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
	if (!buf) {
		rc = -ENOMEM;
		goto out;
	}

	ptcon = tlink_tcon(cifs_sb_tlink(cifs_sb));
	if ((ptcon->ses) && (ptcon->ses->server->ops->query_mf_symlink))
		rc = ptcon->ses->server->ops->query_mf_symlink(path, buf,
						 &bytes_read, cifs_sb, xid);
	else
		goto out;

	if (rc != 0)
		goto out;

	if (bytes_read == 0) /* not a symlink */
		goto out;

	rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
	kfree(buf);
	if (rc == -EINVAL) {
		/* it's not a symlink */
		rc = 0;
@@ -381,7 +403,7 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr,
	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
	fattr->cf_dtype = DT_LNK;
out:
	cifs_put_tlink(tlink);
	kfree(buf);
	return rc;
}

+1 −0
Original line number Diff line number Diff line
@@ -944,6 +944,7 @@ struct smb_version_operations smb1_operations = {
	.mand_lock = cifs_mand_lock,
	.mand_unlock_range = cifs_unlock_range,
	.push_mand_locks = cifs_push_mandatory_locks,
	.query_mf_symlink = open_query_close_cifs_symlink,
};

struct smb_version_values smb1_values = {