xref: /openbmc/linux/fs/smb/client/smb2inode.c (revision 886b7fb4)
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  *
4  *   Copyright (C) International Business Machines  Corp., 2002, 2011
5  *                 Etersoft, 2012
6  *   Author(s): Pavel Shilovsky (pshilovsky@samba.org),
7  *              Steve French (sfrench@us.ibm.com)
8  *
9  */
10 #include <linux/fs.h>
11 #include <linux/stat.h>
12 #include <linux/slab.h>
13 #include <linux/pagemap.h>
14 #include <asm/div64.h>
15 #include "cifsfs.h"
16 #include "cifspdu.h"
17 #include "cifsglob.h"
18 #include "cifsproto.h"
19 #include "cifs_debug.h"
20 #include "cifs_fs_sb.h"
21 #include "cifs_unicode.h"
22 #include "fscache.h"
23 #include "smb2glob.h"
24 #include "smb2pdu.h"
25 #include "smb2proto.h"
26 #include "cached_dir.h"
27 #include "smb2status.h"
28 
29 static inline __u32 file_create_options(struct dentry *dentry)
30 {
31 	struct cifsInodeInfo *ci;
32 
33 	if (dentry) {
34 		ci = CIFS_I(d_inode(dentry));
35 		if (ci->cifsAttrs & ATTR_REPARSE)
36 			return OPEN_REPARSE_POINT;
37 	}
38 	return 0;
39 }
40 
41 /*
42  * note: If cfile is passed, the reference to it is dropped here.
43  * So make sure that you do not reuse cfile after return from this func.
44  *
45  * If passing @out_iov and @out_buftype, ensure to make them both large enough
46  * (>= 3) to hold all compounded responses.  Caller is also responsible for
47  * freeing them up with free_rsp_buf().
48  */
49 static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
50 			    struct cifs_sb_info *cifs_sb, const char *full_path,
51 			    __u32 desired_access, __u32 create_disposition,
52 			    __u32 create_options, umode_t mode, struct kvec *in_iov,
53 			    int *cmds, int num_cmds, struct cifsFileInfo *cfile,
54 			    __u8 **extbuf, size_t *extbuflen,
55 			    struct kvec *out_iov, int *out_buftype)
56 {
57 	struct smb2_compound_vars *vars = NULL;
58 	struct kvec *rsp_iov;
59 	struct smb_rqst *rqst;
60 	int rc;
61 	__le16 *utf16_path = NULL;
62 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
63 	struct cifs_fid fid;
64 	struct cifs_ses *ses = tcon->ses;
65 	struct TCP_Server_Info *server;
66 	int num_rqst = 0, i;
67 	int resp_buftype[MAX_COMPOUND];
68 	struct smb2_query_info_rsp *qi_rsp = NULL;
69 	struct cifs_open_info_data *idata;
70 	int flags = 0;
71 	__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
72 	unsigned int size[2];
73 	void *data[2];
74 	int len;
75 
76 	vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
77 	if (vars == NULL)
78 		return -ENOMEM;
79 	rqst = &vars->rqst[0];
80 	rsp_iov = &vars->rsp_iov[0];
81 
82 	server = cifs_pick_channel(ses);
83 
84 	if (smb3_encryption_required(tcon))
85 		flags |= CIFS_TRANSFORM_REQ;
86 
87 	for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
88 		resp_buftype[i] = CIFS_NO_BUFFER;
89 
90 	/* We already have a handle so we can skip the open */
91 	if (cfile)
92 		goto after_open;
93 
94 	/* Open */
95 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
96 	if (!utf16_path) {
97 		rc = -ENOMEM;
98 		goto finished;
99 	}
100 
101 	vars->oparms = (struct cifs_open_parms) {
102 		.tcon = tcon,
103 		.path = full_path,
104 		.desired_access = desired_access,
105 		.disposition = create_disposition,
106 		.create_options = cifs_create_options(cifs_sb, create_options),
107 		.fid = &fid,
108 		.mode = mode,
109 		.cifs_sb = cifs_sb,
110 	};
111 
112 	rqst[num_rqst].rq_iov = &vars->open_iov[0];
113 	rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
114 	rc = SMB2_open_init(tcon, server,
115 			    &rqst[num_rqst], &oplock, &vars->oparms,
116 			    utf16_path);
117 	kfree(utf16_path);
118 	if (rc)
119 		goto finished;
120 
121 	smb2_set_next_command(tcon, &rqst[num_rqst]);
122  after_open:
123 	num_rqst++;
124 	rc = 0;
125 
126 	for (i = 0; i < num_cmds; i++) {
127 		/* Operation */
128 		switch (cmds[i]) {
129 		case SMB2_OP_QUERY_INFO:
130 			rqst[num_rqst].rq_iov = &vars->qi_iov;
131 			rqst[num_rqst].rq_nvec = 1;
132 
133 			if (cfile) {
134 				rc = SMB2_query_info_init(tcon, server,
135 							  &rqst[num_rqst],
136 							  cfile->fid.persistent_fid,
137 							  cfile->fid.volatile_fid,
138 							  FILE_ALL_INFORMATION,
139 							  SMB2_O_INFO_FILE, 0,
140 							  sizeof(struct smb2_file_all_info) +
141 							  PATH_MAX * 2, 0, NULL);
142 			} else {
143 				rc = SMB2_query_info_init(tcon, server,
144 							  &rqst[num_rqst],
145 							  COMPOUND_FID,
146 							  COMPOUND_FID,
147 							  FILE_ALL_INFORMATION,
148 							  SMB2_O_INFO_FILE, 0,
149 							  sizeof(struct smb2_file_all_info) +
150 							  PATH_MAX * 2, 0, NULL);
151 				if (!rc) {
152 					smb2_set_next_command(tcon, &rqst[num_rqst]);
153 					smb2_set_related(&rqst[num_rqst]);
154 				}
155 			}
156 
157 			if (rc)
158 				goto finished;
159 			num_rqst++;
160 			trace_smb3_query_info_compound_enter(xid, ses->Suid,
161 							     tcon->tid, full_path);
162 			break;
163 		case SMB2_OP_POSIX_QUERY_INFO:
164 			rqst[num_rqst].rq_iov = &vars->qi_iov;
165 			rqst[num_rqst].rq_nvec = 1;
166 
167 			if (cfile) {
168 				/* TBD: fix following to allow for longer SIDs */
169 				rc = SMB2_query_info_init(tcon, server,
170 							  &rqst[num_rqst],
171 							  cfile->fid.persistent_fid,
172 							  cfile->fid.volatile_fid,
173 							  SMB_FIND_FILE_POSIX_INFO,
174 							  SMB2_O_INFO_FILE, 0,
175 							  sizeof(struct smb311_posix_qinfo *) +
176 							  (PATH_MAX * 2) +
177 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
178 			} else {
179 				rc = SMB2_query_info_init(tcon, server,
180 							  &rqst[num_rqst],
181 							  COMPOUND_FID,
182 							  COMPOUND_FID,
183 							  SMB_FIND_FILE_POSIX_INFO,
184 							  SMB2_O_INFO_FILE, 0,
185 							  sizeof(struct smb311_posix_qinfo *) +
186 							  (PATH_MAX * 2) +
187 							  (sizeof(struct cifs_sid) * 2), 0, NULL);
188 				if (!rc) {
189 					smb2_set_next_command(tcon, &rqst[num_rqst]);
190 					smb2_set_related(&rqst[num_rqst]);
191 				}
192 			}
193 
194 			if (rc)
195 				goto finished;
196 			num_rqst++;
197 			trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
198 								   tcon->tid, full_path);
199 			break;
200 		case SMB2_OP_DELETE:
201 			trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
202 			break;
203 		case SMB2_OP_MKDIR:
204 			/*
205 			 * Directories are created through parameters in the
206 			 * SMB2_open() call.
207 			 */
208 			trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
209 			break;
210 		case SMB2_OP_RMDIR:
211 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
212 			rqst[num_rqst].rq_nvec = 1;
213 
214 			size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
215 			data[0] = &delete_pending[0];
216 
217 			rc = SMB2_set_info_init(tcon, server,
218 						&rqst[num_rqst], COMPOUND_FID,
219 						COMPOUND_FID, current->tgid,
220 						FILE_DISPOSITION_INFORMATION,
221 						SMB2_O_INFO_FILE, 0, data, size);
222 			if (rc)
223 				goto finished;
224 			smb2_set_next_command(tcon, &rqst[num_rqst]);
225 			smb2_set_related(&rqst[num_rqst++]);
226 			trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
227 			break;
228 		case SMB2_OP_SET_EOF:
229 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
230 			rqst[num_rqst].rq_nvec = 1;
231 
232 			size[0] = in_iov[i].iov_len;
233 			data[0] = in_iov[i].iov_base;
234 
235 			if (cfile) {
236 				rc = SMB2_set_info_init(tcon, server,
237 							&rqst[num_rqst],
238 							cfile->fid.persistent_fid,
239 							cfile->fid.volatile_fid,
240 							current->tgid,
241 							FILE_END_OF_FILE_INFORMATION,
242 							SMB2_O_INFO_FILE, 0,
243 							data, size);
244 			} else {
245 				rc = SMB2_set_info_init(tcon, server,
246 							&rqst[num_rqst],
247 							COMPOUND_FID,
248 							COMPOUND_FID,
249 							current->tgid,
250 							FILE_END_OF_FILE_INFORMATION,
251 							SMB2_O_INFO_FILE, 0,
252 							data, size);
253 				if (!rc) {
254 					smb2_set_next_command(tcon, &rqst[num_rqst]);
255 					smb2_set_related(&rqst[num_rqst]);
256 				}
257 			}
258 			if (rc)
259 				goto finished;
260 			num_rqst++;
261 			trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
262 			break;
263 		case SMB2_OP_SET_INFO:
264 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
265 			rqst[num_rqst].rq_nvec = 1;
266 
267 			size[0] = in_iov[i].iov_len;
268 			data[0] = in_iov[i].iov_base;
269 
270 			if (cfile) {
271 				rc = SMB2_set_info_init(tcon, server,
272 							&rqst[num_rqst],
273 							cfile->fid.persistent_fid,
274 							cfile->fid.volatile_fid, current->tgid,
275 							FILE_BASIC_INFORMATION,
276 							SMB2_O_INFO_FILE, 0, data, size);
277 			} else {
278 				rc = SMB2_set_info_init(tcon, server,
279 							&rqst[num_rqst],
280 							COMPOUND_FID,
281 							COMPOUND_FID, current->tgid,
282 							FILE_BASIC_INFORMATION,
283 							SMB2_O_INFO_FILE, 0, data, size);
284 				if (!rc) {
285 					smb2_set_next_command(tcon, &rqst[num_rqst]);
286 					smb2_set_related(&rqst[num_rqst]);
287 				}
288 			}
289 
290 			if (rc)
291 				goto finished;
292 			num_rqst++;
293 			trace_smb3_set_info_compound_enter(xid, ses->Suid,
294 							   tcon->tid, full_path);
295 			break;
296 		case SMB2_OP_RENAME:
297 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
298 			rqst[num_rqst].rq_nvec = 2;
299 
300 			len = in_iov[i].iov_len;
301 
302 			vars->rename_info.ReplaceIfExists = 1;
303 			vars->rename_info.RootDirectory = 0;
304 			vars->rename_info.FileNameLength = cpu_to_le32(len);
305 
306 			size[0] = sizeof(struct smb2_file_rename_info);
307 			data[0] = &vars->rename_info;
308 
309 			size[1] = len + 2 /* null */;
310 			data[1] = in_iov[i].iov_base;
311 
312 			if (cfile) {
313 				rc = SMB2_set_info_init(tcon, server,
314 							&rqst[num_rqst],
315 							cfile->fid.persistent_fid,
316 							cfile->fid.volatile_fid,
317 							current->tgid, FILE_RENAME_INFORMATION,
318 							SMB2_O_INFO_FILE, 0, data, size);
319 			} else {
320 				rc = SMB2_set_info_init(tcon, server,
321 							&rqst[num_rqst],
322 							COMPOUND_FID, COMPOUND_FID,
323 							current->tgid, FILE_RENAME_INFORMATION,
324 							SMB2_O_INFO_FILE, 0, data, size);
325 				if (!rc) {
326 					smb2_set_next_command(tcon, &rqst[num_rqst]);
327 					smb2_set_related(&rqst[num_rqst]);
328 				}
329 			}
330 			if (rc)
331 				goto finished;
332 			num_rqst++;
333 			trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
334 			break;
335 		case SMB2_OP_HARDLINK:
336 			rqst[num_rqst].rq_iov = &vars->si_iov[0];
337 			rqst[num_rqst].rq_nvec = 2;
338 
339 			len = in_iov[i].iov_len;
340 
341 			vars->link_info.ReplaceIfExists = 0;
342 			vars->link_info.RootDirectory = 0;
343 			vars->link_info.FileNameLength = cpu_to_le32(len);
344 
345 			size[0] = sizeof(struct smb2_file_link_info);
346 			data[0] = &vars->link_info;
347 
348 			size[1] = len + 2 /* null */;
349 			data[1] = in_iov[i].iov_base;
350 
351 			rc = SMB2_set_info_init(tcon, server,
352 						&rqst[num_rqst], COMPOUND_FID,
353 						COMPOUND_FID, current->tgid,
354 						FILE_LINK_INFORMATION,
355 						SMB2_O_INFO_FILE, 0, data, size);
356 			if (rc)
357 				goto finished;
358 			smb2_set_next_command(tcon, &rqst[num_rqst]);
359 			smb2_set_related(&rqst[num_rqst++]);
360 			trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
361 			break;
362 		default:
363 			cifs_dbg(VFS, "Invalid command\n");
364 			rc = -EINVAL;
365 		}
366 	}
367 	if (rc)
368 		goto finished;
369 
370 	/* We already have a handle so we can skip the close */
371 	if (cfile)
372 		goto after_close;
373 	/* Close */
374 	flags |= CIFS_CP_CREATE_CLOSE_OP;
375 	rqst[num_rqst].rq_iov = &vars->close_iov;
376 	rqst[num_rqst].rq_nvec = 1;
377 	rc = SMB2_close_init(tcon, server,
378 			     &rqst[num_rqst], COMPOUND_FID,
379 			     COMPOUND_FID, false);
380 	smb2_set_related(&rqst[num_rqst]);
381 	if (rc)
382 		goto finished;
383  after_close:
384 	num_rqst++;
385 
386 	if (cfile) {
387 		rc = compound_send_recv(xid, ses, server,
388 					flags, num_rqst - 2,
389 					&rqst[1], &resp_buftype[1],
390 					&rsp_iov[1]);
391 	} else
392 		rc = compound_send_recv(xid, ses, server,
393 					flags, num_rqst,
394 					rqst, resp_buftype,
395 					rsp_iov);
396 
397 finished:
398 	num_rqst = 0;
399 	SMB2_open_free(&rqst[num_rqst++]);
400 	if (rc == -EREMCHG) {
401 		pr_warn_once("server share %s deleted\n", tcon->tree_name);
402 		tcon->need_reconnect = true;
403 	}
404 
405 	for (i = 0; i < num_cmds; i++) {
406 		switch (cmds[i]) {
407 		case SMB2_OP_QUERY_INFO:
408 			idata = in_iov[i].iov_base;
409 			if (rc == 0 && cfile && cfile->symlink_target) {
410 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
411 				if (!idata->symlink_target)
412 					rc = -ENOMEM;
413 			}
414 			if (rc == 0) {
415 				qi_rsp = (struct smb2_query_info_rsp *)
416 					rsp_iov[i + 1].iov_base;
417 				rc = smb2_validate_and_copy_iov(
418 					le16_to_cpu(qi_rsp->OutputBufferOffset),
419 					le32_to_cpu(qi_rsp->OutputBufferLength),
420 					&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
421 			}
422 			SMB2_query_info_free(&rqst[num_rqst++]);
423 			if (rc)
424 				trace_smb3_query_info_compound_err(xid,  ses->Suid,
425 								   tcon->tid, rc);
426 			else
427 				trace_smb3_query_info_compound_done(xid, ses->Suid,
428 								    tcon->tid);
429 			break;
430 		case SMB2_OP_POSIX_QUERY_INFO:
431 			idata = in_iov[i].iov_base;
432 			if (rc == 0 && cfile && cfile->symlink_target) {
433 				idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
434 				if (!idata->symlink_target)
435 					rc = -ENOMEM;
436 			}
437 			if (rc == 0) {
438 				qi_rsp = (struct smb2_query_info_rsp *)
439 					rsp_iov[i + 1].iov_base;
440 				rc = smb2_validate_and_copy_iov(
441 					le16_to_cpu(qi_rsp->OutputBufferOffset),
442 					le32_to_cpu(qi_rsp->OutputBufferLength),
443 					&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
444 					(char *)&idata->posix_fi);
445 			}
446 			if (rc == 0) {
447 				unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength);
448 
449 				if (length > sizeof(idata->posix_fi)) {
450 					char *base = (char *)rsp_iov[i + 1].iov_base +
451 						le16_to_cpu(qi_rsp->OutputBufferOffset) +
452 						sizeof(idata->posix_fi);
453 					*extbuflen = length - sizeof(idata->posix_fi);
454 					*extbuf = kmemdup(base, *extbuflen, GFP_KERNEL);
455 					if (!*extbuf)
456 						rc = -ENOMEM;
457 				} else {
458 					rc = -EINVAL;
459 				}
460 			}
461 			SMB2_query_info_free(&rqst[num_rqst++]);
462 			if (rc)
463 				trace_smb3_posix_query_info_compound_err(xid,  ses->Suid,
464 									 tcon->tid, rc);
465 			else
466 				trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
467 									  tcon->tid);
468 			break;
469 		case SMB2_OP_DELETE:
470 			if (rc)
471 				trace_smb3_delete_err(xid,  ses->Suid, tcon->tid, rc);
472 			else
473 				trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
474 			break;
475 		case SMB2_OP_MKDIR:
476 			if (rc)
477 				trace_smb3_mkdir_err(xid,  ses->Suid, tcon->tid, rc);
478 			else
479 				trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
480 			break;
481 		case SMB2_OP_HARDLINK:
482 			if (rc)
483 				trace_smb3_hardlink_err(xid,  ses->Suid, tcon->tid, rc);
484 			else
485 				trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
486 			SMB2_set_info_free(&rqst[num_rqst++]);
487 			break;
488 		case SMB2_OP_RENAME:
489 			if (rc)
490 				trace_smb3_rename_err(xid,  ses->Suid, tcon->tid, rc);
491 			else
492 				trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
493 			SMB2_set_info_free(&rqst[num_rqst++]);
494 			break;
495 		case SMB2_OP_RMDIR:
496 			if (rc)
497 				trace_smb3_rmdir_err(xid,  ses->Suid, tcon->tid, rc);
498 			else
499 				trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
500 			SMB2_set_info_free(&rqst[num_rqst++]);
501 			break;
502 		case SMB2_OP_SET_EOF:
503 			if (rc)
504 				trace_smb3_set_eof_err(xid,  ses->Suid, tcon->tid, rc);
505 			else
506 				trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
507 			SMB2_set_info_free(&rqst[num_rqst++]);
508 			break;
509 		case SMB2_OP_SET_INFO:
510 			if (rc)
511 				trace_smb3_set_info_compound_err(xid,  ses->Suid,
512 								 tcon->tid, rc);
513 			else
514 				trace_smb3_set_info_compound_done(xid, ses->Suid,
515 								  tcon->tid);
516 			SMB2_set_info_free(&rqst[num_rqst++]);
517 			break;
518 		}
519 	}
520 	SMB2_close_free(&rqst[num_rqst]);
521 
522 	if (cfile)
523 		cifsFileInfo_put(cfile);
524 
525 	num_cmds += 2;
526 	if (out_iov && out_buftype) {
527 		memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
528 		memcpy(out_buftype, resp_buftype,
529 		       num_cmds * sizeof(*out_buftype));
530 	} else {
531 		for (i = 0; i < num_cmds; i++)
532 			free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
533 	}
534 	kfree(vars);
535 	return rc;
536 }
537 
538 static int parse_create_response(struct cifs_open_info_data *data,
539 				 struct cifs_sb_info *cifs_sb,
540 				 const struct kvec *iov)
541 {
542 	struct smb2_create_rsp *rsp = iov->iov_base;
543 	bool reparse_point = false;
544 	u32 tag = 0;
545 	int rc = 0;
546 
547 	switch (rsp->hdr.Status) {
548 	case STATUS_IO_REPARSE_TAG_NOT_HANDLED:
549 		reparse_point = true;
550 		break;
551 	case STATUS_STOPPED_ON_SYMLINK:
552 		rc = smb2_parse_symlink_response(cifs_sb, iov,
553 						 &data->symlink_target);
554 		if (rc)
555 			return rc;
556 		tag = IO_REPARSE_TAG_SYMLINK;
557 		reparse_point = true;
558 		break;
559 	case STATUS_SUCCESS:
560 		reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
561 		break;
562 	}
563 	data->reparse_point = reparse_point;
564 	data->reparse.tag = tag;
565 	return rc;
566 }
567 
568 int smb2_query_path_info(const unsigned int xid,
569 			 struct cifs_tcon *tcon,
570 			 struct cifs_sb_info *cifs_sb,
571 			 const char *full_path,
572 			 struct cifs_open_info_data *data)
573 {
574 	__u32 create_options = 0;
575 	struct cifsFileInfo *cfile;
576 	struct cached_fid *cfid = NULL;
577 	struct smb2_hdr *hdr;
578 	struct kvec in_iov, out_iov[3] = {};
579 	int out_buftype[3] = {};
580 	bool islink;
581 	int cmd = SMB2_OP_QUERY_INFO;
582 	int rc, rc2;
583 
584 	data->adjust_tz = false;
585 	data->reparse_point = false;
586 
587 	if (strcmp(full_path, ""))
588 		rc = -ENOENT;
589 	else
590 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
591 	/* If it is a root and its handle is cached then use it */
592 	if (!rc) {
593 		if (cfid->file_all_info_is_valid) {
594 			memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi));
595 		} else {
596 			rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid,
597 					     cfid->fid.volatile_fid, &data->fi);
598 		}
599 		close_cached_dir(cfid);
600 		return rc;
601 	}
602 
603 	in_iov.iov_base = data;
604 	in_iov.iov_len = sizeof(*data);
605 
606 	cifs_get_readable_path(tcon, full_path, &cfile);
607 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
608 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
609 			      create_options, ACL_NO_MODE, &in_iov,
610 			      &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype);
611 	hdr = out_iov[0].iov_base;
612 	/*
613 	 * If first iov is unset, then SMB session was dropped or we've got a
614 	 * cached open file (@cfile).
615 	 */
616 	if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
617 		goto out;
618 
619 	switch (rc) {
620 	case 0:
621 	case -EOPNOTSUPP:
622 		rc = parse_create_response(data, cifs_sb, &out_iov[0]);
623 		if (rc || !data->reparse_point)
624 			goto out;
625 
626 		create_options |= OPEN_REPARSE_POINT;
627 		/* Failed on a symbolic link - query a reparse point info */
628 		cifs_get_readable_path(tcon, full_path, &cfile);
629 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
630 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
631 				      create_options, ACL_NO_MODE, &in_iov,
632 				      &cmd, 1, cfile, NULL, NULL, NULL, NULL);
633 		break;
634 	case -EREMOTE:
635 		break;
636 	default:
637 		if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
638 			break;
639 		rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
640 						     full_path, &islink);
641 		if (rc2) {
642 			rc = rc2;
643 			goto out;
644 		}
645 		if (islink)
646 			rc = -EREMOTE;
647 	}
648 
649 out:
650 	free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
651 	free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
652 	free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
653 	return rc;
654 }
655 
656 int smb311_posix_query_path_info(const unsigned int xid,
657 				 struct cifs_tcon *tcon,
658 				 struct cifs_sb_info *cifs_sb,
659 				 const char *full_path,
660 				 struct cifs_open_info_data *data,
661 				 struct cifs_sid *owner,
662 				 struct cifs_sid *group)
663 {
664 	int rc;
665 	__u32 create_options = 0;
666 	struct cifsFileInfo *cfile;
667 	struct kvec in_iov, out_iov[3] = {};
668 	int out_buftype[3] = {};
669 	__u8 *sidsbuf = NULL;
670 	__u8 *sidsbuf_end = NULL;
671 	size_t sidsbuflen = 0;
672 	size_t owner_len, group_len;
673 	int cmd = SMB2_OP_POSIX_QUERY_INFO;
674 
675 	data->adjust_tz = false;
676 	data->reparse_point = false;
677 
678 	/*
679 	 * BB TODO: Add support for using the cached root handle.
680 	 * Create SMB2_query_posix_info worker function to do non-compounded query
681 	 * when we already have an open file handle for this. For now this is fast enough
682 	 * (always using the compounded version).
683 	 */
684 	in_iov.iov_base = data;
685 	in_iov.iov_len = sizeof(*data);
686 
687 	cifs_get_readable_path(tcon, full_path, &cfile);
688 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
689 			      FILE_READ_ATTRIBUTES, FILE_OPEN,
690 			      create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
691 			      cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
692 	/*
693 	 * If first iov is unset, then SMB session was dropped or we've got a
694 	 * cached open file (@cfile).
695 	 */
696 	if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER)
697 		goto out;
698 
699 	switch (rc) {
700 	case 0:
701 	case -EOPNOTSUPP:
702 		/* BB TODO: When support for special files added to Samba re-verify this path */
703 		rc = parse_create_response(data, cifs_sb, &out_iov[0]);
704 		if (rc || !data->reparse_point)
705 			goto out;
706 
707 		create_options |= OPEN_REPARSE_POINT;
708 		/* Failed on a symbolic link - query a reparse point info */
709 		cifs_get_readable_path(tcon, full_path, &cfile);
710 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
711 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
712 				      create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
713 				      cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
714 		break;
715 	}
716 
717 out:
718 	if (rc == 0) {
719 		sidsbuf_end = sidsbuf + sidsbuflen;
720 
721 		owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
722 		if (owner_len == -1) {
723 			rc = -EINVAL;
724 			goto out;
725 		}
726 		memcpy(owner, sidsbuf, owner_len);
727 
728 		group_len = posix_info_sid_size(
729 			sidsbuf + owner_len, sidsbuf_end);
730 		if (group_len == -1) {
731 			rc = -EINVAL;
732 			goto out;
733 		}
734 		memcpy(group, sidsbuf + owner_len, group_len);
735 	}
736 
737 	kfree(sidsbuf);
738 	free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
739 	free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
740 	free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
741 	return rc;
742 }
743 
744 int
745 smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
746 	   struct cifs_tcon *tcon, const char *name,
747 	   struct cifs_sb_info *cifs_sb)
748 {
749 	return smb2_compound_op(xid, tcon, cifs_sb, name,
750 				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
751 				CREATE_NOT_FILE, mode, NULL,
752 				&(int){SMB2_OP_MKDIR}, 1,
753 				NULL, NULL, NULL, NULL, NULL);
754 }
755 
756 void
757 smb2_mkdir_setinfo(struct inode *inode, const char *name,
758 		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
759 		   const unsigned int xid)
760 {
761 	FILE_BASIC_INFO data = {};
762 	struct cifsInodeInfo *cifs_i;
763 	struct cifsFileInfo *cfile;
764 	struct kvec in_iov;
765 	u32 dosattrs;
766 	int tmprc;
767 
768 	in_iov.iov_base = &data;
769 	in_iov.iov_len = sizeof(data);
770 	cifs_i = CIFS_I(inode);
771 	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
772 	data.Attributes = cpu_to_le32(dosattrs);
773 	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
774 	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
775 				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
776 				 CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
777 				 &(int){SMB2_OP_SET_INFO}, 1,
778 				 cfile, NULL, NULL, NULL, NULL);
779 	if (tmprc == 0)
780 		cifs_i->cifsAttrs = dosattrs;
781 }
782 
783 int
784 smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
785 	   struct cifs_sb_info *cifs_sb)
786 {
787 	drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
788 	return smb2_compound_op(xid, tcon, cifs_sb, name,
789 				DELETE, FILE_OPEN, CREATE_NOT_FILE,
790 				ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1,
791 				NULL, NULL, NULL, NULL, NULL);
792 }
793 
794 int
795 smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
796 	    struct cifs_sb_info *cifs_sb)
797 {
798 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
799 				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
800 				ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1,
801 				NULL, NULL, NULL, NULL, NULL);
802 }
803 
804 static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
805 			      const char *from_name, const char *to_name,
806 			      struct cifs_sb_info *cifs_sb,
807 			      __u32 create_options, __u32 access,
808 			      int command, struct cifsFileInfo *cfile)
809 {
810 	struct kvec in_iov;
811 	__le16 *smb2_to_name = NULL;
812 	int rc;
813 
814 	smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
815 	if (smb2_to_name == NULL) {
816 		rc = -ENOMEM;
817 		goto smb2_rename_path;
818 	}
819 	in_iov.iov_base = smb2_to_name;
820 	in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
821 
822 	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
823 			      FILE_OPEN, 0, ACL_NO_MODE, &in_iov,
824 			      &command, 1, cfile, NULL, NULL, NULL, NULL);
825 smb2_rename_path:
826 	kfree(smb2_to_name);
827 	return rc;
828 }
829 
830 int smb2_rename_path(const unsigned int xid,
831 		     struct cifs_tcon *tcon,
832 		     struct dentry *source_dentry,
833 		     const char *from_name, const char *to_name,
834 		     struct cifs_sb_info *cifs_sb)
835 {
836 	struct cifsFileInfo *cfile;
837 	__u32 co = file_create_options(source_dentry);
838 
839 	drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
840 	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
841 
842 	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
843 				  co, DELETE, SMB2_OP_RENAME, cfile);
844 }
845 
846 int smb2_create_hardlink(const unsigned int xid,
847 			 struct cifs_tcon *tcon,
848 			 struct dentry *source_dentry,
849 			 const char *from_name, const char *to_name,
850 			 struct cifs_sb_info *cifs_sb)
851 {
852 	__u32 co = file_create_options(source_dentry);
853 
854 	return smb2_set_path_attr(xid, tcon, from_name, to_name,
855 				  cifs_sb, co, FILE_READ_ATTRIBUTES,
856 				  SMB2_OP_HARDLINK, NULL);
857 }
858 
859 int
860 smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
861 		   const char *full_path, __u64 size,
862 		   struct cifs_sb_info *cifs_sb, bool set_alloc)
863 {
864 	struct cifsFileInfo *cfile;
865 	struct kvec in_iov;
866 	__le64 eof = cpu_to_le64(size);
867 
868 	in_iov.iov_base = &eof;
869 	in_iov.iov_len = sizeof(eof);
870 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
871 	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
872 				FILE_WRITE_DATA, FILE_OPEN,
873 				0, ACL_NO_MODE, &in_iov,
874 				&(int){SMB2_OP_SET_EOF}, 1,
875 				cfile, NULL, NULL, NULL, NULL);
876 }
877 
878 int
879 smb2_set_file_info(struct inode *inode, const char *full_path,
880 		   FILE_BASIC_INFO *buf, const unsigned int xid)
881 {
882 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
883 	struct tcon_link *tlink;
884 	struct cifs_tcon *tcon;
885 	struct cifsFileInfo *cfile;
886 	struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
887 	int rc;
888 
889 	if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
890 	    (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
891 	    (buf->Attributes == 0))
892 		return 0; /* would be a no op, no sense sending this */
893 
894 	tlink = cifs_sb_tlink(cifs_sb);
895 	if (IS_ERR(tlink))
896 		return PTR_ERR(tlink);
897 	tcon = tlink_tcon(tlink);
898 
899 	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
900 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
901 			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
902 			      0, ACL_NO_MODE, &in_iov,
903 			      &(int){SMB2_OP_SET_INFO}, 1, cfile,
904 			      NULL, NULL, NULL, NULL);
905 	cifs_put_tlink(tlink);
906 	return rc;
907 }
908