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

Commit 3ea4fcc5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "First set of CIFS/SMB3 fixes for the merge window. Also improves POSIX
  character mapping for SMB3"

* tag 'cifs-bug-fixes-for-4.13' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: fix circular locking dependency
  cifs: set oparms.create_options rather than or'ing in CREATE_OPEN_BACKUP_INTENT
  cifs: Do not modify mid entry after submitting I/O in cifs_call_async
  CIFS: add SFM mapping for 0x01-0x1F
  cifs: hide unused functions
  cifs: Use smb 2 - 3 and cifsacl mount options getacl functions
  cifs: prototype declaration and definition for smb 2 - 3 and cifsacl mount options
  CIFS: add CONFIG_CIFS_DEBUG_KEYS to dump encryption keys
  cifs: set mapping error when page writeback fails in writepage or launder_pages
  SMB3: Enable encryption for SMB3.1.1
parents 1a86fc75 966681c9
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -146,6 +146,15 @@ config CIFS_DEBUG2
	   option can be turned off unless you are debugging
	   cifs problems.  If unsure, say N.

config CIFS_DEBUG_DUMP_KEYS
	bool "Dump encryption keys for offline decryption (Unsafe)"
	depends on CIFS_DEBUG && CIFS_SMB2
	help
	   Enabling this will dump the encryption and decryption keys
	   used to communicate on an encrypted share connection on the
	   console. This allows Wireshark to decrypt and dissect
	   encrypted network captures. Enable this carefully.

config CIFS_DFS_UPCALL
	  bool "DFS feature support"
	  depends on CIFS && KEYS
+8 −0
Original line number Diff line number Diff line
@@ -79,6 +79,10 @@ convert_sfu_char(const __u16 src_char, char *target)
static bool
convert_sfm_char(const __u16 src_char, char *target)
{
	if (src_char >= 0xF001 && src_char <= 0xF01F) {
		*target = src_char - 0xF000;
		return true;
	}
	switch (src_char) {
	case SFM_COLON:
		*target = ':';
@@ -417,6 +421,10 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
{
	__le16 dest_char;

	if (src_char >= 0x01 && src_char <= 0x1F) {
		dest_char = cpu_to_le16(src_char + 0xF000);
		return dest_char;
	}
	switch (src_char) {
	case ':':
		dest_char = cpu_to_le16(SFM_COLON);
+9 −7
Original line number Diff line number Diff line
@@ -2234,14 +2234,16 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
	set_page_writeback(page);
retry_write:
	rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
	if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL)
	if (rc == -EAGAIN) {
		if (wbc->sync_mode == WB_SYNC_ALL)
			goto retry_write;
	else if (rc == -EAGAIN)
		redirty_page_for_writepage(wbc, page);
	else if (rc != 0)
	} else if (rc != 0) {
		SetPageError(page);
	else
		mapping_set_error(page->mapping, rc);
	} else {
		SetPageUptodate(page);
	}
	end_page_writeback(page);
	put_page(page);
	free_xid(xid);
@@ -2810,12 +2812,12 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
	ssize_t rc;

	inode_lock(inode);
	/*
	 * We need to hold the sem to be sure nobody modifies lock list
	 * with a brlock that prevents writing.
	 */
	down_read(&cinode->lock_sem);
	inode_lock(inode);

	rc = generic_write_checks(iocb, from);
	if (rc <= 0)
@@ -2828,11 +2830,11 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
	else
		rc = -EACCES;
out:
	up_read(&cinode->lock_sem);
	inode_unlock(inode);

	if (rc > 0)
		rc = generic_write_sync(iocb, rc);
	up_read(&cinode->lock_sem);
	return rc;
}

+118 −1
Original line number Diff line number Diff line
@@ -1288,6 +1288,108 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
	return rc;
}

#ifdef CONFIG_CIFS_ACL
static struct cifs_ntsd *
get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
		const struct cifs_fid *cifsfid, u32 *pacllen)
{
	struct cifs_ntsd *pntsd = NULL;
	unsigned int xid;
	int rc = -EOPNOTSUPP;
	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);

	if (IS_ERR(tlink))
		return ERR_CAST(tlink);

	xid = get_xid();
	cifs_dbg(FYI, "trying to get acl\n");

	rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid,
			    cifsfid->volatile_fid, (void **)&pntsd, pacllen);
	free_xid(xid);

	cifs_put_tlink(tlink);

	cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
	if (rc)
		return ERR_PTR(rc);
	return pntsd;

}

static struct cifs_ntsd *
get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
		const char *path, u32 *pacllen)
{
	struct cifs_ntsd *pntsd = NULL;
	u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
	unsigned int xid;
	int rc;
	struct cifs_tcon *tcon;
	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
	struct cifs_fid fid;
	struct cifs_open_parms oparms;
	__le16 *utf16_path;

	cifs_dbg(FYI, "get smb3 acl for path %s\n", path);
	if (IS_ERR(tlink))
		return ERR_CAST(tlink);

	tcon = tlink_tcon(tlink);
	xid = get_xid();

	if (backup_cred(cifs_sb))
		oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
	else
		oparms.create_options = 0;

	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
	if (!utf16_path)
		return ERR_PTR(-ENOMEM);

	oparms.tcon = tcon;
	oparms.desired_access = READ_CONTROL;
	oparms.disposition = FILE_OPEN;
	oparms.fid = &fid;
	oparms.reconnect = false;

	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
	kfree(utf16_path);
	if (!rc) {
		rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
			    fid.volatile_fid, (void **)&pntsd, pacllen);
		SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
	}

	cifs_put_tlink(tlink);
	free_xid(xid);

	cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
	if (rc)
		return ERR_PTR(rc);
	return pntsd;
}

/* Retrieve an ACL from the server */
static struct cifs_ntsd *
get_smb2_acl(struct cifs_sb_info *cifs_sb,
				      struct inode *inode, const char *path,
				      u32 *pacllen)
{
	struct cifs_ntsd *pntsd = NULL;
	struct cifsFileInfo *open_file = NULL;

	if (inode)
		open_file = find_readable_file(CIFS_I(inode), true);
	if (!open_file)
		return get_smb2_acl_by_path(cifs_sb, path, pacllen);

	pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
	cifsFileInfo_put(open_file);
	return pntsd;
}
#endif

static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
			    loff_t offset, loff_t len, bool keep_size)
{
@@ -2393,6 +2495,11 @@ struct smb_version_operations smb20_operations = {
	.dir_needs_close = smb2_dir_needs_close,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
/*	.set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
};

struct smb_version_operations smb21_operations = {
@@ -2477,6 +2584,11 @@ struct smb_version_operations smb21_operations = {
	.enum_snapshots = smb3_enum_snapshots,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
/*	.set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
};

struct smb_version_operations smb30_operations = {
@@ -2571,6 +2683,11 @@ struct smb_version_operations smb30_operations = {
	.receive_transform = smb3_receive_transform,
	.get_dfs_refer = smb2_get_dfs_refer,
	.select_sectype = smb2_select_sectype,
#ifdef CONFIG_CIFS_ACL
	.get_acl = get_smb2_acl,
	.get_acl_by_fid = get_smb2_acl_by_fid,
/*	.set_acl = set_smb3_acl, */
#endif /* CIFS_ACL */
};

#ifdef CONFIG_CIFS_SMB311
@@ -2753,7 +2870,7 @@ struct smb_version_values smb302_values = {
struct smb_version_values smb311_values = {
	.version_string = SMB311_VERSION_STRING,
	.protocol_id = SMB311_PROT_ID,
	.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
	.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
	.large_lock_type = 0,
	.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
	.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+41 −11
Original line number Diff line number Diff line
@@ -2081,8 +2081,9 @@ validate_and_copy_buf(unsigned int offset, unsigned int buffer_length,

static int
query_info(const unsigned int xid, struct cifs_tcon *tcon,
	   u64 persistent_fid, u64 volatile_fid, u8 info_class,
	   size_t output_len, size_t min_len, void *data)
	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
	   u32 additional_info, size_t output_len, size_t min_len, void **data,
		u32 *dlen)
{
	struct smb2_query_info_req *req;
	struct smb2_query_info_rsp *rsp = NULL;
@@ -2108,10 +2109,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
	if (encryption_required(tcon))
		flags |= CIFS_TRANSFORM_REQ;

	req->InfoType = SMB2_O_INFO_FILE;
	req->InfoType = info_type;
	req->FileInfoClass = info_class;
	req->PersistentFileId = persistent_fid;
	req->VolatileFileId = volatile_fid;
	req->AdditionalInformation = cpu_to_le32(additional_info);
	/* 4 for rfc1002 length field and 1 for Buffer */
	req->InputBufferOffset =
		cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
@@ -2130,24 +2132,51 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
		goto qinf_exit;
	}

	if (dlen) {
		*dlen = le32_to_cpu(rsp->OutputBufferLength);
		if (!*data) {
			*data = kmalloc(*dlen, GFP_KERNEL);
			if (!*data) {
				cifs_dbg(VFS,
					"Error %d allocating memory for acl\n",
					rc);
				*dlen = 0;
				goto qinf_exit;
			}
		}
	}

	rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset),
				   le32_to_cpu(rsp->OutputBufferLength),
				   &rsp->hdr, min_len, data);
				   &rsp->hdr, min_len, *data);

qinf_exit:
	free_rsp_buf(resp_buftype, rsp);
	return rc;
}

int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
	u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
{
	return query_info(xid, tcon, persistent_fid, volatile_fid,
			  FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0,
			  sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
			  sizeof(struct smb2_file_all_info), (void **)&data,
			  NULL);
}

int
SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
		u64 persistent_fid, u64 volatile_fid,
		struct smb2_file_all_info *data)
		void **data, u32 *plen)
{
	__u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
	*plen = 0;

	return query_info(xid, tcon, persistent_fid, volatile_fid,
			  FILE_ALL_INFORMATION,
			  sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
			  sizeof(struct smb2_file_all_info), data);
			  0, SMB2_O_INFO_SECURITY, additional_info,
			  SMB2_MAX_BUFFER_SIZE,
			  sizeof(struct smb2_file_all_info), data, plen);
}

int
@@ -2155,9 +2184,10 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
		 u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
{
	return query_info(xid, tcon, persistent_fid, volatile_fid,
			  FILE_INTERNAL_INFORMATION,
			  FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0,
			  sizeof(struct smb2_file_internal_info),
			  sizeof(struct smb2_file_internal_info),
			  sizeof(struct smb2_file_internal_info), uniqueid);
			  (void **)&uniqueid, NULL);
}

/*
Loading