Commit ea41367b authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

smb: client: introduce SMB2_OP_QUERY_WSL_EA



Add a new command to smb2_compound_op() for querying WSL extended
attributes from reparse points.

Signed-off-by: default avatarPaulo Alcantara <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent e0e1e09b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -214,6 +214,10 @@ struct cifs_open_info_data {
			struct reparse_posix_data *posix;
		};
	} reparse;
	struct {
		__u8		eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
		unsigned int	eas_len;
	} wsl;
	char *symlink_target;
	struct cifs_sid posix_owner;
	struct cifs_sid posix_group;
@@ -2296,6 +2300,7 @@ struct smb2_compound_vars {
	struct kvec close_iov;
	struct smb2_file_rename_info rename_info;
	struct smb2_file_link_info link_info;
	struct kvec ea_iov;
};

#endif	/* _CIFS_GLOB_H */
+5 −5
Original line number Diff line number Diff line
@@ -205,15 +205,15 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
	__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
	__le64 mode = cpu_to_le64(_mode);
	struct wsl_xattr xattrs[] = {
		{ .name = "$LXUID", .value = uid, .size = 4, },
		{ .name = "$LXGID", .value = gid, .size = 4, },
		{ .name = "$LXMOD", .value = mode, .size = 4, },
		{ .name = "$LXDEV", .value = dev, .size = 8, },
		{ .name = SMB2_WSL_XATTR_UID,  .value = uid,  .size = SMB2_WSL_XATTR_UID_SIZE, },
		{ .name = SMB2_WSL_XATTR_GID,  .value = gid,  .size = SMB2_WSL_XATTR_GID_SIZE, },
		{ .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
		{ .name = SMB2_WSL_XATTR_DEV,  .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
	};
	size_t cc_len;
	u32 dlen = 0, next = 0;
	int i, num_xattrs;
	u8 name_size = strlen(xattrs[0].name) + 1;
	u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;

	memset(iov, 0, sizeof(*iov));

+2 −1
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ enum smb2_compound_ops {
	SMB2_OP_RMDIR,
	SMB2_OP_POSIX_QUERY_INFO,
	SMB2_OP_SET_REPARSE,
	SMB2_OP_GET_REPARSE
	SMB2_OP_GET_REPARSE,
	SMB2_OP_QUERY_WSL_EA,
};

/* Used when constructing chained read requests. */
+151 −19
Original line number Diff line number Diff line
@@ -85,6 +85,82 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
	return 0;
}

struct wsl_query_ea {
	__le32	next;
	__u8	name_len;
	__u8	name[SMB2_WSL_XATTR_NAME_LEN + 1];
} __packed;

#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))

static const struct wsl_query_ea wsl_query_eas[] = {
	{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
	{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
	{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
	{ .next = 0,        .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
};

static int check_wsl_eas(struct kvec *rsp_iov)
{
	struct smb2_file_full_ea_info *ea;
	struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
	unsigned long addr;
	u32 outlen, next;
	u16 vlen;
	u8 nlen;
	u8 *end;

	outlen = le32_to_cpu(rsp->OutputBufferLength);
	if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
	    outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
		return -EINVAL;

	ea = (void *)((u8 *)rsp_iov->iov_base +
		      le16_to_cpu(rsp->OutputBufferOffset));
	end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
	for (;;) {
		if ((u8 *)ea > end - sizeof(*ea))
			return -EINVAL;

		nlen = ea->ea_name_length;
		vlen = le16_to_cpu(ea->ea_value_length);
		if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
		    (u8 *)ea + nlen + 1 + vlen > end)
			return -EINVAL;

		switch (vlen) {
		case 4:
			if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
			    strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
			    strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
				return -EINVAL;
			break;
		case 8:
			if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
				return -EINVAL;
			break;
		case 0:
			if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
			    !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
			    !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
			    !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
				break;
			fallthrough;
		default:
			return -EINVAL;
		}

		next = le32_to_cpu(ea->next_entry_offset);
		if (!next)
			break;
		if (!IS_ALIGNED(next, 4) ||
		    check_add_overflow((unsigned long)ea, next, &addr))
			return -EINVAL;
		ea = (void *)addr;
	}
	return 0;
}

/*
 * note: If cfile is passed, the reference to it is dropped here.
 * So make sure that you do not reuse cfile after return from this func.
@@ -119,7 +195,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
	__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
	unsigned int size[2];
	void *data[2];
	int len;
	unsigned int len;
	int retries = 0, cur_sleep = 1;

replay_again:
@@ -476,6 +552,39 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
			trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
							      tcon->tid, full_path);
			break;
		case SMB2_OP_QUERY_WSL_EA:
			rqst[num_rqst].rq_iov = &vars->ea_iov;
			rqst[num_rqst].rq_nvec = 1;

			if (cfile) {
				rc = SMB2_query_info_init(tcon, server,
							  &rqst[num_rqst],
							  cfile->fid.persistent_fid,
							  cfile->fid.volatile_fid,
							  FILE_FULL_EA_INFORMATION,
							  SMB2_O_INFO_FILE, 0,
							  SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
							  sizeof(wsl_query_eas),
							  (void *)wsl_query_eas);
			} else {
				rc = SMB2_query_info_init(tcon, server,
							  &rqst[num_rqst],
							  COMPOUND_FID,
							  COMPOUND_FID,
							  FILE_FULL_EA_INFORMATION,
							  SMB2_O_INFO_FILE, 0,
							  SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
							  sizeof(wsl_query_eas),
							  (void *)wsl_query_eas);
			}
			if (!rc && (!cfile || num_rqst > 1)) {
				smb2_set_next_command(tcon, &rqst[num_rqst]);
				smb2_set_related(&rqst[num_rqst]);
			} else if (rc) {
				goto finished;
			}
			num_rqst++;
			break;
		default:
			cifs_dbg(VFS, "Invalid command\n");
			rc = -EINVAL;
@@ -670,6 +779,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
			}
			SMB2_ioctl_free(&rqst[num_rqst++]);
			break;
		case SMB2_OP_QUERY_WSL_EA:
			if (!rc) {
				idata = in_iov[i].iov_base;
				qi_rsp = rsp_iov[i + 1].iov_base;
				data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
				size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
				rc = check_wsl_eas(&rsp_iov[i + 1]);
				if (!rc) {
					memcpy(idata->wsl.eas, data[0], size[0]);
					idata->wsl.eas_len = size[0];
				}
			}
			if (!rc) {
				trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
								      tcon->tid);
			} else {
				trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
								     tcon->tid, rc);
			}
			SMB2_query_info_free(&rqst[num_rqst++]);
			break;
		}
	}
	SMB2_close_free(&rqst[num_rqst]);
@@ -737,11 +867,11 @@ int smb2_query_path_info(const unsigned int xid,
	struct cifsFileInfo *cfile;
	struct cached_fid *cfid = NULL;
	struct smb2_hdr *hdr;
	struct kvec in_iov[2], out_iov[3] = {};
	struct kvec in_iov[3], out_iov[3] = {};
	int out_buftype[3] = {};
	int cmds[2];
	int cmds[3];
	bool islink;
	int i, num_cmds;
	int i, num_cmds = 0;
	int rc, rc2;

	data->adjust_tz = false;
@@ -774,21 +904,22 @@ int smb2_query_path_info(const unsigned int xid,
			close_cached_dir(cfid);
			return rc;
		}
		cmds[0] = SMB2_OP_QUERY_INFO;
		cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
	} else {
		cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
		cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
	}

	in_iov[0].iov_base = data;
	in_iov[0].iov_len = sizeof(*data);
	in_iov[1] = in_iov[0];
	in_iov[2] = in_iov[0];

	cifs_get_readable_path(tcon, full_path, &cfile);
	oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
			     FILE_OPEN, create_options, ACL_NO_MODE);
	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
			      &oparms, in_iov, cmds, 1, cfile,
			      out_iov, out_buftype, NULL);
			      &oparms, in_iov, cmds, num_cmds,
			      cfile, out_iov, out_buftype, NULL);
	hdr = out_iov[0].iov_base;
	/*
	 * If first iov is unset, then SMB session was dropped or we've got a
@@ -808,17 +939,18 @@ int smb2_query_path_info(const unsigned int xid,
		if (rc || !data->reparse_point)
			goto out;

		if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
			/* symlink already parsed in create response */
			num_cmds = 1;
		} else {
			cmds[1] = SMB2_OP_GET_REPARSE;
			num_cmds = 2;
		}
		cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
		/*
		 * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
		 * response.
		 */
		if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
			cmds[num_cmds++] = SMB2_OP_GET_REPARSE;

		oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
				     FILE_READ_ATTRIBUTES, FILE_OPEN,
				     create_options | OPEN_REPARSE_POINT,
				     ACL_NO_MODE);
				     FILE_READ_ATTRIBUTES | FILE_READ_EA,
				     FILE_OPEN, create_options |
				     OPEN_REPARSE_POINT, ACL_NO_MODE);
		cifs_get_readable_path(tcon, full_path, &cfile);
		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
				      &oparms, in_iov, cmds, num_cmds,
+25 −0
Original line number Diff line number Diff line
@@ -420,4 +420,29 @@ struct smb2_create_ea_ctx {
	struct smb2_file_full_ea_info ea;
} __packed;

#define SMB2_WSL_XATTR_UID		"$LXUID"
#define SMB2_WSL_XATTR_GID		"$LXGID"
#define SMB2_WSL_XATTR_MODE		"$LXMOD"
#define SMB2_WSL_XATTR_DEV		"$LXDEV"
#define SMB2_WSL_XATTR_NAME_LEN	6
#define SMB2_WSL_NUM_XATTRS		4

#define SMB2_WSL_XATTR_UID_SIZE	4
#define SMB2_WSL_XATTR_GID_SIZE	4
#define SMB2_WSL_XATTR_MODE_SIZE	4
#define SMB2_WSL_XATTR_DEV_SIZE	8

#define SMB2_WSL_MIN_QUERY_EA_RESP_SIZE \
	(ALIGN((SMB2_WSL_NUM_XATTRS - 1) * \
	       (SMB2_WSL_XATTR_NAME_LEN + 1 + \
		sizeof(struct smb2_file_full_ea_info)), 4) + \
	 SMB2_WSL_XATTR_NAME_LEN + 1 + sizeof(struct smb2_file_full_ea_info))

#define SMB2_WSL_MAX_QUERY_EA_RESP_SIZE \
	(ALIGN(SMB2_WSL_MIN_QUERY_EA_RESP_SIZE + \
	       SMB2_WSL_XATTR_UID_SIZE + \
	       SMB2_WSL_XATTR_GID_SIZE + \
	       SMB2_WSL_XATTR_MODE_SIZE + \
	       SMB2_WSL_XATTR_DEV_SIZE, 4))

#endif				/* _SMB2PDU_H */
Loading