xref: /openbmc/linux/fs/smb/client/smb2inode.c (revision 02bcf865)
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 
4138c8a9a5SSteve French /*
4238c8a9a5SSteve French  * note: If cfile is passed, the reference to it is dropped here.
4338c8a9a5SSteve French  * So make sure that you do not reuse cfile after return from this func.
4438c8a9a5SSteve French  *
45c5f44a3dSPaulo Alcantara  * If passing @out_iov and @out_buftype, ensure to make them both large enough
46c5f44a3dSPaulo Alcantara  * (>= 3) to hold all compounded responses.  Caller is also responsible for
47c5f44a3dSPaulo Alcantara  * freeing them up with free_rsp_buf().
4838c8a9a5SSteve French  */
4938c8a9a5SSteve French static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
5038c8a9a5SSteve French 			    struct cifs_sb_info *cifs_sb, const char *full_path,
51886b7fb4SSteve French 			    __u32 desired_access, __u32 create_disposition,
52886b7fb4SSteve French 			    __u32 create_options, umode_t mode, struct kvec *in_iov,
53886b7fb4SSteve French 			    int *cmds, int num_cmds, struct cifsFileInfo *cfile,
5438c8a9a5SSteve French 			    __u8 **extbuf, size_t *extbuflen,
55c5f44a3dSPaulo Alcantara 			    struct kvec *out_iov, int *out_buftype)
5638c8a9a5SSteve French {
57f4e5ceb6SPaulo Alcantara 	struct smb2_compound_vars *vars = NULL;
5838c8a9a5SSteve French 	struct kvec *rsp_iov;
5938c8a9a5SSteve French 	struct smb_rqst *rqst;
6038c8a9a5SSteve French 	int rc;
6138c8a9a5SSteve French 	__le16 *utf16_path = NULL;
6238c8a9a5SSteve French 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
6338c8a9a5SSteve French 	struct cifs_fid fid;
6438c8a9a5SSteve French 	struct cifs_ses *ses = tcon->ses;
6538c8a9a5SSteve French 	struct TCP_Server_Info *server;
66886b7fb4SSteve French 	int num_rqst = 0, i;
67886b7fb4SSteve French 	int resp_buftype[MAX_COMPOUND];
6838c8a9a5SSteve French 	struct smb2_query_info_rsp *qi_rsp = NULL;
6938c8a9a5SSteve French 	struct cifs_open_info_data *idata;
7038c8a9a5SSteve French 	int flags = 0;
7138c8a9a5SSteve French 	__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
7238c8a9a5SSteve French 	unsigned int size[2];
7338c8a9a5SSteve French 	void *data[2];
7438c8a9a5SSteve French 	int len;
7538c8a9a5SSteve French 
7638c8a9a5SSteve French 	vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
7738c8a9a5SSteve French 	if (vars == NULL)
7838c8a9a5SSteve French 		return -ENOMEM;
7938c8a9a5SSteve French 	rqst = &vars->rqst[0];
8038c8a9a5SSteve French 	rsp_iov = &vars->rsp_iov[0];
8138c8a9a5SSteve French 
8238c8a9a5SSteve French 	server = cifs_pick_channel(ses);
8338c8a9a5SSteve French 
8438c8a9a5SSteve French 	if (smb3_encryption_required(tcon))
8538c8a9a5SSteve French 		flags |= CIFS_TRANSFORM_REQ;
8638c8a9a5SSteve French 
87886b7fb4SSteve French 	for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
88886b7fb4SSteve French 		resp_buftype[i] = CIFS_NO_BUFFER;
8938c8a9a5SSteve French 
9038c8a9a5SSteve French 	/* We already have a handle so we can skip the open */
9138c8a9a5SSteve French 	if (cfile)
9238c8a9a5SSteve French 		goto after_open;
9338c8a9a5SSteve French 
9438c8a9a5SSteve French 	/* Open */
9538c8a9a5SSteve French 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
9638c8a9a5SSteve French 	if (!utf16_path) {
9738c8a9a5SSteve French 		rc = -ENOMEM;
9838c8a9a5SSteve French 		goto finished;
9938c8a9a5SSteve French 	}
10038c8a9a5SSteve French 
10138c8a9a5SSteve French 	vars->oparms = (struct cifs_open_parms) {
10238c8a9a5SSteve French 		.tcon = tcon,
10338c8a9a5SSteve French 		.path = full_path,
10438c8a9a5SSteve French 		.desired_access = desired_access,
10538c8a9a5SSteve French 		.disposition = create_disposition,
10638c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, create_options),
10738c8a9a5SSteve French 		.fid = &fid,
10838c8a9a5SSteve French 		.mode = mode,
10938c8a9a5SSteve French 		.cifs_sb = cifs_sb,
11038c8a9a5SSteve French 	};
11138c8a9a5SSteve French 
11238c8a9a5SSteve French 	rqst[num_rqst].rq_iov = &vars->open_iov[0];
11338c8a9a5SSteve French 	rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
11438c8a9a5SSteve French 	rc = SMB2_open_init(tcon, server,
11538c8a9a5SSteve French 			    &rqst[num_rqst], &oplock, &vars->oparms,
11638c8a9a5SSteve French 			    utf16_path);
11738c8a9a5SSteve French 	kfree(utf16_path);
11838c8a9a5SSteve French 	if (rc)
11938c8a9a5SSteve French 		goto finished;
12038c8a9a5SSteve French 
12138c8a9a5SSteve French 	smb2_set_next_command(tcon, &rqst[num_rqst]);
12238c8a9a5SSteve French  after_open:
12338c8a9a5SSteve French 	num_rqst++;
12438c8a9a5SSteve French 	rc = 0;
12538c8a9a5SSteve French 
126886b7fb4SSteve French 	for (i = 0; i < num_cmds; i++) {
12738c8a9a5SSteve French 		/* Operation */
128886b7fb4SSteve French 		switch (cmds[i]) {
12938c8a9a5SSteve French 		case SMB2_OP_QUERY_INFO:
130f4e5ceb6SPaulo Alcantara 			rqst[num_rqst].rq_iov = &vars->qi_iov;
13138c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
13238c8a9a5SSteve French 
133886b7fb4SSteve French 			if (cfile) {
13438c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
13538c8a9a5SSteve French 							  &rqst[num_rqst],
13638c8a9a5SSteve French 							  cfile->fid.persistent_fid,
13738c8a9a5SSteve French 							  cfile->fid.volatile_fid,
13838c8a9a5SSteve French 							  FILE_ALL_INFORMATION,
13938c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
14038c8a9a5SSteve French 							  sizeof(struct smb2_file_all_info) +
14138c8a9a5SSteve French 							  PATH_MAX * 2, 0, NULL);
142886b7fb4SSteve French 			} else {
14338c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
14438c8a9a5SSteve French 							  &rqst[num_rqst],
14538c8a9a5SSteve French 							  COMPOUND_FID,
14638c8a9a5SSteve French 							  COMPOUND_FID,
14738c8a9a5SSteve French 							  FILE_ALL_INFORMATION,
14838c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
14938c8a9a5SSteve French 							  sizeof(struct smb2_file_all_info) +
15038c8a9a5SSteve French 							  PATH_MAX * 2, 0, NULL);
15138c8a9a5SSteve French 				if (!rc) {
15238c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
15338c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
15438c8a9a5SSteve French 				}
15538c8a9a5SSteve French 			}
15638c8a9a5SSteve French 
15738c8a9a5SSteve French 			if (rc)
15838c8a9a5SSteve French 				goto finished;
15938c8a9a5SSteve French 			num_rqst++;
160886b7fb4SSteve French 			trace_smb3_query_info_compound_enter(xid, ses->Suid,
161886b7fb4SSteve French 							     tcon->tid, full_path);
16238c8a9a5SSteve French 			break;
16338c8a9a5SSteve French 		case SMB2_OP_POSIX_QUERY_INFO:
164f4e5ceb6SPaulo Alcantara 			rqst[num_rqst].rq_iov = &vars->qi_iov;
16538c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
16638c8a9a5SSteve French 
167886b7fb4SSteve French 			if (cfile) {
168886b7fb4SSteve French 				/* TBD: fix following to allow for longer SIDs */
16938c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
17038c8a9a5SSteve French 							  &rqst[num_rqst],
17138c8a9a5SSteve French 							  cfile->fid.persistent_fid,
17238c8a9a5SSteve French 							  cfile->fid.volatile_fid,
17338c8a9a5SSteve French 							  SMB_FIND_FILE_POSIX_INFO,
17438c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
175886b7fb4SSteve French 							  sizeof(struct smb311_posix_qinfo *) +
176886b7fb4SSteve French 							  (PATH_MAX * 2) +
17738c8a9a5SSteve French 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
178886b7fb4SSteve French 			} else {
17938c8a9a5SSteve French 				rc = SMB2_query_info_init(tcon, server,
18038c8a9a5SSteve French 							  &rqst[num_rqst],
18138c8a9a5SSteve French 							  COMPOUND_FID,
18238c8a9a5SSteve French 							  COMPOUND_FID,
18338c8a9a5SSteve French 							  SMB_FIND_FILE_POSIX_INFO,
18438c8a9a5SSteve French 							  SMB2_O_INFO_FILE, 0,
185886b7fb4SSteve French 							  sizeof(struct smb311_posix_qinfo *) +
186886b7fb4SSteve French 							  (PATH_MAX * 2) +
18738c8a9a5SSteve French 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
18838c8a9a5SSteve French 				if (!rc) {
18938c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
19038c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
19138c8a9a5SSteve French 				}
19238c8a9a5SSteve French 			}
19338c8a9a5SSteve French 
19438c8a9a5SSteve French 			if (rc)
19538c8a9a5SSteve French 				goto finished;
19638c8a9a5SSteve French 			num_rqst++;
197886b7fb4SSteve French 			trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
198886b7fb4SSteve French 								   tcon->tid, full_path);
19938c8a9a5SSteve French 			break;
20038c8a9a5SSteve French 		case SMB2_OP_DELETE:
20138c8a9a5SSteve French 			trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
20238c8a9a5SSteve French 			break;
20338c8a9a5SSteve French 		case SMB2_OP_MKDIR:
20438c8a9a5SSteve French 			/*
20538c8a9a5SSteve French 			 * Directories are created through parameters in the
20638c8a9a5SSteve French 			 * SMB2_open() call.
20738c8a9a5SSteve French 			 */
20838c8a9a5SSteve French 			trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
20938c8a9a5SSteve French 			break;
21038c8a9a5SSteve French 		case SMB2_OP_RMDIR:
21138c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
21238c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
21338c8a9a5SSteve French 
21438c8a9a5SSteve French 			size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
21538c8a9a5SSteve French 			data[0] = &delete_pending[0];
21638c8a9a5SSteve French 
21738c8a9a5SSteve French 			rc = SMB2_set_info_init(tcon, server,
21838c8a9a5SSteve French 						&rqst[num_rqst], COMPOUND_FID,
21938c8a9a5SSteve French 						COMPOUND_FID, current->tgid,
22038c8a9a5SSteve French 						FILE_DISPOSITION_INFORMATION,
22138c8a9a5SSteve French 						SMB2_O_INFO_FILE, 0, data, size);
22238c8a9a5SSteve French 			if (rc)
22338c8a9a5SSteve French 				goto finished;
22438c8a9a5SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
22538c8a9a5SSteve French 			smb2_set_related(&rqst[num_rqst++]);
22638c8a9a5SSteve French 			trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
22738c8a9a5SSteve French 			break;
22838c8a9a5SSteve French 		case SMB2_OP_SET_EOF:
22938c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
23038c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
23138c8a9a5SSteve French 
232886b7fb4SSteve French 			size[0] = in_iov[i].iov_len;
233886b7fb4SSteve French 			data[0] = in_iov[i].iov_base;
23438c8a9a5SSteve French 
23538c8a9a5SSteve French 			if (cfile) {
23638c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
23738c8a9a5SSteve French 							&rqst[num_rqst],
23838c8a9a5SSteve French 							cfile->fid.persistent_fid,
23938c8a9a5SSteve French 							cfile->fid.volatile_fid,
24038c8a9a5SSteve French 							current->tgid,
24138c8a9a5SSteve French 							FILE_END_OF_FILE_INFORMATION,
24238c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0,
24338c8a9a5SSteve French 							data, size);
24438c8a9a5SSteve French 			} else {
24538c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
24638c8a9a5SSteve French 							&rqst[num_rqst],
24738c8a9a5SSteve French 							COMPOUND_FID,
24838c8a9a5SSteve French 							COMPOUND_FID,
24938c8a9a5SSteve French 							current->tgid,
25038c8a9a5SSteve French 							FILE_END_OF_FILE_INFORMATION,
25138c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0,
25238c8a9a5SSteve French 							data, size);
25338c8a9a5SSteve French 				if (!rc) {
25438c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
25538c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
25638c8a9a5SSteve French 				}
25738c8a9a5SSteve French 			}
25838c8a9a5SSteve French 			if (rc)
25938c8a9a5SSteve French 				goto finished;
26038c8a9a5SSteve French 			num_rqst++;
26138c8a9a5SSteve French 			trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
26238c8a9a5SSteve French 			break;
26338c8a9a5SSteve French 		case SMB2_OP_SET_INFO:
26438c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
26538c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 1;
26638c8a9a5SSteve French 
267886b7fb4SSteve French 			size[0] = in_iov[i].iov_len;
268886b7fb4SSteve French 			data[0] = in_iov[i].iov_base;
26938c8a9a5SSteve French 
270886b7fb4SSteve French 			if (cfile) {
27138c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
27238c8a9a5SSteve French 							&rqst[num_rqst],
27338c8a9a5SSteve French 							cfile->fid.persistent_fid,
27438c8a9a5SSteve French 							cfile->fid.volatile_fid, current->tgid,
27538c8a9a5SSteve French 							FILE_BASIC_INFORMATION,
27638c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
277886b7fb4SSteve French 			} else {
27838c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
27938c8a9a5SSteve French 							&rqst[num_rqst],
28038c8a9a5SSteve French 							COMPOUND_FID,
28138c8a9a5SSteve French 							COMPOUND_FID, current->tgid,
28238c8a9a5SSteve French 							FILE_BASIC_INFORMATION,
28338c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
28438c8a9a5SSteve French 				if (!rc) {
28538c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
28638c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
28738c8a9a5SSteve French 				}
28838c8a9a5SSteve French 			}
28938c8a9a5SSteve French 
29038c8a9a5SSteve French 			if (rc)
29138c8a9a5SSteve French 				goto finished;
29238c8a9a5SSteve French 			num_rqst++;
293886b7fb4SSteve French 			trace_smb3_set_info_compound_enter(xid, ses->Suid,
294886b7fb4SSteve French 							   tcon->tid, full_path);
29538c8a9a5SSteve French 			break;
29638c8a9a5SSteve French 		case SMB2_OP_RENAME:
29738c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
29838c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 2;
29938c8a9a5SSteve French 
300886b7fb4SSteve French 			len = in_iov[i].iov_len;
30138c8a9a5SSteve French 
30238c8a9a5SSteve French 			vars->rename_info.ReplaceIfExists = 1;
30338c8a9a5SSteve French 			vars->rename_info.RootDirectory = 0;
30438c8a9a5SSteve French 			vars->rename_info.FileNameLength = cpu_to_le32(len);
30538c8a9a5SSteve French 
30638c8a9a5SSteve French 			size[0] = sizeof(struct smb2_file_rename_info);
30738c8a9a5SSteve French 			data[0] = &vars->rename_info;
30838c8a9a5SSteve French 
30938c8a9a5SSteve French 			size[1] = len + 2 /* null */;
310886b7fb4SSteve French 			data[1] = in_iov[i].iov_base;
31138c8a9a5SSteve French 
312886b7fb4SSteve French 			if (cfile) {
31338c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
31438c8a9a5SSteve French 							&rqst[num_rqst],
31538c8a9a5SSteve French 							cfile->fid.persistent_fid,
31638c8a9a5SSteve French 							cfile->fid.volatile_fid,
31738c8a9a5SSteve French 							current->tgid, FILE_RENAME_INFORMATION,
31838c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
319886b7fb4SSteve French 			} else {
32038c8a9a5SSteve French 				rc = SMB2_set_info_init(tcon, server,
32138c8a9a5SSteve French 							&rqst[num_rqst],
32238c8a9a5SSteve French 							COMPOUND_FID, COMPOUND_FID,
32338c8a9a5SSteve French 							current->tgid, FILE_RENAME_INFORMATION,
32438c8a9a5SSteve French 							SMB2_O_INFO_FILE, 0, data, size);
32538c8a9a5SSteve French 				if (!rc) {
32638c8a9a5SSteve French 					smb2_set_next_command(tcon, &rqst[num_rqst]);
32738c8a9a5SSteve French 					smb2_set_related(&rqst[num_rqst]);
32838c8a9a5SSteve French 				}
32938c8a9a5SSteve French 			}
33038c8a9a5SSteve French 			if (rc)
33138c8a9a5SSteve French 				goto finished;
33238c8a9a5SSteve French 			num_rqst++;
33338c8a9a5SSteve French 			trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
33438c8a9a5SSteve French 			break;
33538c8a9a5SSteve French 		case SMB2_OP_HARDLINK:
33638c8a9a5SSteve French 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
33738c8a9a5SSteve French 			rqst[num_rqst].rq_nvec = 2;
33838c8a9a5SSteve French 
339886b7fb4SSteve French 			len = in_iov[i].iov_len;
34038c8a9a5SSteve French 
34138c8a9a5SSteve French 			vars->link_info.ReplaceIfExists = 0;
34238c8a9a5SSteve French 			vars->link_info.RootDirectory = 0;
34338c8a9a5SSteve French 			vars->link_info.FileNameLength = cpu_to_le32(len);
34438c8a9a5SSteve French 
34538c8a9a5SSteve French 			size[0] = sizeof(struct smb2_file_link_info);
34638c8a9a5SSteve French 			data[0] = &vars->link_info;
34738c8a9a5SSteve French 
34838c8a9a5SSteve French 			size[1] = len + 2 /* null */;
349886b7fb4SSteve French 			data[1] = in_iov[i].iov_base;
35038c8a9a5SSteve French 
35138c8a9a5SSteve French 			rc = SMB2_set_info_init(tcon, server,
35238c8a9a5SSteve French 						&rqst[num_rqst], COMPOUND_FID,
35338c8a9a5SSteve French 						COMPOUND_FID, current->tgid,
35438c8a9a5SSteve French 						FILE_LINK_INFORMATION,
35538c8a9a5SSteve French 						SMB2_O_INFO_FILE, 0, data, size);
35638c8a9a5SSteve French 			if (rc)
35738c8a9a5SSteve French 				goto finished;
35838c8a9a5SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
35938c8a9a5SSteve French 			smb2_set_related(&rqst[num_rqst++]);
36038c8a9a5SSteve French 			trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
36138c8a9a5SSteve French 			break;
36202bcf865SSteve French 		case SMB2_OP_SET_REPARSE:
36302bcf865SSteve French 			rqst[num_rqst].rq_iov = vars->io_iov;
36402bcf865SSteve French 			rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
36502bcf865SSteve French 
36602bcf865SSteve French 			rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
36702bcf865SSteve French 					     COMPOUND_FID, COMPOUND_FID,
36802bcf865SSteve French 					     FSCTL_SET_REPARSE_POINT,
36902bcf865SSteve French 					     in_iov[i].iov_base,
37002bcf865SSteve French 					     in_iov[i].iov_len, 0);
37102bcf865SSteve French 			if (rc)
37202bcf865SSteve French 				goto finished;
37302bcf865SSteve French 			smb2_set_next_command(tcon, &rqst[num_rqst]);
37402bcf865SSteve French 			smb2_set_related(&rqst[num_rqst++]);
37502bcf865SSteve French 			trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
37602bcf865SSteve French 							      tcon->tid, full_path);
37702bcf865SSteve French 			break;
37838c8a9a5SSteve French 		default:
37938c8a9a5SSteve French 			cifs_dbg(VFS, "Invalid command\n");
38038c8a9a5SSteve French 			rc = -EINVAL;
38138c8a9a5SSteve French 		}
382886b7fb4SSteve French 	}
38338c8a9a5SSteve French 	if (rc)
38438c8a9a5SSteve French 		goto finished;
38538c8a9a5SSteve French 
38638c8a9a5SSteve French 	/* We already have a handle so we can skip the close */
38738c8a9a5SSteve French 	if (cfile)
38838c8a9a5SSteve French 		goto after_close;
38938c8a9a5SSteve French 	/* Close */
39038c8a9a5SSteve French 	flags |= CIFS_CP_CREATE_CLOSE_OP;
391f4e5ceb6SPaulo Alcantara 	rqst[num_rqst].rq_iov = &vars->close_iov;
39238c8a9a5SSteve French 	rqst[num_rqst].rq_nvec = 1;
39338c8a9a5SSteve French 	rc = SMB2_close_init(tcon, server,
39438c8a9a5SSteve French 			     &rqst[num_rqst], COMPOUND_FID,
39538c8a9a5SSteve French 			     COMPOUND_FID, false);
39638c8a9a5SSteve French 	smb2_set_related(&rqst[num_rqst]);
39738c8a9a5SSteve French 	if (rc)
39838c8a9a5SSteve French 		goto finished;
39938c8a9a5SSteve French  after_close:
40038c8a9a5SSteve French 	num_rqst++;
40138c8a9a5SSteve French 
40238c8a9a5SSteve French 	if (cfile) {
40338c8a9a5SSteve French 		rc = compound_send_recv(xid, ses, server,
40438c8a9a5SSteve French 					flags, num_rqst - 2,
40538c8a9a5SSteve French 					&rqst[1], &resp_buftype[1],
40638c8a9a5SSteve French 					&rsp_iov[1]);
40738c8a9a5SSteve French 	} else
40838c8a9a5SSteve French 		rc = compound_send_recv(xid, ses, server,
40938c8a9a5SSteve French 					flags, num_rqst,
41038c8a9a5SSteve French 					rqst, resp_buftype,
41138c8a9a5SSteve French 					rsp_iov);
41238c8a9a5SSteve French 
41338c8a9a5SSteve French finished:
414886b7fb4SSteve French 	num_rqst = 0;
415886b7fb4SSteve French 	SMB2_open_free(&rqst[num_rqst++]);
41638c8a9a5SSteve French 	if (rc == -EREMCHG) {
41738c8a9a5SSteve French 		pr_warn_once("server share %s deleted\n", tcon->tree_name);
41838c8a9a5SSteve French 		tcon->need_reconnect = true;
41938c8a9a5SSteve French 	}
42038c8a9a5SSteve French 
421886b7fb4SSteve French 	for (i = 0; i < num_cmds; i++) {
422886b7fb4SSteve French 		switch (cmds[i]) {
42338c8a9a5SSteve French 		case SMB2_OP_QUERY_INFO:
424886b7fb4SSteve French 			idata = in_iov[i].iov_base;
42538c8a9a5SSteve French 			if (rc == 0 && cfile && cfile->symlink_target) {
42638c8a9a5SSteve French 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
42738c8a9a5SSteve French 				if (!idata->symlink_target)
42838c8a9a5SSteve French 					rc = -ENOMEM;
42938c8a9a5SSteve French 			}
43038c8a9a5SSteve French 			if (rc == 0) {
43138c8a9a5SSteve French 				qi_rsp = (struct smb2_query_info_rsp *)
432886b7fb4SSteve French 					rsp_iov[i + 1].iov_base;
43338c8a9a5SSteve French 				rc = smb2_validate_and_copy_iov(
43438c8a9a5SSteve French 					le16_to_cpu(qi_rsp->OutputBufferOffset),
43538c8a9a5SSteve French 					le32_to_cpu(qi_rsp->OutputBufferLength),
436886b7fb4SSteve French 					&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
43738c8a9a5SSteve French 			}
438886b7fb4SSteve French 			SMB2_query_info_free(&rqst[num_rqst++]);
43938c8a9a5SSteve French 			if (rc)
44038c8a9a5SSteve French 				trace_smb3_query_info_compound_err(xid,  ses->Suid,
44138c8a9a5SSteve French 								   tcon->tid, rc);
44238c8a9a5SSteve French 			else
44338c8a9a5SSteve French 				trace_smb3_query_info_compound_done(xid, ses->Suid,
44438c8a9a5SSteve French 								    tcon->tid);
44538c8a9a5SSteve French 			break;
44638c8a9a5SSteve French 		case SMB2_OP_POSIX_QUERY_INFO:
447886b7fb4SSteve French 			idata = in_iov[i].iov_base;
44838c8a9a5SSteve French 			if (rc == 0 && cfile && cfile->symlink_target) {
44938c8a9a5SSteve French 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
45038c8a9a5SSteve French 				if (!idata->symlink_target)
45138c8a9a5SSteve French 					rc = -ENOMEM;
45238c8a9a5SSteve French 			}
45338c8a9a5SSteve French 			if (rc == 0) {
45438c8a9a5SSteve French 				qi_rsp = (struct smb2_query_info_rsp *)
455886b7fb4SSteve French 					rsp_iov[i + 1].iov_base;
45638c8a9a5SSteve French 				rc = smb2_validate_and_copy_iov(
45738c8a9a5SSteve French 					le16_to_cpu(qi_rsp->OutputBufferOffset),
45838c8a9a5SSteve French 					le32_to_cpu(qi_rsp->OutputBufferLength),
459886b7fb4SSteve French 					&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
46038c8a9a5SSteve French 					(char *)&idata->posix_fi);
46138c8a9a5SSteve French 			}
46238c8a9a5SSteve French 			if (rc == 0) {
46338c8a9a5SSteve French 				unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength);
46438c8a9a5SSteve French 
46538c8a9a5SSteve French 				if (length > sizeof(idata->posix_fi)) {
466886b7fb4SSteve French 					char *base = (char *)rsp_iov[i + 1].iov_base +
46738c8a9a5SSteve French 						le16_to_cpu(qi_rsp->OutputBufferOffset) +
46838c8a9a5SSteve French 						sizeof(idata->posix_fi);
46938c8a9a5SSteve French 					*extbuflen = length - sizeof(idata->posix_fi);
47038c8a9a5SSteve French 					*extbuf = kmemdup(base, *extbuflen, GFP_KERNEL);
47138c8a9a5SSteve French 					if (!*extbuf)
47238c8a9a5SSteve French 						rc = -ENOMEM;
47338c8a9a5SSteve French 				} else {
47438c8a9a5SSteve French 					rc = -EINVAL;
47538c8a9a5SSteve French 				}
47638c8a9a5SSteve French 			}
477886b7fb4SSteve French 			SMB2_query_info_free(&rqst[num_rqst++]);
47838c8a9a5SSteve French 			if (rc)
479886b7fb4SSteve French 				trace_smb3_posix_query_info_compound_err(xid,  ses->Suid,
480886b7fb4SSteve French 									 tcon->tid, rc);
48138c8a9a5SSteve French 			else
482886b7fb4SSteve French 				trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
483886b7fb4SSteve French 									  tcon->tid);
48438c8a9a5SSteve French 			break;
48538c8a9a5SSteve French 		case SMB2_OP_DELETE:
48638c8a9a5SSteve French 			if (rc)
48738c8a9a5SSteve French 				trace_smb3_delete_err(xid,  ses->Suid, tcon->tid, rc);
48838c8a9a5SSteve French 			else
48938c8a9a5SSteve French 				trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
49038c8a9a5SSteve French 			break;
49138c8a9a5SSteve French 		case SMB2_OP_MKDIR:
49238c8a9a5SSteve French 			if (rc)
49338c8a9a5SSteve French 				trace_smb3_mkdir_err(xid,  ses->Suid, tcon->tid, rc);
49438c8a9a5SSteve French 			else
49538c8a9a5SSteve French 				trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
49638c8a9a5SSteve French 			break;
49738c8a9a5SSteve French 		case SMB2_OP_HARDLINK:
49838c8a9a5SSteve French 			if (rc)
49938c8a9a5SSteve French 				trace_smb3_hardlink_err(xid,  ses->Suid, tcon->tid, rc);
50038c8a9a5SSteve French 			else
50138c8a9a5SSteve French 				trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
502886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
50338c8a9a5SSteve French 			break;
50438c8a9a5SSteve French 		case SMB2_OP_RENAME:
50538c8a9a5SSteve French 			if (rc)
50638c8a9a5SSteve French 				trace_smb3_rename_err(xid,  ses->Suid, tcon->tid, rc);
50738c8a9a5SSteve French 			else
50838c8a9a5SSteve French 				trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
509886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
51038c8a9a5SSteve French 			break;
51138c8a9a5SSteve French 		case SMB2_OP_RMDIR:
51238c8a9a5SSteve French 			if (rc)
51338c8a9a5SSteve French 				trace_smb3_rmdir_err(xid,  ses->Suid, tcon->tid, rc);
51438c8a9a5SSteve French 			else
51538c8a9a5SSteve French 				trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
516886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
51738c8a9a5SSteve French 			break;
51838c8a9a5SSteve French 		case SMB2_OP_SET_EOF:
51938c8a9a5SSteve French 			if (rc)
52038c8a9a5SSteve French 				trace_smb3_set_eof_err(xid,  ses->Suid, tcon->tid, rc);
52138c8a9a5SSteve French 			else
52238c8a9a5SSteve French 				trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
523886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
52438c8a9a5SSteve French 			break;
52538c8a9a5SSteve French 		case SMB2_OP_SET_INFO:
52638c8a9a5SSteve French 			if (rc)
52738c8a9a5SSteve French 				trace_smb3_set_info_compound_err(xid,  ses->Suid,
52838c8a9a5SSteve French 								 tcon->tid, rc);
52938c8a9a5SSteve French 			else
53038c8a9a5SSteve French 				trace_smb3_set_info_compound_done(xid, ses->Suid,
53138c8a9a5SSteve French 								  tcon->tid);
532886b7fb4SSteve French 			SMB2_set_info_free(&rqst[num_rqst++]);
53338c8a9a5SSteve French 			break;
53402bcf865SSteve French 		case SMB2_OP_SET_REPARSE:
53502bcf865SSteve French 			if (rc) {
53602bcf865SSteve French 				trace_smb3_set_reparse_compound_err(xid,  ses->Suid,
53702bcf865SSteve French 								    tcon->tid, rc);
53802bcf865SSteve French 			} else {
53902bcf865SSteve French 				trace_smb3_set_reparse_compound_done(xid, ses->Suid,
54002bcf865SSteve French 								     tcon->tid);
54102bcf865SSteve French 			}
54202bcf865SSteve French 			SMB2_ioctl_free(&rqst[num_rqst++]);
54302bcf865SSteve French 			break;
54438c8a9a5SSteve French 		}
545886b7fb4SSteve French 	}
546886b7fb4SSteve French 	SMB2_close_free(&rqst[num_rqst]);
54738c8a9a5SSteve French 
54833f73618SShyam Prasad N 	if (cfile)
54933f73618SShyam Prasad N 		cifsFileInfo_put(cfile);
55033f73618SShyam Prasad N 
551886b7fb4SSteve French 	num_cmds += 2;
552c5f44a3dSPaulo Alcantara 	if (out_iov && out_buftype) {
553886b7fb4SSteve French 		memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
554886b7fb4SSteve French 		memcpy(out_buftype, resp_buftype,
555886b7fb4SSteve French 		       num_cmds * sizeof(*out_buftype));
55638c8a9a5SSteve French 	} else {
557886b7fb4SSteve French 		for (i = 0; i < num_cmds; i++)
558886b7fb4SSteve French 			free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
55938c8a9a5SSteve French 	}
56038c8a9a5SSteve French 	kfree(vars);
56138c8a9a5SSteve French 	return rc;
56238c8a9a5SSteve French }
56338c8a9a5SSteve French 
5645f71ebc4SPaulo Alcantara static int parse_create_response(struct cifs_open_info_data *data,
5655f71ebc4SPaulo Alcantara 				 struct cifs_sb_info *cifs_sb,
5665f71ebc4SPaulo Alcantara 				 const struct kvec *iov)
5675f71ebc4SPaulo Alcantara {
5685f71ebc4SPaulo Alcantara 	struct smb2_create_rsp *rsp = iov->iov_base;
5695f71ebc4SPaulo Alcantara 	bool reparse_point = false;
5705f71ebc4SPaulo Alcantara 	u32 tag = 0;
5715f71ebc4SPaulo Alcantara 	int rc = 0;
5725f71ebc4SPaulo Alcantara 
5735f71ebc4SPaulo Alcantara 	switch (rsp->hdr.Status) {
5747fb77d9cSPaulo Alcantara 	case STATUS_IO_REPARSE_TAG_NOT_HANDLED:
5757fb77d9cSPaulo Alcantara 		reparse_point = true;
5767fb77d9cSPaulo Alcantara 		break;
5775f71ebc4SPaulo Alcantara 	case STATUS_STOPPED_ON_SYMLINK:
5785f71ebc4SPaulo Alcantara 		rc = smb2_parse_symlink_response(cifs_sb, iov,
5795f71ebc4SPaulo Alcantara 						 &data->symlink_target);
5805f71ebc4SPaulo Alcantara 		if (rc)
5815f71ebc4SPaulo Alcantara 			return rc;
5825f71ebc4SPaulo Alcantara 		tag = IO_REPARSE_TAG_SYMLINK;
5835f71ebc4SPaulo Alcantara 		reparse_point = true;
5845f71ebc4SPaulo Alcantara 		break;
5855f71ebc4SPaulo Alcantara 	case STATUS_SUCCESS:
5865f71ebc4SPaulo Alcantara 		reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
5875f71ebc4SPaulo Alcantara 		break;
5885f71ebc4SPaulo Alcantara 	}
5895f71ebc4SPaulo Alcantara 	data->reparse_point = reparse_point;
590df32e887SPaulo Alcantara 	data->reparse.tag = tag;
5915f71ebc4SPaulo Alcantara 	return rc;
5925f71ebc4SPaulo Alcantara }
5935f71ebc4SPaulo Alcantara 
5948b4e285dSPaulo Alcantara int smb2_query_path_info(const unsigned int xid,
5958b4e285dSPaulo Alcantara 			 struct cifs_tcon *tcon,
5968b4e285dSPaulo Alcantara 			 struct cifs_sb_info *cifs_sb,
5978b4e285dSPaulo Alcantara 			 const char *full_path,
5988b4e285dSPaulo Alcantara 			 struct cifs_open_info_data *data)
59938c8a9a5SSteve French {
60038c8a9a5SSteve French 	__u32 create_options = 0;
60138c8a9a5SSteve French 	struct cifsFileInfo *cfile;
60238c8a9a5SSteve French 	struct cached_fid *cfid = NULL;
6035f71ebc4SPaulo Alcantara 	struct smb2_hdr *hdr;
604886b7fb4SSteve French 	struct kvec in_iov, out_iov[3] = {};
605c5f44a3dSPaulo Alcantara 	int out_buftype[3] = {};
60638c8a9a5SSteve French 	bool islink;
607886b7fb4SSteve French 	int cmd = SMB2_OP_QUERY_INFO;
60838c8a9a5SSteve French 	int rc, rc2;
60938c8a9a5SSteve French 
6108b4e285dSPaulo Alcantara 	data->adjust_tz = false;
6118b4e285dSPaulo Alcantara 	data->reparse_point = false;
61238c8a9a5SSteve French 
61338c8a9a5SSteve French 	if (strcmp(full_path, ""))
61438c8a9a5SSteve French 		rc = -ENOENT;
61538c8a9a5SSteve French 	else
61638c8a9a5SSteve French 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
61738c8a9a5SSteve French 	/* If it is a root and its handle is cached then use it */
61838c8a9a5SSteve French 	if (!rc) {
61938c8a9a5SSteve French 		if (cfid->file_all_info_is_valid) {
62038c8a9a5SSteve French 			memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi));
62138c8a9a5SSteve French 		} else {
62238c8a9a5SSteve French 			rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid,
62338c8a9a5SSteve French 					     cfid->fid.volatile_fid, &data->fi);
62438c8a9a5SSteve French 		}
62538c8a9a5SSteve French 		close_cached_dir(cfid);
62638c8a9a5SSteve French 		return rc;
62738c8a9a5SSteve French 	}
62838c8a9a5SSteve French 
629886b7fb4SSteve French 	in_iov.iov_base = data;
630886b7fb4SSteve French 	in_iov.iov_len = sizeof(*data);
631886b7fb4SSteve French 
63238c8a9a5SSteve French 	cifs_get_readable_path(tcon, full_path, &cfile);
633886b7fb4SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
634886b7fb4SSteve French 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
635886b7fb4SSteve French 			      create_options, ACL_NO_MODE, &in_iov,
636886b7fb4SSteve French 			      &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype);
6375f71ebc4SPaulo Alcantara 	hdr = out_iov[0].iov_base;
6385f71ebc4SPaulo Alcantara 	/*
6395f71ebc4SPaulo Alcantara 	 * If first iov is unset, then SMB session was dropped or we've got a
6405f71ebc4SPaulo Alcantara 	 * cached open file (@cfile).
6415f71ebc4SPaulo Alcantara 	 */
6425f71ebc4SPaulo Alcantara 	if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
64338c8a9a5SSteve French 		goto out;
64438c8a9a5SSteve French 
6455f71ebc4SPaulo Alcantara 	switch (rc) {
6465f71ebc4SPaulo Alcantara 	case 0:
6475f71ebc4SPaulo Alcantara 	case -EOPNOTSUPP:
6485f71ebc4SPaulo Alcantara 		rc = parse_create_response(data, cifs_sb, &out_iov[0]);
6495f71ebc4SPaulo Alcantara 		if (rc || !data->reparse_point)
6505f71ebc4SPaulo Alcantara 			goto out;
6515f71ebc4SPaulo Alcantara 
65238c8a9a5SSteve French 		create_options |= OPEN_REPARSE_POINT;
65338c8a9a5SSteve French 		/* Failed on a symbolic link - query a reparse point info */
65438c8a9a5SSteve French 		cifs_get_readable_path(tcon, full_path, &cfile);
65538c8a9a5SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
65638c8a9a5SSteve French 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
657886b7fb4SSteve French 				      create_options, ACL_NO_MODE, &in_iov,
658886b7fb4SSteve French 				      &cmd, 1, cfile, NULL, NULL, NULL, NULL);
6595f71ebc4SPaulo Alcantara 		break;
6605f71ebc4SPaulo Alcantara 	case -EREMOTE:
6615f71ebc4SPaulo Alcantara 		break;
6625f71ebc4SPaulo Alcantara 	default:
6635f71ebc4SPaulo Alcantara 		if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
6645f71ebc4SPaulo Alcantara 			break;
66538c8a9a5SSteve French 		rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
66638c8a9a5SSteve French 						     full_path, &islink);
66738c8a9a5SSteve French 		if (rc2) {
66838c8a9a5SSteve French 			rc = rc2;
66938c8a9a5SSteve French 			goto out;
67038c8a9a5SSteve French 		}
67138c8a9a5SSteve French 		if (islink)
67238c8a9a5SSteve French 			rc = -EREMOTE;
67338c8a9a5SSteve French 	}
67438c8a9a5SSteve French 
67538c8a9a5SSteve French out:
676c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
677c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
678c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
67938c8a9a5SSteve French 	return rc;
68038c8a9a5SSteve French }
68138c8a9a5SSteve French 
6828b4e285dSPaulo Alcantara int smb311_posix_query_path_info(const unsigned int xid,
6838b4e285dSPaulo Alcantara 				 struct cifs_tcon *tcon,
6848b4e285dSPaulo Alcantara 				 struct cifs_sb_info *cifs_sb,
6858b4e285dSPaulo Alcantara 				 const char *full_path,
68638c8a9a5SSteve French 				 struct cifs_open_info_data *data,
68738c8a9a5SSteve French 				 struct cifs_sid *owner,
6888b4e285dSPaulo Alcantara 				 struct cifs_sid *group)
68938c8a9a5SSteve French {
69038c8a9a5SSteve French 	int rc;
69138c8a9a5SSteve French 	__u32 create_options = 0;
69238c8a9a5SSteve French 	struct cifsFileInfo *cfile;
693886b7fb4SSteve French 	struct kvec in_iov, out_iov[3] = {};
694c5f44a3dSPaulo Alcantara 	int out_buftype[3] = {};
69538c8a9a5SSteve French 	__u8 *sidsbuf = NULL;
69638c8a9a5SSteve French 	__u8 *sidsbuf_end = NULL;
69738c8a9a5SSteve French 	size_t sidsbuflen = 0;
69838c8a9a5SSteve French 	size_t owner_len, group_len;
699886b7fb4SSteve French 	int cmd = SMB2_OP_POSIX_QUERY_INFO;
70038c8a9a5SSteve French 
7018b4e285dSPaulo Alcantara 	data->adjust_tz = false;
7028b4e285dSPaulo Alcantara 	data->reparse_point = false;
70338c8a9a5SSteve French 
70438c8a9a5SSteve French 	/*
70538c8a9a5SSteve French 	 * BB TODO: Add support for using the cached root handle.
70638c8a9a5SSteve French 	 * Create SMB2_query_posix_info worker function to do non-compounded query
70738c8a9a5SSteve French 	 * when we already have an open file handle for this. For now this is fast enough
70838c8a9a5SSteve French 	 * (always using the compounded version).
70938c8a9a5SSteve French 	 */
710886b7fb4SSteve French 	in_iov.iov_base = data;
711886b7fb4SSteve French 	in_iov.iov_len = sizeof(*data);
71238c8a9a5SSteve French 
71338c8a9a5SSteve French 	cifs_get_readable_path(tcon, full_path, &cfile);
714886b7fb4SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
715886b7fb4SSteve French 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
716886b7fb4SSteve French 			      create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
717886b7fb4SSteve French 			      cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
7185f71ebc4SPaulo Alcantara 	/*
7195f71ebc4SPaulo Alcantara 	 * If first iov is unset, then SMB session was dropped or we've got a
7205f71ebc4SPaulo Alcantara 	 * cached open file (@cfile).
7215f71ebc4SPaulo Alcantara 	 */
7225f71ebc4SPaulo Alcantara 	if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER)
72338c8a9a5SSteve French 		goto out;
72438c8a9a5SSteve French 
7255f71ebc4SPaulo Alcantara 	switch (rc) {
7265f71ebc4SPaulo Alcantara 	case 0:
7275f71ebc4SPaulo Alcantara 	case -EOPNOTSUPP:
7285f71ebc4SPaulo Alcantara 		/* BB TODO: When support for special files added to Samba re-verify this path */
7295f71ebc4SPaulo Alcantara 		rc = parse_create_response(data, cifs_sb, &out_iov[0]);
7305f71ebc4SPaulo Alcantara 		if (rc || !data->reparse_point)
7315f71ebc4SPaulo Alcantara 			goto out;
7325f71ebc4SPaulo Alcantara 
7335f71ebc4SPaulo Alcantara 		create_options |= OPEN_REPARSE_POINT;
73438c8a9a5SSteve French 		/* Failed on a symbolic link - query a reparse point info */
73538c8a9a5SSteve French 		cifs_get_readable_path(tcon, full_path, &cfile);
736886b7fb4SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
737886b7fb4SSteve French 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
738886b7fb4SSteve French 				      create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
739886b7fb4SSteve French 				      cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
7405f71ebc4SPaulo Alcantara 		break;
74138c8a9a5SSteve French 	}
74238c8a9a5SSteve French 
7435f71ebc4SPaulo Alcantara out:
74438c8a9a5SSteve French 	if (rc == 0) {
74538c8a9a5SSteve French 		sidsbuf_end = sidsbuf + sidsbuflen;
74638c8a9a5SSteve French 
74738c8a9a5SSteve French 		owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
74838c8a9a5SSteve French 		if (owner_len == -1) {
74938c8a9a5SSteve French 			rc = -EINVAL;
75038c8a9a5SSteve French 			goto out;
75138c8a9a5SSteve French 		}
75238c8a9a5SSteve French 		memcpy(owner, sidsbuf, owner_len);
75338c8a9a5SSteve French 
75438c8a9a5SSteve French 		group_len = posix_info_sid_size(
75538c8a9a5SSteve French 			sidsbuf + owner_len, sidsbuf_end);
75638c8a9a5SSteve French 		if (group_len == -1) {
75738c8a9a5SSteve French 			rc = -EINVAL;
75838c8a9a5SSteve French 			goto out;
75938c8a9a5SSteve French 		}
76038c8a9a5SSteve French 		memcpy(group, sidsbuf + owner_len, group_len);
76138c8a9a5SSteve French 	}
76238c8a9a5SSteve French 
76338c8a9a5SSteve French 	kfree(sidsbuf);
764c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
765c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
766c5f44a3dSPaulo Alcantara 	free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
76738c8a9a5SSteve French 	return rc;
76838c8a9a5SSteve French }
76938c8a9a5SSteve French 
77038c8a9a5SSteve French int
77138c8a9a5SSteve French smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
77238c8a9a5SSteve French 	   struct cifs_tcon *tcon, const char *name,
77338c8a9a5SSteve French 	   struct cifs_sb_info *cifs_sb)
77438c8a9a5SSteve French {
77538c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name,
77638c8a9a5SSteve French 				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
777886b7fb4SSteve French 				CREATE_NOT_FILE, mode, NULL,
778886b7fb4SSteve French 				&(int){SMB2_OP_MKDIR}, 1,
77938c8a9a5SSteve French 				NULL, NULL, NULL, NULL, NULL);
78038c8a9a5SSteve French }
78138c8a9a5SSteve French 
78238c8a9a5SSteve French void
78338c8a9a5SSteve French smb2_mkdir_setinfo(struct inode *inode, const char *name,
78438c8a9a5SSteve French 		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
78538c8a9a5SSteve French 		   const unsigned int xid)
78638c8a9a5SSteve French {
787886b7fb4SSteve French 	FILE_BASIC_INFO data = {};
78838c8a9a5SSteve French 	struct cifsInodeInfo *cifs_i;
78938c8a9a5SSteve French 	struct cifsFileInfo *cfile;
790886b7fb4SSteve French 	struct kvec in_iov;
79138c8a9a5SSteve French 	u32 dosattrs;
79238c8a9a5SSteve French 	int tmprc;
79338c8a9a5SSteve French 
794886b7fb4SSteve French 	in_iov.iov_base = &data;
795886b7fb4SSteve French 	in_iov.iov_len = sizeof(data);
79638c8a9a5SSteve French 	cifs_i = CIFS_I(inode);
79738c8a9a5SSteve French 	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
79838c8a9a5SSteve French 	data.Attributes = cpu_to_le32(dosattrs);
79938c8a9a5SSteve French 	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
80038c8a9a5SSteve French 	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
80138c8a9a5SSteve French 				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
802886b7fb4SSteve French 				 CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
803886b7fb4SSteve French 				 &(int){SMB2_OP_SET_INFO}, 1,
804886b7fb4SSteve French 				 cfile, NULL, NULL, NULL, NULL);
80538c8a9a5SSteve French 	if (tmprc == 0)
80638c8a9a5SSteve French 		cifs_i->cifsAttrs = dosattrs;
80738c8a9a5SSteve French }
80838c8a9a5SSteve French 
80938c8a9a5SSteve French int
81038c8a9a5SSteve French smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
81138c8a9a5SSteve French 	   struct cifs_sb_info *cifs_sb)
81238c8a9a5SSteve French {
81338c8a9a5SSteve French 	drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
814886b7fb4SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name,
815886b7fb4SSteve French 				DELETE, FILE_OPEN, CREATE_NOT_FILE,
816886b7fb4SSteve French 				ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1,
817886b7fb4SSteve French 				NULL, NULL, NULL, NULL, NULL);
81838c8a9a5SSteve French }
81938c8a9a5SSteve French 
82038c8a9a5SSteve French int
82138c8a9a5SSteve French smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
82238c8a9a5SSteve French 	    struct cifs_sb_info *cifs_sb)
82338c8a9a5SSteve French {
82438c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
82538c8a9a5SSteve French 				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
826886b7fb4SSteve French 				ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1,
827886b7fb4SSteve French 				NULL, NULL, NULL, NULL, NULL);
82838c8a9a5SSteve French }
82938c8a9a5SSteve French 
830c586b0c7SPaulo Alcantara static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
83138c8a9a5SSteve French 			      const char *from_name, const char *to_name,
832c586b0c7SPaulo Alcantara 			      struct cifs_sb_info *cifs_sb,
833c586b0c7SPaulo Alcantara 			      __u32 create_options, __u32 access,
834c586b0c7SPaulo Alcantara 			      int command, struct cifsFileInfo *cfile)
83538c8a9a5SSteve French {
836886b7fb4SSteve French 	struct kvec in_iov;
83738c8a9a5SSteve French 	__le16 *smb2_to_name = NULL;
83838c8a9a5SSteve French 	int rc;
83938c8a9a5SSteve French 
84038c8a9a5SSteve French 	smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
84138c8a9a5SSteve French 	if (smb2_to_name == NULL) {
84238c8a9a5SSteve French 		rc = -ENOMEM;
84338c8a9a5SSteve French 		goto smb2_rename_path;
84438c8a9a5SSteve French 	}
845886b7fb4SSteve French 	in_iov.iov_base = smb2_to_name;
846886b7fb4SSteve French 	in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
847886b7fb4SSteve French 
84838c8a9a5SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
849886b7fb4SSteve French 			      FILE_OPEN, 0, ACL_NO_MODE, &in_iov,
850886b7fb4SSteve French 			      &command, 1, cfile, NULL, NULL, NULL, NULL);
85138c8a9a5SSteve French smb2_rename_path:
85238c8a9a5SSteve French 	kfree(smb2_to_name);
85338c8a9a5SSteve French 	return rc;
85438c8a9a5SSteve French }
85538c8a9a5SSteve French 
856c586b0c7SPaulo Alcantara int smb2_rename_path(const unsigned int xid,
857c586b0c7SPaulo Alcantara 		     struct cifs_tcon *tcon,
858c586b0c7SPaulo Alcantara 		     struct dentry *source_dentry,
85938c8a9a5SSteve French 		     const char *from_name, const char *to_name,
86038c8a9a5SSteve French 		     struct cifs_sb_info *cifs_sb)
86138c8a9a5SSteve French {
86238c8a9a5SSteve French 	struct cifsFileInfo *cfile;
8638c944f8aSPaulo Alcantara 	__u32 co = file_create_options(source_dentry);
86438c8a9a5SSteve French 
86538c8a9a5SSteve French 	drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
86638c8a9a5SSteve French 	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
86738c8a9a5SSteve French 
868c586b0c7SPaulo Alcantara 	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
869c586b0c7SPaulo Alcantara 				  co, DELETE, SMB2_OP_RENAME, cfile);
87038c8a9a5SSteve French }
87138c8a9a5SSteve French 
8728c944f8aSPaulo Alcantara int smb2_create_hardlink(const unsigned int xid,
8738c944f8aSPaulo Alcantara 			 struct cifs_tcon *tcon,
8748c944f8aSPaulo Alcantara 			 struct dentry *source_dentry,
87538c8a9a5SSteve French 			 const char *from_name, const char *to_name,
87638c8a9a5SSteve French 			 struct cifs_sb_info *cifs_sb)
87738c8a9a5SSteve French {
8788c944f8aSPaulo Alcantara 	__u32 co = file_create_options(source_dentry);
8798c944f8aSPaulo Alcantara 
880c586b0c7SPaulo Alcantara 	return smb2_set_path_attr(xid, tcon, from_name, to_name,
8818c944f8aSPaulo Alcantara 				  cifs_sb, co, FILE_READ_ATTRIBUTES,
882c586b0c7SPaulo Alcantara 				  SMB2_OP_HARDLINK, NULL);
88338c8a9a5SSteve French }
88438c8a9a5SSteve French 
88538c8a9a5SSteve French int
88638c8a9a5SSteve French smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
88738c8a9a5SSteve French 		   const char *full_path, __u64 size,
88838c8a9a5SSteve French 		   struct cifs_sb_info *cifs_sb, bool set_alloc)
88938c8a9a5SSteve French {
89038c8a9a5SSteve French 	struct cifsFileInfo *cfile;
891886b7fb4SSteve French 	struct kvec in_iov;
892886b7fb4SSteve French 	__le64 eof = cpu_to_le64(size);
89338c8a9a5SSteve French 
894886b7fb4SSteve French 	in_iov.iov_base = &eof;
895886b7fb4SSteve French 	in_iov.iov_len = sizeof(eof);
89638c8a9a5SSteve French 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
89738c8a9a5SSteve French 	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
898886b7fb4SSteve French 				FILE_WRITE_DATA, FILE_OPEN,
899886b7fb4SSteve French 				0, ACL_NO_MODE, &in_iov,
900886b7fb4SSteve French 				&(int){SMB2_OP_SET_EOF}, 1,
901886b7fb4SSteve French 				cfile, NULL, NULL, NULL, NULL);
90238c8a9a5SSteve French }
90338c8a9a5SSteve French 
90438c8a9a5SSteve French int
90538c8a9a5SSteve French smb2_set_file_info(struct inode *inode, const char *full_path,
90638c8a9a5SSteve French 		   FILE_BASIC_INFO *buf, const unsigned int xid)
90738c8a9a5SSteve French {
90838c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
90938c8a9a5SSteve French 	struct tcon_link *tlink;
91038c8a9a5SSteve French 	struct cifs_tcon *tcon;
91138c8a9a5SSteve French 	struct cifsFileInfo *cfile;
912886b7fb4SSteve French 	struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
91338c8a9a5SSteve French 	int rc;
91438c8a9a5SSteve French 
91538c8a9a5SSteve French 	if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
91638c8a9a5SSteve French 	    (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
91738c8a9a5SSteve French 	    (buf->Attributes == 0))
91838c8a9a5SSteve French 		return 0; /* would be a no op, no sense sending this */
91938c8a9a5SSteve French 
92038c8a9a5SSteve French 	tlink = cifs_sb_tlink(cifs_sb);
92138c8a9a5SSteve French 	if (IS_ERR(tlink))
92238c8a9a5SSteve French 		return PTR_ERR(tlink);
92338c8a9a5SSteve French 	tcon = tlink_tcon(tlink);
92438c8a9a5SSteve French 
92538c8a9a5SSteve French 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
92638c8a9a5SSteve French 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
92738c8a9a5SSteve French 			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
928886b7fb4SSteve French 			      0, ACL_NO_MODE, &in_iov,
929886b7fb4SSteve French 			      &(int){SMB2_OP_SET_INFO}, 1, cfile,
93038c8a9a5SSteve French 			      NULL, NULL, NULL, NULL);
93138c8a9a5SSteve French 	cifs_put_tlink(tlink);
93238c8a9a5SSteve French 	return rc;
93338c8a9a5SSteve French }
93402bcf865SSteve French 
93502bcf865SSteve French struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
93602bcf865SSteve French 				     struct super_block *sb,
93702bcf865SSteve French 				     const unsigned int xid,
93802bcf865SSteve French 				     struct cifs_tcon *tcon,
93902bcf865SSteve French 				     const char *full_path,
94002bcf865SSteve French 				     struct kvec *iov)
94102bcf865SSteve French {
94202bcf865SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
94302bcf865SSteve French 	struct cifsFileInfo *cfile;
94402bcf865SSteve French 	struct inode *new = NULL;
94502bcf865SSteve French 	struct kvec in_iov[2];
94602bcf865SSteve French 	int cmds[2];
94702bcf865SSteve French 	int da, co, cd;
94802bcf865SSteve French 	int rc;
94902bcf865SSteve French 
95002bcf865SSteve French 	da = SYNCHRONIZE | DELETE |
95102bcf865SSteve French 		FILE_READ_ATTRIBUTES |
95202bcf865SSteve French 		FILE_WRITE_ATTRIBUTES;
95302bcf865SSteve French 	co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
95402bcf865SSteve French 	cd = FILE_CREATE;
95502bcf865SSteve French 	cmds[0] = SMB2_OP_SET_REPARSE;
95602bcf865SSteve French 	in_iov[0] = *iov;
95702bcf865SSteve French 	in_iov[1].iov_base = data;
95802bcf865SSteve French 	in_iov[1].iov_len = sizeof(*data);
95902bcf865SSteve French 
96002bcf865SSteve French 	if (tcon->posix_extensions) {
96102bcf865SSteve French 		cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
96202bcf865SSteve French 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
96302bcf865SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
96402bcf865SSteve French 				      da, cd, co, ACL_NO_MODE, in_iov,
96502bcf865SSteve French 				      cmds, 2, cfile, NULL, NULL, NULL, NULL);
96602bcf865SSteve French 		if (!rc) {
96702bcf865SSteve French 			rc = smb311_posix_get_inode_info(&new, full_path,
96802bcf865SSteve French 							 data, sb, xid);
96902bcf865SSteve French 		}
97002bcf865SSteve French 	} else {
97102bcf865SSteve French 		cmds[1] = SMB2_OP_QUERY_INFO;
97202bcf865SSteve French 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
97302bcf865SSteve French 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
97402bcf865SSteve French 				      da, cd, co, ACL_NO_MODE, in_iov,
97502bcf865SSteve French 				      cmds, 2, cfile, NULL, NULL, NULL, NULL);
97602bcf865SSteve French 		if (!rc) {
97702bcf865SSteve French 			rc = cifs_get_inode_info(&new, full_path,
97802bcf865SSteve French 						 data, sb, xid, NULL);
97902bcf865SSteve French 		}
98002bcf865SSteve French 	}
98102bcf865SSteve French 	return rc ? ERR_PTR(rc) : new;
98202bcf865SSteve French }
983