xref: /openbmc/linux/fs/smb/client/smb2inode.c (revision b6e27f7f)
138c8a9a5SSteve French // SPDX-License-Identifier: LGPL-2.1
238c8a9a5SSteve French /*
338c8a9a5SSteve French  *
438c8a9a5SSteve French  *   Copyright (C) International Business Machines  Corp., 2002, 2011
538c8a9a5SSteve French  *                 Etersoft, 2012
638c8a9a5SSteve French  *   Author(s): Pavel Shilovsky (pshilovsky@samba.org),
738c8a9a5SSteve French  *              Steve French (sfrench@us.ibm.com)
838c8a9a5SSteve French  *
938c8a9a5SSteve French  */
1038c8a9a5SSteve French #include <linux/fs.h>
1138c8a9a5SSteve French #include <linux/stat.h>
1238c8a9a5SSteve French #include <linux/slab.h>
1338c8a9a5SSteve French #include <linux/pagemap.h>
1438c8a9a5SSteve French #include <asm/div64.h>
1538c8a9a5SSteve French #include "cifsfs.h"
1638c8a9a5SSteve French #include "cifspdu.h"
1738c8a9a5SSteve French #include "cifsglob.h"
1838c8a9a5SSteve French #include "cifsproto.h"
1938c8a9a5SSteve French #include "cifs_debug.h"
2038c8a9a5SSteve French #include "cifs_fs_sb.h"
2138c8a9a5SSteve French #include "cifs_unicode.h"
2238c8a9a5SSteve French #include "fscache.h"
2338c8a9a5SSteve French #include "smb2glob.h"
2438c8a9a5SSteve French #include "smb2pdu.h"
2538c8a9a5SSteve French #include "smb2proto.h"
2638c8a9a5SSteve French #include "cached_dir.h"
2738c8a9a5SSteve French #include "smb2status.h"
2838c8a9a5SSteve French 
298c944f8aSPaulo Alcantara static inline __u32 file_create_options(struct dentry *dentry)
308c944f8aSPaulo Alcantara {
318c944f8aSPaulo Alcantara 	struct cifsInodeInfo *ci;
328c944f8aSPaulo Alcantara 
338c944f8aSPaulo Alcantara 	if (dentry) {
348c944f8aSPaulo Alcantara 		ci = CIFS_I(d_inode(dentry));
358c944f8aSPaulo Alcantara 		if (ci->cifsAttrs & ATTR_REPARSE)
368c944f8aSPaulo Alcantara 			return OPEN_REPARSE_POINT;
378c944f8aSPaulo Alcantara 	}
388c944f8aSPaulo Alcantara 	return 0;
398c944f8aSPaulo Alcantara }
408c944f8aSPaulo Alcantara 
41a158bb66SSteve French static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
42a158bb66SSteve French {
43a158bb66SSteve French 	struct reparse_data_buffer *buf;
44a158bb66SSteve French 	struct smb2_ioctl_rsp *io = iov->iov_base;
45a158bb66SSteve French 	u32 off, count, len;
46a158bb66SSteve French 
47a158bb66SSteve French 	count = le32_to_cpu(io->OutputCount);
48a158bb66SSteve French 	off = le32_to_cpu(io->OutputOffset);
49a158bb66SSteve French 	if (check_add_overflow(off, count, &len) || len > iov->iov_len)
50a158bb66SSteve French 		return ERR_PTR(-EIO);
51a158bb66SSteve French 
52a158bb66SSteve French 	buf = (struct reparse_data_buffer *)((u8 *)io + off);
53a158bb66SSteve French 	len = sizeof(*buf);
54a158bb66SSteve French 	if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
55a158bb66SSteve French 		return ERR_PTR(-EIO);
56a158bb66SSteve French 	return buf;
57a158bb66SSteve French }
58a158bb66SSteve French 
59a90f37e3SSteve French /* Parse owner and group from SMB3.1.1 POSIX query info */
60a90f37e3SSteve French static int parse_posix_sids(struct cifs_open_info_data *data,
61a90f37e3SSteve French 			    struct kvec *rsp_iov)
62a90f37e3SSteve French {
63a90f37e3SSteve French 	struct smb2_query_info_rsp *qi = rsp_iov->iov_base;
64a90f37e3SSteve French 	unsigned int out_len = le32_to_cpu(qi->OutputBufferLength);
65a90f37e3SSteve French 	unsigned int qi_len = sizeof(data->posix_fi);
66a90f37e3SSteve French 	int owner_len, group_len;
67a90f37e3SSteve French 	u8 *sidsbuf, *sidsbuf_end;
68a90f37e3SSteve French 
69a90f37e3SSteve French 	if (out_len <= qi_len)
70a90f37e3SSteve French 		return -EINVAL;
71a90f37e3SSteve French 
72a90f37e3SSteve French 	sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
73a90f37e3SSteve French 	sidsbuf_end = sidsbuf + out_len - qi_len;
74a90f37e3SSteve French 
75a90f37e3SSteve French 	owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
76a90f37e3SSteve French 	if (owner_len == -1)
77a90f37e3SSteve French 		return -EINVAL;
78a90f37e3SSteve French 
79a90f37e3SSteve French 	memcpy(&data->posix_owner, sidsbuf, owner_len);
80a90f37e3SSteve French 	group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end);
81a90f37e3SSteve French 	if (group_len == -1)
82a90f37e3SSteve French 		return -EINVAL;
83a90f37e3SSteve French 
84a90f37e3SSteve French 	memcpy(&data->posix_group, sidsbuf + owner_len, group_len);
85a90f37e3SSteve French 	return 0;
86a90f37e3SSteve French }
87a90f37e3SSteve French 
8838c8a9a5SSteve French /*
8938c8a9a5SSteve French  * note: If cfile is passed, the reference to it is dropped here.
9038c8a9a5SSteve French  * So make sure that you do not reuse cfile after return from this func.
9138c8a9a5SSteve French  *
92c5f44a3dSPaulo Alcantara  * If passing @out_iov and @out_buftype, ensure to make them both large enough
93c5f44a3dSPaulo Alcantara  * (>= 3) to hold all compounded responses.  Caller is also responsible for
94c5f44a3dSPaulo Alcantara  * freeing them up with free_rsp_buf().
9538c8a9a5SSteve French  */
9638c8a9a5SSteve French static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
9738c8a9a5SSteve French 			    struct cifs_sb_info *cifs_sb, const char *full_path,
98886b7fb4SSteve French 			    __u32 desired_access, __u32 create_disposition,
99886b7fb4SSteve French 			    __u32 create_options, umode_t mode, struct kvec *in_iov,
100886b7fb4SSteve French 			    int *cmds, int num_cmds, struct cifsFileInfo *cfile,
101f93d145fSMeetakshi Setiya 			    struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
10238c8a9a5SSteve French {
103a158bb66SSteve French 
104a158bb66SSteve French 	struct reparse_data_buffer *rbuf;
105f4e5ceb6SPaulo Alcantara 	struct smb2_compound_vars *vars = NULL;
106a158bb66SSteve French 	struct kvec *rsp_iov, *iov;
10738c8a9a5SSteve French 	struct smb_rqst *rqst;
10838c8a9a5SSteve French 	int rc;
10938c8a9a5SSteve French 	__le16 *utf16_path = NULL;
11038c8a9a5SSteve French 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
11138c8a9a5SSteve French 	struct cifs_fid fid;
11238c8a9a5SSteve French 	struct cifs_ses *ses = tcon->ses;
11338c8a9a5SSteve French 	struct TCP_Server_Info *server;
114886b7fb4SSteve French 	int num_rqst = 0, i;
115886b7fb4SSteve French 	int resp_buftype[MAX_COMPOUND];
11638c8a9a5SSteve French 	struct smb2_query_info_rsp *qi_rsp = NULL;
11738c8a9a5SSteve French 	struct cifs_open_info_data *idata;
118f93d145fSMeetakshi Setiya 	struct inode *inode = NULL;
11938c8a9a5SSteve French 	int flags = 0;
12038c8a9a5SSteve French 	__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
12138c8a9a5SSteve French 	unsigned int size[2];
12238c8a9a5SSteve French 	void *data[2];
12338c8a9a5SSteve French 	int len;
124433042a9SShyam Prasad N 	int retries = 0, cur_sleep = 1;
125433042a9SShyam Prasad N 
126433042a9SShyam Prasad N replay_again:
127433042a9SShyam Prasad N 	/* reinitialize for possible replay */
128433042a9SShyam Prasad N 	flags = 0;
129433042a9SShyam Prasad N 	oplock = SMB2_OPLOCK_LEVEL_NONE;
130433042a9SShyam Prasad N 	num_rqst = 0;
131433042a9SShyam Prasad N 	server = cifs_pick_channel(ses);
13238c8a9a5SSteve French 
13338c8a9a5SSteve French 	vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
13438c8a9a5SSteve French 	if (vars == NULL)
13538c8a9a5SSteve French 		return -ENOMEM;
13638c8a9a5SSteve French 	rqst = &vars->rqst[0];
13738c8a9a5SSteve French 	rsp_iov = &vars->rsp_iov[0];
13838c8a9a5SSteve French 
13938c8a9a5SSteve French 	if (smb3_encryption_required(tcon))
14038c8a9a5SSteve French 		flags |= CIFS_TRANSFORM_REQ;
14138c8a9a5SSteve French 
142886b7fb4SSteve French 	for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
143886b7fb4SSteve French 		resp_buftype[i] = CIFS_NO_BUFFER;
14438c8a9a5SSteve French 
14538c8a9a5SSteve French 	/* We already have a handle so we can skip the open */
14638c8a9a5SSteve French 	if (cfile)
14738c8a9a5SSteve French 		goto after_open;
14838c8a9a5SSteve French 
14938c8a9a5SSteve French 	/* Open */
15038c8a9a5SSteve French 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
15138c8a9a5SSteve French 	if (!utf16_path) {
15238c8a9a5SSteve French 		rc = -ENOMEM;
15338c8a9a5SSteve French 		goto finished;
15438c8a9a5SSteve French 	}
15538c8a9a5SSteve French 
156f93d145fSMeetakshi Setiya 	/* if there is an existing lease, reuse it */
157f93d145fSMeetakshi Setiya 	if (dentry) {
158f93d145fSMeetakshi Setiya 		inode = d_inode(dentry);
159f93d145fSMeetakshi Setiya 		if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
160f93d145fSMeetakshi Setiya 			oplock = SMB2_OPLOCK_LEVEL_LEASE;
161f93d145fSMeetakshi Setiya 			server->ops->get_lease_key(inode, &fid);
162f93d145fSMeetakshi Setiya 		}
163f93d145fSMeetakshi Setiya 	}
164f93d145fSMeetakshi Setiya 
16538c8a9a5SSteve French 	vars->oparms = (struct cifs_open_parms) {
16638c8a9a5SSteve French 		.tcon = tcon,
16738c8a9a5SSteve French 		.path = full_path,
16838c8a9a5SSteve French 		.desired_access = desired_access,
16938c8a9a5SSteve French 		.disposition = create_disposition,
17038c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, create_options),
17138c8a9a5SSteve French 		.fid = &fid,
17238c8a9a5SSteve French 		.mode = mode,
17338c8a9a5SSteve French 		.cifs_sb = cifs_sb,
17438c8a9a5SSteve French 	};
17538c8a9a5SSteve French 
17638c8a9a5SSteve French 	rqst[num_rqst].rq_iov = &vars->open_iov[0];
17738c8a9a5SSteve French 	rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
17838c8a9a5SSteve French 	rc = SMB2_open_init(tcon, server,
17938c8a9a5SSteve French 			    &rqst[num_rqst], &oplock, &vars->oparms,
18038c8a9a5SSteve French 			    utf16_path);
18138c8a9a5SSteve French 	kfree(utf16_path);
18238c8a9a5SSteve French 	if (rc)
18338c8a9a5SSteve French 		goto finished;
18438c8a9a5SSteve French 
18538c8a9a5SSteve French 	smb2_set_next_command(tcon, &rqst[num_rqst]);
18638c8a9a5SSteve French  after_open:
18738c8a9a5SSteve French 	num_rqst++;
18838c8a9a5SSteve French 	rc = 0;
18938c8a9a5SSteve French 
190886b7fb4SSteve French 	for (i = 0; i < num_cmds; i++) {
19138c8a9a5SSteve French 		/* Operation */
192886b7fb4SSteve French 		switch (cmds[i]) {
19338c8a9a5SSteve French 		case SMB2_OP_QUERY_INFO:
194f4e5ceb6SPaulo Alcantara 			rqst[num_rqst].rq_iov = &vars->qi_iov;
19538c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
19638c8a9a5SSteve French 
197886b7fb4SSteve French 			if (cfile) {
19838c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
19938c8a9a5SSteve French 							  &rqst[num_rqst],
20038c8a9a5SSteve French 							  cfile->fid.persistent_fid,
20138c8a9a5SSteve French 							  cfile->fid.volatile_fid,
20238c8a9a5SSteve French 							  FILE_ALL_INFORMATION,
20338c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
20438c8a9a5SSteve French 							  sizeof(struct smb2_file_all_info) +
20538c8a9a5SSteve French 							  PATH_MAX * 2, 0, NULL);
206886b7fb4SSteve French 			} else {
20738c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
20838c8a9a5SSteve French 							  &rqst[num_rqst],
20938c8a9a5SSteve French 							  COMPOUND_FID,
21038c8a9a5SSteve French 							  COMPOUND_FID,
21138c8a9a5SSteve French 							  FILE_ALL_INFORMATION,
21238c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
21338c8a9a5SSteve French 							  sizeof(struct smb2_file_all_info) +
21438c8a9a5SSteve French 							  PATH_MAX * 2, 0, NULL);
21538c8a9a5SSteve French 				if (!rc) {
21638c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
21738c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
21838c8a9a5SSteve French 				}
21938c8a9a5SSteve French 			}
22038c8a9a5SSteve French 
22138c8a9a5SSteve French 			if (rc)
22238c8a9a5SSteve French 				goto finished;
22338c8a9a5SSteve French 			num_rqst++;
224886b7fb4SSteve French 			trace_smb3_query_info_compound_enter(xid, ses->Suid,
225886b7fb4SSteve French 							     tcon->tid, full_path);
22638c8a9a5SSteve French 			break;
22738c8a9a5SSteve French 		case SMB2_OP_POSIX_QUERY_INFO:
228f4e5ceb6SPaulo Alcantara 			rqst[num_rqst].rq_iov = &vars->qi_iov;
22938c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
23038c8a9a5SSteve French 
231886b7fb4SSteve French 			if (cfile) {
232886b7fb4SSteve French 				/* TBD: fix following to allow for longer SIDs */
23338c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
23438c8a9a5SSteve French 							  &rqst[num_rqst],
23538c8a9a5SSteve French 							  cfile->fid.persistent_fid,
23638c8a9a5SSteve French 							  cfile->fid.volatile_fid,
23738c8a9a5SSteve French 							  SMB_FIND_FILE_POSIX_INFO,
23838c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
239886b7fb4SSteve French 							  sizeof(struct smb311_posix_qinfo *) +
240886b7fb4SSteve French 							  (PATH_MAX * 2) +
24138c8a9a5SSteve French 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
242886b7fb4SSteve French 			} else {
24338c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
24438c8a9a5SSteve French 							  &rqst[num_rqst],
24538c8a9a5SSteve French 							  COMPOUND_FID,
24638c8a9a5SSteve French 							  COMPOUND_FID,
24738c8a9a5SSteve French 							  SMB_FIND_FILE_POSIX_INFO,
24838c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
249886b7fb4SSteve French 							  sizeof(struct smb311_posix_qinfo *) +
250886b7fb4SSteve French 							  (PATH_MAX * 2) +
25138c8a9a5SSteve French 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
25238c8a9a5SSteve French 				if (!rc) {
25338c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
25438c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
25538c8a9a5SSteve French 				}
25638c8a9a5SSteve French 			}
25738c8a9a5SSteve French 
25838c8a9a5SSteve French 			if (rc)
25938c8a9a5SSteve French 				goto finished;
26038c8a9a5SSteve French 			num_rqst++;
261886b7fb4SSteve French 			trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
262886b7fb4SSteve French 								   tcon->tid, full_path);
26338c8a9a5SSteve French 			break;
26438c8a9a5SSteve French 		case SMB2_OP_DELETE:
26538c8a9a5SSteve French 			trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
26638c8a9a5SSteve French 			break;
26738c8a9a5SSteve French 		case SMB2_OP_MKDIR:
26838c8a9a5SSteve French 			/*
26938c8a9a5SSteve French 			 * Directories are created through parameters in the
27038c8a9a5SSteve French 			 * SMB2_open() call.
27138c8a9a5SSteve French 			 */
27238c8a9a5SSteve French 			trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
27338c8a9a5SSteve French 			break;
27438c8a9a5SSteve French 		case SMB2_OP_RMDIR:
27538c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
27638c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
27738c8a9a5SSteve French 
27838c8a9a5SSteve French 			size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
27938c8a9a5SSteve French 			data[0] = &delete_pending[0];
28038c8a9a5SSteve French 
28138c8a9a5SSteve French 			rc = SMB2_set_info_init(tcon, server,
28238c8a9a5SSteve French 						&rqst[num_rqst], COMPOUND_FID,
28338c8a9a5SSteve French 						COMPOUND_FID, current->tgid,
28438c8a9a5SSteve French 						FILE_DISPOSITION_INFORMATION,
28538c8a9a5SSteve French 						SMB2_O_INFO_FILE, 0, data, size);
28638c8a9a5SSteve French 			if (rc)
28738c8a9a5SSteve French 				goto finished;
28838c8a9a5SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
28938c8a9a5SSteve French 			smb2_set_related(&rqst[num_rqst++]);
29038c8a9a5SSteve French 			trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
29138c8a9a5SSteve French 			break;
29238c8a9a5SSteve French 		case SMB2_OP_SET_EOF:
29338c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
29438c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
29538c8a9a5SSteve French 
296886b7fb4SSteve French 			size[0] = in_iov[i].iov_len;
297886b7fb4SSteve French 			data[0] = in_iov[i].iov_base;
29838c8a9a5SSteve French 
29938c8a9a5SSteve French 			if (cfile) {
30038c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
30138c8a9a5SSteve French 							&rqst[num_rqst],
30238c8a9a5SSteve French 							cfile->fid.persistent_fid,
30338c8a9a5SSteve French 							cfile->fid.volatile_fid,
30438c8a9a5SSteve French 							current->tgid,
30538c8a9a5SSteve French 							FILE_END_OF_FILE_INFORMATION,
30638c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0,
30738c8a9a5SSteve French 							data, size);
30838c8a9a5SSteve French 			} else {
30938c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
31038c8a9a5SSteve French 							&rqst[num_rqst],
31138c8a9a5SSteve French 							COMPOUND_FID,
31238c8a9a5SSteve French 							COMPOUND_FID,
31338c8a9a5SSteve French 							current->tgid,
31438c8a9a5SSteve French 							FILE_END_OF_FILE_INFORMATION,
31538c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0,
31638c8a9a5SSteve French 							data, size);
31738c8a9a5SSteve French 				if (!rc) {
31838c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
31938c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
32038c8a9a5SSteve French 				}
32138c8a9a5SSteve French 			}
32238c8a9a5SSteve French 			if (rc)
32338c8a9a5SSteve French 				goto finished;
32438c8a9a5SSteve French 			num_rqst++;
32538c8a9a5SSteve French 			trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
32638c8a9a5SSteve French 			break;
32738c8a9a5SSteve French 		case SMB2_OP_SET_INFO:
32838c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
32938c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
33038c8a9a5SSteve French 
331886b7fb4SSteve French 			size[0] = in_iov[i].iov_len;
332886b7fb4SSteve French 			data[0] = in_iov[i].iov_base;
33338c8a9a5SSteve French 
334886b7fb4SSteve French 			if (cfile) {
33538c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
33638c8a9a5SSteve French 							&rqst[num_rqst],
33738c8a9a5SSteve French 							cfile->fid.persistent_fid,
33838c8a9a5SSteve French 							cfile->fid.volatile_fid, current->tgid,
33938c8a9a5SSteve French 							FILE_BASIC_INFORMATION,
34038c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
341886b7fb4SSteve French 			} else {
34238c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
34338c8a9a5SSteve French 							&rqst[num_rqst],
34438c8a9a5SSteve French 							COMPOUND_FID,
34538c8a9a5SSteve French 							COMPOUND_FID, current->tgid,
34638c8a9a5SSteve French 							FILE_BASIC_INFORMATION,
34738c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
34838c8a9a5SSteve French 				if (!rc) {
34938c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
35038c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
35138c8a9a5SSteve French 				}
35238c8a9a5SSteve French 			}
35338c8a9a5SSteve French 
35438c8a9a5SSteve French 			if (rc)
35538c8a9a5SSteve French 				goto finished;
35638c8a9a5SSteve French 			num_rqst++;
357886b7fb4SSteve French 			trace_smb3_set_info_compound_enter(xid, ses->Suid,
358886b7fb4SSteve French 							   tcon->tid, full_path);
35938c8a9a5SSteve French 			break;
36038c8a9a5SSteve French 		case SMB2_OP_RENAME:
36138c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
36238c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 2;
36338c8a9a5SSteve French 
364886b7fb4SSteve French 			len = in_iov[i].iov_len;
36538c8a9a5SSteve French 
36638c8a9a5SSteve French 			vars->rename_info.ReplaceIfExists = 1;
36738c8a9a5SSteve French 			vars->rename_info.RootDirectory = 0;
36838c8a9a5SSteve French 			vars->rename_info.FileNameLength = cpu_to_le32(len);
36938c8a9a5SSteve French 
37038c8a9a5SSteve French 			size[0] = sizeof(struct smb2_file_rename_info);
37138c8a9a5SSteve French 			data[0] = &vars->rename_info;
37238c8a9a5SSteve French 
37338c8a9a5SSteve French 			size[1] = len + 2 /* null */;
374886b7fb4SSteve French 			data[1] = in_iov[i].iov_base;
37538c8a9a5SSteve French 
376886b7fb4SSteve French 			if (cfile) {
37738c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
37838c8a9a5SSteve French 							&rqst[num_rqst],
37938c8a9a5SSteve French 							cfile->fid.persistent_fid,
38038c8a9a5SSteve French 							cfile->fid.volatile_fid,
38138c8a9a5SSteve French 							current->tgid, FILE_RENAME_INFORMATION,
38238c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
383886b7fb4SSteve French 			} else {
38438c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
38538c8a9a5SSteve French 							&rqst[num_rqst],
38638c8a9a5SSteve French 							COMPOUND_FID, COMPOUND_FID,
38738c8a9a5SSteve French 							current->tgid, FILE_RENAME_INFORMATION,
38838c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
38938c8a9a5SSteve French 				if (!rc) {
39038c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
39138c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
39238c8a9a5SSteve French 				}
39338c8a9a5SSteve French 			}
39438c8a9a5SSteve French 			if (rc)
39538c8a9a5SSteve French 				goto finished;
39638c8a9a5SSteve French 			num_rqst++;
39738c8a9a5SSteve French 			trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
39838c8a9a5SSteve French 			break;
39938c8a9a5SSteve French 		case SMB2_OP_HARDLINK:
40038c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
40138c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 2;
40238c8a9a5SSteve French 
403886b7fb4SSteve French 			len = in_iov[i].iov_len;
40438c8a9a5SSteve French 
40538c8a9a5SSteve French 			vars->link_info.ReplaceIfExists = 0;
40638c8a9a5SSteve French 			vars->link_info.RootDirectory = 0;
40738c8a9a5SSteve French 			vars->link_info.FileNameLength = cpu_to_le32(len);
40838c8a9a5SSteve French 
40938c8a9a5SSteve French 			size[0] = sizeof(struct smb2_file_link_info);
41038c8a9a5SSteve French 			data[0] = &vars->link_info;
41138c8a9a5SSteve French 
41238c8a9a5SSteve French 			size[1] = len + 2 /* null */;
413886b7fb4SSteve French 			data[1] = in_iov[i].iov_base;
41438c8a9a5SSteve French 
41538c8a9a5SSteve French 			rc = SMB2_set_info_init(tcon, server,
41638c8a9a5SSteve French 						&rqst[num_rqst], COMPOUND_FID,
41738c8a9a5SSteve French 						COMPOUND_FID, current->tgid,
41838c8a9a5SSteve French 						FILE_LINK_INFORMATION,
41938c8a9a5SSteve French 						SMB2_O_INFO_FILE, 0, data, size);
42038c8a9a5SSteve French 			if (rc)
42138c8a9a5SSteve French 				goto finished;
42238c8a9a5SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
42338c8a9a5SSteve French 			smb2_set_related(&rqst[num_rqst++]);
42438c8a9a5SSteve French 			trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
42538c8a9a5SSteve French 			break;
42602bcf865SSteve French 		case SMB2_OP_SET_REPARSE:
42702bcf865SSteve French 			rqst[num_rqst].rq_iov = vars->io_iov;
42802bcf865SSteve French 			rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
42902bcf865SSteve French 
43002bcf865SSteve French 			rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
43102bcf865SSteve French 					     COMPOUND_FID, COMPOUND_FID,
43202bcf865SSteve French 					     FSCTL_SET_REPARSE_POINT,
43302bcf865SSteve French 					     in_iov[i].iov_base,
43402bcf865SSteve French 					     in_iov[i].iov_len, 0);
43502bcf865SSteve French 			if (rc)
43602bcf865SSteve French 				goto finished;
43702bcf865SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
43802bcf865SSteve French 			smb2_set_related(&rqst[num_rqst++]);
43902bcf865SSteve French 			trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
44002bcf865SSteve French 							      tcon->tid, full_path);
44102bcf865SSteve French 			break;
442a158bb66SSteve French 		case SMB2_OP_GET_REPARSE:
443a158bb66SSteve French 			rqst[num_rqst].rq_iov = vars->io_iov;
444a158bb66SSteve French 			rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
445a158bb66SSteve French 
446a158bb66SSteve French 			rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
447a158bb66SSteve French 					     COMPOUND_FID, COMPOUND_FID,
448a158bb66SSteve French 					     FSCTL_GET_REPARSE_POINT,
449a158bb66SSteve French 					     NULL, 0, CIFSMaxBufSize);
450a158bb66SSteve French 			if (rc)
451a158bb66SSteve French 				goto finished;
452a158bb66SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
453a158bb66SSteve French 			smb2_set_related(&rqst[num_rqst++]);
454a158bb66SSteve French 			trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
455a158bb66SSteve French 							      tcon->tid, full_path);
456a158bb66SSteve French 			break;
45738c8a9a5SSteve French 		default:
45838c8a9a5SSteve French 			cifs_dbg(VFS, "Invalid command\n");
45938c8a9a5SSteve French 			rc = -EINVAL;
46038c8a9a5SSteve French 		}
461886b7fb4SSteve French 	}
46238c8a9a5SSteve French 	if (rc)
46338c8a9a5SSteve French 		goto finished;
46438c8a9a5SSteve French 
46538c8a9a5SSteve French 	/* We already have a handle so we can skip the close */
46638c8a9a5SSteve French 	if (cfile)
46738c8a9a5SSteve French 		goto after_close;
46838c8a9a5SSteve French 	/* Close */
46938c8a9a5SSteve French 	flags |= CIFS_CP_CREATE_CLOSE_OP;
470f4e5ceb6SPaulo Alcantara 	rqst[num_rqst].rq_iov = &vars->close_iov;
47138c8a9a5SSteve French 	rqst[num_rqst].rq_nvec = 1;
47238c8a9a5SSteve French 	rc = SMB2_close_init(tcon, server,
47338c8a9a5SSteve French 			     &rqst[num_rqst], COMPOUND_FID,
47438c8a9a5SSteve French 			     COMPOUND_FID, false);
47538c8a9a5SSteve French 	smb2_set_related(&rqst[num_rqst]);
47638c8a9a5SSteve French 	if (rc)
47738c8a9a5SSteve French 		goto finished;
47838c8a9a5SSteve French  after_close:
47938c8a9a5SSteve French 	num_rqst++;
48038c8a9a5SSteve French 
48138c8a9a5SSteve French 	if (cfile) {
482433042a9SShyam Prasad N 		if (retries)
483433042a9SShyam Prasad N 			for (i = 1; i < num_rqst - 2; i++)
484433042a9SShyam Prasad N 				smb2_set_replay(server, &rqst[i]);
485433042a9SShyam Prasad N 
48638c8a9a5SSteve French 		rc = compound_send_recv(xid, ses, server,
48738c8a9a5SSteve French 					flags, num_rqst - 2,
48838c8a9a5SSteve French 					&rqst[1], &resp_buftype[1],
48938c8a9a5SSteve French 					&rsp_iov[1]);
490433042a9SShyam Prasad N 	} else {
491433042a9SShyam Prasad N 		if (retries)
492433042a9SShyam Prasad N 			for (i = 0; i < num_rqst; i++)
493433042a9SShyam Prasad N 				smb2_set_replay(server, &rqst[i]);
494433042a9SShyam Prasad N 
49538c8a9a5SSteve French 		rc = compound_send_recv(xid, ses, server,
49638c8a9a5SSteve French 					flags, num_rqst,
49738c8a9a5SSteve French 					rqst, resp_buftype,
49838c8a9a5SSteve French 					rsp_iov);
499433042a9SShyam Prasad N 	}
50038c8a9a5SSteve French 
50138c8a9a5SSteve French finished:
502886b7fb4SSteve French 	num_rqst = 0;
503886b7fb4SSteve French 	SMB2_open_free(&rqst[num_rqst++]);
50438c8a9a5SSteve French 	if (rc == -EREMCHG) {
50538c8a9a5SSteve French 		pr_warn_once("server share %s deleted\n", tcon->tree_name);
50638c8a9a5SSteve French 		tcon->need_reconnect = true;
50738c8a9a5SSteve French 	}
50838c8a9a5SSteve French 
509886b7fb4SSteve French 	for (i = 0; i < num_cmds; i++) {
510886b7fb4SSteve French 		switch (cmds[i]) {
51138c8a9a5SSteve French 		case SMB2_OP_QUERY_INFO:
512886b7fb4SSteve French 			idata = in_iov[i].iov_base;
51338c8a9a5SSteve French 			if (rc == 0 && cfile && cfile->symlink_target) {
51438c8a9a5SSteve French 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
51538c8a9a5SSteve French 				if (!idata->symlink_target)
51638c8a9a5SSteve French 					rc = -ENOMEM;
51738c8a9a5SSteve French 			}
51838c8a9a5SSteve French 			if (rc == 0) {
51938c8a9a5SSteve French 				qi_rsp = (struct smb2_query_info_rsp *)
520886b7fb4SSteve French 					rsp_iov[i + 1].iov_base;
52138c8a9a5SSteve French 				rc = smb2_validate_and_copy_iov(
52238c8a9a5SSteve French 					le16_to_cpu(qi_rsp->OutputBufferOffset),
52338c8a9a5SSteve French 					le32_to_cpu(qi_rsp->OutputBufferLength),
524886b7fb4SSteve French 					&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
52538c8a9a5SSteve French 			}
526886b7fb4SSteve French 			SMB2_query_info_free(&rqst[num_rqst++]);
52738c8a9a5SSteve French 			if (rc)
52838c8a9a5SSteve French 				trace_smb3_query_info_compound_err(xid,  ses->Suid,
52938c8a9a5SSteve French 								   tcon->tid, rc);
53038c8a9a5SSteve French 			else
53138c8a9a5SSteve French 				trace_smb3_query_info_compound_done(xid, ses->Suid,
53238c8a9a5SSteve French 								    tcon->tid);
53338c8a9a5SSteve French 			break;
53438c8a9a5SSteve French 		case SMB2_OP_POSIX_QUERY_INFO:
535886b7fb4SSteve French 			idata = in_iov[i].iov_base;
53638c8a9a5SSteve French 			if (rc == 0 && cfile && cfile->symlink_target) {
53738c8a9a5SSteve French 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
53838c8a9a5SSteve French 				if (!idata->symlink_target)
53938c8a9a5SSteve French 					rc = -ENOMEM;
54038c8a9a5SSteve French 			}
54138c8a9a5SSteve French 			if (rc == 0) {
54238c8a9a5SSteve French 				qi_rsp = (struct smb2_query_info_rsp *)
543886b7fb4SSteve French 					rsp_iov[i + 1].iov_base;
54438c8a9a5SSteve French 				rc = smb2_validate_and_copy_iov(
54538c8a9a5SSteve French 					le16_to_cpu(qi_rsp->OutputBufferOffset),
54638c8a9a5SSteve French 					le32_to_cpu(qi_rsp->OutputBufferLength),
547886b7fb4SSteve French 					&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
54838c8a9a5SSteve French 					(char *)&idata->posix_fi);
54938c8a9a5SSteve French 			}
550a90f37e3SSteve French 			if (rc == 0)
551a90f37e3SSteve French 				rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
55238c8a9a5SSteve French 
553886b7fb4SSteve French 			SMB2_query_info_free(&rqst[num_rqst++]);
55438c8a9a5SSteve French 			if (rc)
555886b7fb4SSteve French 				trace_smb3_posix_query_info_compound_err(xid,  ses->Suid,
556886b7fb4SSteve French 									 tcon->tid, rc);
55738c8a9a5SSteve French 			else
558886b7fb4SSteve French 				trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
559886b7fb4SSteve French 									  tcon->tid);
56038c8a9a5SSteve French 			break;
56138c8a9a5SSteve French 		case SMB2_OP_DELETE:
56238c8a9a5SSteve French 			if (rc)
56338c8a9a5SSteve French 				trace_smb3_delete_err(xid,  ses->Suid, tcon->tid, rc);
564b6e27f7fSSteve French 			else {
565b6e27f7fSSteve French 				/*
566b6e27f7fSSteve French 				 * If dentry (hence, inode) is NULL, lease break is going to
567b6e27f7fSSteve French 				 * take care of degrading leases on handles for deleted files.
568b6e27f7fSSteve French 				 */
569b6e27f7fSSteve French 				if (inode)
570b6e27f7fSSteve French 					cifs_mark_open_handles_for_deleted_file(inode, full_path);
57138c8a9a5SSteve French 				trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
572b6e27f7fSSteve French 			}
57338c8a9a5SSteve French 			break;
57438c8a9a5SSteve French 		case SMB2_OP_MKDIR:
57538c8a9a5SSteve French 			if (rc)
57638c8a9a5SSteve French 				trace_smb3_mkdir_err(xid,  ses->Suid, tcon->tid, rc);
57738c8a9a5SSteve French 			else
57838c8a9a5SSteve French 				trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
57938c8a9a5SSteve French 			break;
58038c8a9a5SSteve French 		case SMB2_OP_HARDLINK:
58138c8a9a5SSteve French 			if (rc)
58238c8a9a5SSteve French 				trace_smb3_hardlink_err(xid,  ses->Suid, tcon->tid, rc);
58338c8a9a5SSteve French 			else
58438c8a9a5SSteve French 				trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
585886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
58638c8a9a5SSteve French 			break;
58738c8a9a5SSteve French 		case SMB2_OP_RENAME:
58838c8a9a5SSteve French 			if (rc)
58938c8a9a5SSteve French 				trace_smb3_rename_err(xid,  ses->Suid, tcon->tid, rc);
59038c8a9a5SSteve French 			else
59138c8a9a5SSteve French 				trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
592886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
59338c8a9a5SSteve French 			break;
59438c8a9a5SSteve French 		case SMB2_OP_RMDIR:
59538c8a9a5SSteve French 			if (rc)
59638c8a9a5SSteve French 				trace_smb3_rmdir_err(xid,  ses->Suid, tcon->tid, rc);
59738c8a9a5SSteve French 			else
59838c8a9a5SSteve French 				trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
599886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
60038c8a9a5SSteve French 			break;
60138c8a9a5SSteve French 		case SMB2_OP_SET_EOF:
60238c8a9a5SSteve French 			if (rc)
60338c8a9a5SSteve French 				trace_smb3_set_eof_err(xid,  ses->Suid, tcon->tid, rc);
60438c8a9a5SSteve French 			else
60538c8a9a5SSteve French 				trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
606886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
60738c8a9a5SSteve French 			break;
60838c8a9a5SSteve French 		case SMB2_OP_SET_INFO:
60938c8a9a5SSteve French 			if (rc)
61038c8a9a5SSteve French 				trace_smb3_set_info_compound_err(xid,  ses->Suid,
61138c8a9a5SSteve French 								 tcon->tid, rc);
61238c8a9a5SSteve French 			else
61338c8a9a5SSteve French 				trace_smb3_set_info_compound_done(xid, ses->Suid,
61438c8a9a5SSteve French 								  tcon->tid);
615886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
61638c8a9a5SSteve French 			break;
61702bcf865SSteve French 		case SMB2_OP_SET_REPARSE:
61802bcf865SSteve French 			if (rc) {
61902bcf865SSteve French 				trace_smb3_set_reparse_compound_err(xid,  ses->Suid,
62002bcf865SSteve French 								    tcon->tid, rc);
62102bcf865SSteve French 			} else {
62202bcf865SSteve French 				trace_smb3_set_reparse_compound_done(xid, ses->Suid,
62302bcf865SSteve French 								     tcon->tid);
62402bcf865SSteve French 			}
62502bcf865SSteve French 			SMB2_ioctl_free(&rqst[num_rqst++]);
62602bcf865SSteve French 			break;
627a158bb66SSteve French 		case SMB2_OP_GET_REPARSE:
628a158bb66SSteve French 			if (!rc) {
629a158bb66SSteve French 				iov = &rsp_iov[i + 1];
630a158bb66SSteve French 				idata = in_iov[i].iov_base;
631a158bb66SSteve French 				idata->reparse.io.iov = *iov;
632a158bb66SSteve French 				idata->reparse.io.buftype = resp_buftype[i + 1];
633a158bb66SSteve French 				rbuf = reparse_buf_ptr(iov);
634a158bb66SSteve French 				if (IS_ERR(rbuf)) {
635a158bb66SSteve French 					rc = PTR_ERR(rbuf);
636a158bb66SSteve French 					trace_smb3_set_reparse_compound_err(xid,  ses->Suid,
637a158bb66SSteve French 									    tcon->tid, rc);
638a158bb66SSteve French 				} else {
639a158bb66SSteve French 					idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
640a158bb66SSteve French 					trace_smb3_set_reparse_compound_done(xid, ses->Suid,
641a158bb66SSteve French 									     tcon->tid);
642a158bb66SSteve French 				}
643a158bb66SSteve French 				memset(iov, 0, sizeof(*iov));
644a158bb66SSteve French 				resp_buftype[i + 1] = CIFS_NO_BUFFER;
645a158bb66SSteve French 			} else {
646a158bb66SSteve French 				trace_smb3_set_reparse_compound_err(xid,  ses->Suid,
647a158bb66SSteve French 								    tcon->tid, rc);
648a158bb66SSteve French 			}
649a158bb66SSteve French 			SMB2_ioctl_free(&rqst[num_rqst++]);
650a158bb66SSteve French 			break;
65138c8a9a5SSteve French 		}
652886b7fb4SSteve French 	}
653886b7fb4SSteve French 	SMB2_close_free(&rqst[num_rqst]);
65438c8a9a5SSteve French 
655886b7fb4SSteve French 	num_cmds += 2;
656c5f44a3dSPaulo Alcantara 	if (out_iov && out_buftype) {
657886b7fb4SSteve French 		memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
658886b7fb4SSteve French 		memcpy(out_buftype, resp_buftype,
659886b7fb4SSteve French 		       num_cmds * sizeof(*out_buftype));
66038c8a9a5SSteve French 	} else {
661886b7fb4SSteve French 		for (i = 0; i < num_cmds; i++)
662886b7fb4SSteve French 			free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
66338c8a9a5SSteve French 	}
664433042a9SShyam Prasad N 	num_cmds -= 2; /* correct num_cmds as there could be a retry */
66538c8a9a5SSteve French 	kfree(vars);
666433042a9SShyam Prasad N 
667433042a9SShyam Prasad N 	if (is_replayable_error(rc) &&
668433042a9SShyam Prasad N 	    smb2_should_replay(tcon, &retries, &cur_sleep))
669433042a9SShyam Prasad N 		goto replay_again;
670433042a9SShyam Prasad N 
671433042a9SShyam Prasad N 	if (cfile)
672433042a9SShyam Prasad N 		cifsFileInfo_put(cfile);
673433042a9SShyam Prasad N 
67438c8a9a5SSteve French 	return rc;
67538c8a9a5SSteve French }
67638c8a9a5SSteve French 
6775f71ebc4SPaulo Alcantara static int parse_create_response(struct cifs_open_info_data *data,
6785f71ebc4SPaulo Alcantara 				 struct cifs_sb_info *cifs_sb,
6795f71ebc4SPaulo Alcantara 				 const struct kvec *iov)
6805f71ebc4SPaulo Alcantara {
6815f71ebc4SPaulo Alcantara 	struct smb2_create_rsp *rsp = iov->iov_base;
6825f71ebc4SPaulo Alcantara 	bool reparse_point = false;
6835f71ebc4SPaulo Alcantara 	u32 tag = 0;
6845f71ebc4SPaulo Alcantara 	int rc = 0;
6855f71ebc4SPaulo Alcantara 
6865f71ebc4SPaulo Alcantara 	switch (rsp->hdr.Status) {
6877fb77d9cSPaulo Alcantara 	case STATUS_IO_REPARSE_TAG_NOT_HANDLED:
6887fb77d9cSPaulo Alcantara 		reparse_point = true;
6897fb77d9cSPaulo Alcantara 		break;
6905f71ebc4SPaulo Alcantara 	case STATUS_STOPPED_ON_SYMLINK:
6915f71ebc4SPaulo Alcantara 		rc = smb2_parse_symlink_response(cifs_sb, iov,
6925f71ebc4SPaulo Alcantara 						 &data->symlink_target);
6935f71ebc4SPaulo Alcantara 		if (rc)
6945f71ebc4SPaulo Alcantara 			return rc;
6955f71ebc4SPaulo Alcantara 		tag = IO_REPARSE_TAG_SYMLINK;
6965f71ebc4SPaulo Alcantara 		reparse_point = true;
6975f71ebc4SPaulo Alcantara 		break;
6985f71ebc4SPaulo Alcantara 	case STATUS_SUCCESS:
6995f71ebc4SPaulo Alcantara 		reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
7005f71ebc4SPaulo Alcantara 		break;
7015f71ebc4SPaulo Alcantara 	}
7025f71ebc4SPaulo Alcantara 	data->reparse_point = reparse_point;
703df32e887SPaulo Alcantara 	data->reparse.tag = tag;
7045f71ebc4SPaulo Alcantara 	return rc;
7055f71ebc4SPaulo Alcantara }
7065f71ebc4SPaulo Alcantara 
7078b4e285dSPaulo Alcantara int smb2_query_path_info(const unsigned int xid,
7088b4e285dSPaulo Alcantara 			 struct cifs_tcon *tcon,
7098b4e285dSPaulo Alcantara 			 struct cifs_sb_info *cifs_sb,
7108b4e285dSPaulo Alcantara 			 const char *full_path,
7118b4e285dSPaulo Alcantara 			 struct cifs_open_info_data *data)
71238c8a9a5SSteve French {
71338c8a9a5SSteve French 	__u32 create_options = 0;
71438c8a9a5SSteve French 	struct cifsFileInfo *cfile;
71538c8a9a5SSteve French 	struct cached_fid *cfid = NULL;
7165f71ebc4SPaulo Alcantara 	struct smb2_hdr *hdr;
717a158bb66SSteve French 	struct kvec in_iov[2], out_iov[3] = {};
718c5f44a3dSPaulo Alcantara 	int out_buftype[3] = {};
719cfb8f73dSPaulo Alcantara 	int cmds[2];
72038c8a9a5SSteve French 	bool islink;
721a158bb66SSteve French 	int i, num_cmds;
72238c8a9a5SSteve French 	int rc, rc2;
72338c8a9a5SSteve French 
7248b4e285dSPaulo Alcantara 	data->adjust_tz = false;
7258b4e285dSPaulo Alcantara 	data->reparse_point = false;
72638c8a9a5SSteve French 
727cfb8f73dSPaulo Alcantara 	/*
728cfb8f73dSPaulo Alcantara 	 * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX.
729cfb8f73dSPaulo Alcantara 	 * Create SMB2_query_posix_info worker function to do non-compounded
730cfb8f73dSPaulo Alcantara 	 * query when we already have an open file handle for this. For now this
731cfb8f73dSPaulo Alcantara 	 * is fast enough (always using the compounded version).
732cfb8f73dSPaulo Alcantara 	 */
733cfb8f73dSPaulo Alcantara 	if (!tcon->posix_extensions) {
734cfb8f73dSPaulo Alcantara 		if (*full_path) {
73538c8a9a5SSteve French 			rc = -ENOENT;
736cfb8f73dSPaulo Alcantara 		} else {
737cfb8f73dSPaulo Alcantara 			rc = open_cached_dir(xid, tcon, full_path,
738cfb8f73dSPaulo Alcantara 					     cifs_sb, false, &cfid);
739cfb8f73dSPaulo Alcantara 		}
74038c8a9a5SSteve French 		/* If it is a root and its handle is cached then use it */
74138c8a9a5SSteve French 		if (!rc) {
74238c8a9a5SSteve French 			if (cfid->file_all_info_is_valid) {
743cfb8f73dSPaulo Alcantara 				memcpy(&data->fi, &cfid->file_all_info,
744cfb8f73dSPaulo Alcantara 				       sizeof(data->fi));
74538c8a9a5SSteve French 			} else {
746cfb8f73dSPaulo Alcantara 				rc = SMB2_query_info(xid, tcon,
747cfb8f73dSPaulo Alcantara 						     cfid->fid.persistent_fid,
748cfb8f73dSPaulo Alcantara 						     cfid->fid.volatile_fid,
749cfb8f73dSPaulo Alcantara 						     &data->fi);
75038c8a9a5SSteve French 			}
75138c8a9a5SSteve French 			close_cached_dir(cfid);
75238c8a9a5SSteve French 			return rc;
75338c8a9a5SSteve French 		}
754cfb8f73dSPaulo Alcantara 		cmds[0] = SMB2_OP_QUERY_INFO;
755cfb8f73dSPaulo Alcantara 	} else {
756cfb8f73dSPaulo Alcantara 		cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
757cfb8f73dSPaulo Alcantara 	}
75838c8a9a5SSteve French 
759a158bb66SSteve French 	in_iov[0].iov_base = data;
760a158bb66SSteve French 	in_iov[0].iov_len = sizeof(*data);
761a158bb66SSteve French 	in_iov[1] = in_iov[0];
762886b7fb4SSteve French 
76338c8a9a5SSteve French 	cifs_get_readable_path(tcon, full_path, &cfile);
764886b7fb4SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
765886b7fb4SSteve French 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
766a90f37e3SSteve French 			      create_options, ACL_NO_MODE, in_iov,
767f93d145fSMeetakshi Setiya 			      cmds, 1, cfile, out_iov, out_buftype, NULL);
7685f71ebc4SPaulo Alcantara 	hdr = out_iov[0].iov_base;
7695f71ebc4SPaulo Alcantara 	/*
7705f71ebc4SPaulo Alcantara 	 * If first iov is unset, then SMB session was dropped or we've got a
7715f71ebc4SPaulo Alcantara 	 * cached open file (@cfile).
7725f71ebc4SPaulo Alcantara 	 */
7735f71ebc4SPaulo Alcantara 	if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
77438c8a9a5SSteve French 		goto out;
77538c8a9a5SSteve French 
7765f71ebc4SPaulo Alcantara 	switch (rc) {
7775f71ebc4SPaulo Alcantara 	case 0:
7785f71ebc4SPaulo Alcantara 	case -EOPNOTSUPP:
779cfb8f73dSPaulo Alcantara 		/*
780cfb8f73dSPaulo Alcantara 		 * BB TODO: When support for special files added to Samba
781cfb8f73dSPaulo Alcantara 		 * re-verify this path.
782cfb8f73dSPaulo Alcantara 		 */
7835f71ebc4SPaulo Alcantara 		rc = parse_create_response(data, cifs_sb, &out_iov[0]);
7845f71ebc4SPaulo Alcantara 		if (rc || !data->reparse_point)
7855f71ebc4SPaulo Alcantara 			goto out;
7865f71ebc4SPaulo Alcantara 
787a158bb66SSteve French 		if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
788a158bb66SSteve French 			/* symlink already parsed in create response */
789a158bb66SSteve French 			num_cmds = 1;
790a158bb66SSteve French 		} else {
791a158bb66SSteve French 			cmds[1] = SMB2_OP_GET_REPARSE;
792a158bb66SSteve French 			num_cmds = 2;
793a158bb66SSteve French 		}
79438c8a9a5SSteve French 		create_options |= OPEN_REPARSE_POINT;
79538c8a9a5SSteve French 		cifs_get_readable_path(tcon, full_path, &cfile);
79638c8a9a5SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
79738c8a9a5SSteve French 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
798a90f37e3SSteve French 				      create_options, ACL_NO_MODE, in_iov,
799f93d145fSMeetakshi Setiya 				      cmds, num_cmds, cfile, NULL, NULL, NULL);
8005f71ebc4SPaulo Alcantara 		break;
8015f71ebc4SPaulo Alcantara 	case -EREMOTE:
8025f71ebc4SPaulo Alcantara 		break;
8035f71ebc4SPaulo Alcantara 	default:
8045f71ebc4SPaulo Alcantara 		if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
8055f71ebc4SPaulo Alcantara 			break;
80638c8a9a5SSteve French 		rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
80738c8a9a5SSteve French 						     full_path, &islink);
80838c8a9a5SSteve French 		if (rc2) {
80938c8a9a5SSteve French 			rc = rc2;
81038c8a9a5SSteve French 			goto out;
81138c8a9a5SSteve French 		}
81238c8a9a5SSteve French 		if (islink)
81338c8a9a5SSteve French 			rc = -EREMOTE;
81438c8a9a5SSteve French 	}
81538c8a9a5SSteve French 
81638c8a9a5SSteve French out:
817a158bb66SSteve French 	for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
818a158bb66SSteve French 		free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
81938c8a9a5SSteve French 	return rc;
82038c8a9a5SSteve French }
82138c8a9a5SSteve French 
82238c8a9a5SSteve French int
82338c8a9a5SSteve French smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
82438c8a9a5SSteve French 	   struct cifs_tcon *tcon, const char *name,
82538c8a9a5SSteve French 	   struct cifs_sb_info *cifs_sb)
82638c8a9a5SSteve French {
82738c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name,
82838c8a9a5SSteve French 				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
829a90f37e3SSteve French 				CREATE_NOT_FILE, mode,
830a90f37e3SSteve French 				NULL, &(int){SMB2_OP_MKDIR}, 1,
831f93d145fSMeetakshi Setiya 				NULL, NULL, NULL, NULL);
83238c8a9a5SSteve French }
83338c8a9a5SSteve French 
83438c8a9a5SSteve French void
83538c8a9a5SSteve French smb2_mkdir_setinfo(struct inode *inode, const char *name,
83638c8a9a5SSteve French 		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
83738c8a9a5SSteve French 		   const unsigned int xid)
83838c8a9a5SSteve French {
839886b7fb4SSteve French 	FILE_BASIC_INFO data = {};
84038c8a9a5SSteve French 	struct cifsInodeInfo *cifs_i;
84138c8a9a5SSteve French 	struct cifsFileInfo *cfile;
842886b7fb4SSteve French 	struct kvec in_iov;
84338c8a9a5SSteve French 	u32 dosattrs;
84438c8a9a5SSteve French 	int tmprc;
84538c8a9a5SSteve French 
846886b7fb4SSteve French 	in_iov.iov_base = &data;
847886b7fb4SSteve French 	in_iov.iov_len = sizeof(data);
84838c8a9a5SSteve French 	cifs_i = CIFS_I(inode);
84938c8a9a5SSteve French 	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
85038c8a9a5SSteve French 	data.Attributes = cpu_to_le32(dosattrs);
85138c8a9a5SSteve French 	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
85238c8a9a5SSteve French 	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
85338c8a9a5SSteve French 				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
854886b7fb4SSteve French 				 CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
855886b7fb4SSteve French 				 &(int){SMB2_OP_SET_INFO}, 1,
856f93d145fSMeetakshi Setiya 				 cfile, NULL, NULL, NULL);
85738c8a9a5SSteve French 	if (tmprc == 0)
85838c8a9a5SSteve French 		cifs_i->cifsAttrs = dosattrs;
85938c8a9a5SSteve French }
86038c8a9a5SSteve French 
86138c8a9a5SSteve French int
86238c8a9a5SSteve French smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
86338c8a9a5SSteve French 	   struct cifs_sb_info *cifs_sb)
86438c8a9a5SSteve French {
86538c8a9a5SSteve French 	drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
866886b7fb4SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name,
867886b7fb4SSteve French 				DELETE, FILE_OPEN, CREATE_NOT_FILE,
868a90f37e3SSteve French 				ACL_NO_MODE, NULL,
869a90f37e3SSteve French 				&(int){SMB2_OP_RMDIR}, 1,
870f93d145fSMeetakshi Setiya 				NULL, NULL, NULL, NULL);
87138c8a9a5SSteve French }
87238c8a9a5SSteve French 
87338c8a9a5SSteve French int
87438c8a9a5SSteve French smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
875f93d145fSMeetakshi Setiya 	    struct cifs_sb_info *cifs_sb, struct dentry *dentry)
87638c8a9a5SSteve French {
87738c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
87838c8a9a5SSteve French 				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
879a90f37e3SSteve French 				ACL_NO_MODE, NULL,
880a90f37e3SSteve French 				&(int){SMB2_OP_DELETE}, 1,
881f93d145fSMeetakshi Setiya 				NULL, NULL, NULL, dentry);
88238c8a9a5SSteve French }
88338c8a9a5SSteve French 
884c586b0c7SPaulo Alcantara static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
88538c8a9a5SSteve French 			      const char *from_name, const char *to_name,
886c586b0c7SPaulo Alcantara 			      struct cifs_sb_info *cifs_sb,
887c586b0c7SPaulo Alcantara 			      __u32 create_options, __u32 access,
888f93d145fSMeetakshi Setiya 			      int command, struct cifsFileInfo *cfile,
889f93d145fSMeetakshi Setiya 				  struct dentry *dentry)
89038c8a9a5SSteve French {
891886b7fb4SSteve French 	struct kvec in_iov;
89238c8a9a5SSteve French 	__le16 *smb2_to_name = NULL;
89338c8a9a5SSteve French 	int rc;
89438c8a9a5SSteve French 
89538c8a9a5SSteve French 	smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
89638c8a9a5SSteve French 	if (smb2_to_name == NULL) {
89738c8a9a5SSteve French 		rc = -ENOMEM;
89838c8a9a5SSteve French 		goto smb2_rename_path;
89938c8a9a5SSteve French 	}
900886b7fb4SSteve French 	in_iov.iov_base = smb2_to_name;
901886b7fb4SSteve French 	in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
90238c8a9a5SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
903a90f37e3SSteve French 			      FILE_OPEN, create_options, ACL_NO_MODE,
904f93d145fSMeetakshi Setiya 			      &in_iov, &command, 1, cfile, NULL, NULL, dentry);
90538c8a9a5SSteve French smb2_rename_path:
90638c8a9a5SSteve French 	kfree(smb2_to_name);
90738c8a9a5SSteve French 	return rc;
90838c8a9a5SSteve French }
90938c8a9a5SSteve French 
910c586b0c7SPaulo Alcantara int smb2_rename_path(const unsigned int xid,
911c586b0c7SPaulo Alcantara 		     struct cifs_tcon *tcon,
912c586b0c7SPaulo Alcantara 		     struct dentry *source_dentry,
91338c8a9a5SSteve French 		     const char *from_name, const char *to_name,
91438c8a9a5SSteve French 		     struct cifs_sb_info *cifs_sb)
91538c8a9a5SSteve French {
91638c8a9a5SSteve French 	struct cifsFileInfo *cfile;
9178c944f8aSPaulo Alcantara 	__u32 co = file_create_options(source_dentry);
91838c8a9a5SSteve French 
91938c8a9a5SSteve French 	drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
92038c8a9a5SSteve French 	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
92138c8a9a5SSteve French 
922c586b0c7SPaulo Alcantara 	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
923f93d145fSMeetakshi Setiya 				  co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
92438c8a9a5SSteve French }
92538c8a9a5SSteve French 
9268c944f8aSPaulo Alcantara int smb2_create_hardlink(const unsigned int xid,
9278c944f8aSPaulo Alcantara 			 struct cifs_tcon *tcon,
9288c944f8aSPaulo Alcantara 			 struct dentry *source_dentry,
92938c8a9a5SSteve French 			 const char *from_name, const char *to_name,
93038c8a9a5SSteve French 			 struct cifs_sb_info *cifs_sb)
93138c8a9a5SSteve French {
9328c944f8aSPaulo Alcantara 	__u32 co = file_create_options(source_dentry);
9338c944f8aSPaulo Alcantara 
934c586b0c7SPaulo Alcantara 	return smb2_set_path_attr(xid, tcon, from_name, to_name,
9358c944f8aSPaulo Alcantara 				  cifs_sb, co, FILE_READ_ATTRIBUTES,
936f93d145fSMeetakshi Setiya 				  SMB2_OP_HARDLINK, NULL, NULL);
93738c8a9a5SSteve French }
93838c8a9a5SSteve French 
93938c8a9a5SSteve French int
94038c8a9a5SSteve French smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
94138c8a9a5SSteve French 		   const char *full_path, __u64 size,
942f93d145fSMeetakshi Setiya 		   struct cifs_sb_info *cifs_sb, bool set_alloc,
943f93d145fSMeetakshi Setiya 		   struct dentry *dentry)
94438c8a9a5SSteve French {
94538c8a9a5SSteve French 	struct cifsFileInfo *cfile;
946886b7fb4SSteve French 	struct kvec in_iov;
947886b7fb4SSteve French 	__le64 eof = cpu_to_le64(size);
94838c8a9a5SSteve French 
949886b7fb4SSteve French 	in_iov.iov_base = &eof;
950886b7fb4SSteve French 	in_iov.iov_len = sizeof(eof);
95138c8a9a5SSteve French 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
95238c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
953886b7fb4SSteve French 				FILE_WRITE_DATA, FILE_OPEN,
954886b7fb4SSteve French 				0, ACL_NO_MODE, &in_iov,
955886b7fb4SSteve French 				&(int){SMB2_OP_SET_EOF}, 1,
956f93d145fSMeetakshi Setiya 				cfile, NULL, NULL, dentry);
95738c8a9a5SSteve French }
95838c8a9a5SSteve French 
95938c8a9a5SSteve French int
96038c8a9a5SSteve French smb2_set_file_info(struct inode *inode, const char *full_path,
96138c8a9a5SSteve French 		   FILE_BASIC_INFO *buf, const unsigned int xid)
96238c8a9a5SSteve French {
96338c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
96438c8a9a5SSteve French 	struct tcon_link *tlink;
96538c8a9a5SSteve French 	struct cifs_tcon *tcon;
96638c8a9a5SSteve French 	struct cifsFileInfo *cfile;
967886b7fb4SSteve French 	struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
96838c8a9a5SSteve French 	int rc;
96938c8a9a5SSteve French 
97038c8a9a5SSteve French 	if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
97138c8a9a5SSteve French 	    (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
97238c8a9a5SSteve French 	    (buf->Attributes == 0))
97338c8a9a5SSteve French 		return 0; /* would be a no op, no sense sending this */
97438c8a9a5SSteve French 
97538c8a9a5SSteve French 	tlink = cifs_sb_tlink(cifs_sb);
97638c8a9a5SSteve French 	if (IS_ERR(tlink))
97738c8a9a5SSteve French 		return PTR_ERR(tlink);
97838c8a9a5SSteve French 	tcon = tlink_tcon(tlink);
97938c8a9a5SSteve French 
98038c8a9a5SSteve French 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
98138c8a9a5SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
98238c8a9a5SSteve French 			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
983886b7fb4SSteve French 			      0, ACL_NO_MODE, &in_iov,
984a90f37e3SSteve French 			      &(int){SMB2_OP_SET_INFO}, 1,
985f93d145fSMeetakshi Setiya 			      cfile, NULL, NULL, NULL);
98638c8a9a5SSteve French 	cifs_put_tlink(tlink);
98738c8a9a5SSteve French 	return rc;
98838c8a9a5SSteve French }
98902bcf865SSteve French 
99002bcf865SSteve French struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
99102bcf865SSteve French 				     struct super_block *sb,
99202bcf865SSteve French 				     const unsigned int xid,
99302bcf865SSteve French 				     struct cifs_tcon *tcon,
99402bcf865SSteve French 				     const char *full_path,
99502bcf865SSteve French 				     struct kvec *iov)
99602bcf865SSteve French {
99702bcf865SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
99802bcf865SSteve French 	struct cifsFileInfo *cfile;
99902bcf865SSteve French 	struct inode *new = NULL;
100002bcf865SSteve French 	struct kvec in_iov[2];
100102bcf865SSteve French 	int cmds[2];
100202bcf865SSteve French 	int da, co, cd;
100302bcf865SSteve French 	int rc;
100402bcf865SSteve French 
100502bcf865SSteve French 	da = SYNCHRONIZE | DELETE |
100602bcf865SSteve French 		FILE_READ_ATTRIBUTES |
100702bcf865SSteve French 		FILE_WRITE_ATTRIBUTES;
100802bcf865SSteve French 	co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
100902bcf865SSteve French 	cd = FILE_CREATE;
101002bcf865SSteve French 	cmds[0] = SMB2_OP_SET_REPARSE;
101102bcf865SSteve French 	in_iov[0] = *iov;
101202bcf865SSteve French 	in_iov[1].iov_base = data;
101302bcf865SSteve French 	in_iov[1].iov_len = sizeof(*data);
101402bcf865SSteve French 
101502bcf865SSteve French 	if (tcon->posix_extensions) {
101602bcf865SSteve French 		cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
101702bcf865SSteve French 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
101802bcf865SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
101902bcf865SSteve French 				      da, cd, co, ACL_NO_MODE, in_iov,
1020f93d145fSMeetakshi Setiya 				      cmds, 2, cfile, NULL, NULL, NULL);
102102bcf865SSteve French 		if (!rc) {
102202bcf865SSteve French 			rc = smb311_posix_get_inode_info(&new, full_path,
102302bcf865SSteve French 							 data, sb, xid);
102402bcf865SSteve French 		}
102502bcf865SSteve French 	} else {
102602bcf865SSteve French 		cmds[1] = SMB2_OP_QUERY_INFO;
102702bcf865SSteve French 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
102802bcf865SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
102902bcf865SSteve French 				      da, cd, co, ACL_NO_MODE, in_iov,
1030f93d145fSMeetakshi Setiya 				      cmds, 2, cfile, NULL, NULL, NULL);
103102bcf865SSteve French 		if (!rc) {
103202bcf865SSteve French 			rc = cifs_get_inode_info(&new, full_path,
103302bcf865SSteve French 						 data, sb, xid, NULL);
103402bcf865SSteve French 		}
103502bcf865SSteve French 	}
103602bcf865SSteve French 	return rc ? ERR_PTR(rc) : new;
103702bcf865SSteve French }
1038870c73abSPaulo Alcantara 
1039870c73abSPaulo Alcantara int smb2_query_reparse_point(const unsigned int xid,
1040870c73abSPaulo Alcantara 			     struct cifs_tcon *tcon,
1041870c73abSPaulo Alcantara 			     struct cifs_sb_info *cifs_sb,
1042870c73abSPaulo Alcantara 			     const char *full_path,
1043870c73abSPaulo Alcantara 			     u32 *tag, struct kvec *rsp,
1044870c73abSPaulo Alcantara 			     int *rsp_buftype)
1045870c73abSPaulo Alcantara {
1046870c73abSPaulo Alcantara 	struct cifs_open_info_data data = {};
1047870c73abSPaulo Alcantara 	struct cifsFileInfo *cfile;
1048870c73abSPaulo Alcantara 	struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };
1049870c73abSPaulo Alcantara 	int rc;
1050870c73abSPaulo Alcantara 
1051870c73abSPaulo Alcantara 	cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
1052870c73abSPaulo Alcantara 
1053870c73abSPaulo Alcantara 	cifs_get_readable_path(tcon, full_path, &cfile);
1054870c73abSPaulo Alcantara 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
1055870c73abSPaulo Alcantara 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
1056870c73abSPaulo Alcantara 			      OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov,
1057a90f37e3SSteve French 			      &(int){SMB2_OP_GET_REPARSE}, 1,
1058f93d145fSMeetakshi Setiya 			      cfile, NULL, NULL, NULL);
1059870c73abSPaulo Alcantara 	if (rc)
1060870c73abSPaulo Alcantara 		goto out;
1061870c73abSPaulo Alcantara 
1062870c73abSPaulo Alcantara 	*tag = data.reparse.tag;
1063870c73abSPaulo Alcantara 	*rsp = data.reparse.io.iov;
1064870c73abSPaulo Alcantara 	*rsp_buftype = data.reparse.io.buftype;
1065870c73abSPaulo Alcantara 	memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov));
1066870c73abSPaulo Alcantara 	data.reparse.io.buftype = CIFS_NO_BUFFER;
1067870c73abSPaulo Alcantara out:
1068870c73abSPaulo Alcantara 	cifs_free_open_info(&data);
1069870c73abSPaulo Alcantara 	return rc;
1070870c73abSPaulo Alcantara }
1071