cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

smb2inode.c (20720B)


      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
     27static void
     28free_set_inf_compound(struct smb_rqst *rqst)
     29{
     30	if (rqst[1].rq_iov)
     31		SMB2_set_info_free(&rqst[1]);
     32	if (rqst[2].rq_iov)
     33		SMB2_close_free(&rqst[2]);
     34}
     35
     36
     37struct cop_vars {
     38	struct cifs_open_parms oparms;
     39	struct kvec rsp_iov[3];
     40	struct smb_rqst rqst[3];
     41	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
     42	struct kvec qi_iov[1];
     43	struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
     44	struct kvec close_iov[1];
     45	struct smb2_file_rename_info rename_info;
     46	struct smb2_file_link_info link_info;
     47};
     48
     49/*
     50 * note: If cfile is passed, the reference to it is dropped here.
     51 * So make sure that you do not reuse cfile after return from this func.
     52 */
     53static int
     54smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
     55		 struct cifs_sb_info *cifs_sb, const char *full_path,
     56		 __u32 desired_access, __u32 create_disposition,
     57		 __u32 create_options, umode_t mode, void *ptr, int command,
     58		 struct cifsFileInfo *cfile)
     59{
     60	struct cop_vars *vars = NULL;
     61	struct kvec *rsp_iov;
     62	struct smb_rqst *rqst;
     63	int rc;
     64	__le16 *utf16_path = NULL;
     65	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
     66	struct cifs_fid fid;
     67	struct cifs_ses *ses = tcon->ses;
     68	struct TCP_Server_Info *server;
     69	int num_rqst = 0;
     70	int resp_buftype[3];
     71	struct smb2_query_info_rsp *qi_rsp = NULL;
     72	int flags = 0;
     73	__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
     74	unsigned int size[2];
     75	void *data[2];
     76	int len;
     77
     78	vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
     79	if (vars == NULL)
     80		return -ENOMEM;
     81	rqst = &vars->rqst[0];
     82	rsp_iov = &vars->rsp_iov[0];
     83
     84	server = cifs_pick_channel(ses);
     85
     86	if (smb3_encryption_required(tcon))
     87		flags |= CIFS_TRANSFORM_REQ;
     88
     89	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
     90
     91	/* We already have a handle so we can skip the open */
     92	if (cfile)
     93		goto after_open;
     94
     95	/* Open */
     96	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
     97	if (!utf16_path) {
     98		rc = -ENOMEM;
     99		goto finished;
    100	}
    101
    102	vars->oparms.tcon = tcon;
    103	vars->oparms.desired_access = desired_access;
    104	vars->oparms.disposition = create_disposition;
    105	vars->oparms.create_options = cifs_create_options(cifs_sb, create_options);
    106	vars->oparms.fid = &fid;
    107	vars->oparms.reconnect = false;
    108	vars->oparms.mode = mode;
    109	vars->oparms.cifs_sb = cifs_sb;
    110
    111	rqst[num_rqst].rq_iov = &vars->open_iov[0];
    112	rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
    113	rc = SMB2_open_init(tcon, server,
    114			    &rqst[num_rqst], &oplock, &vars->oparms,
    115			    utf16_path);
    116	kfree(utf16_path);
    117	if (rc)
    118		goto finished;
    119
    120	smb2_set_next_command(tcon, &rqst[num_rqst]);
    121 after_open:
    122	num_rqst++;
    123	rc = 0;
    124
    125	/* Operation */
    126	switch (command) {
    127	case SMB2_OP_QUERY_INFO:
    128		rqst[num_rqst].rq_iov = &vars->qi_iov[0];
    129		rqst[num_rqst].rq_nvec = 1;
    130
    131		if (cfile)
    132			rc = SMB2_query_info_init(tcon, server,
    133				&rqst[num_rqst],
    134				cfile->fid.persistent_fid,
    135				cfile->fid.volatile_fid,
    136				FILE_ALL_INFORMATION,
    137				SMB2_O_INFO_FILE, 0,
    138				sizeof(struct smb2_file_all_info) +
    139					  PATH_MAX * 2, 0, NULL);
    140		else {
    141			rc = SMB2_query_info_init(tcon, server,
    142				&rqst[num_rqst],
    143				COMPOUND_FID,
    144				COMPOUND_FID,
    145				FILE_ALL_INFORMATION,
    146				SMB2_O_INFO_FILE, 0,
    147				sizeof(struct smb2_file_all_info) +
    148					  PATH_MAX * 2, 0, NULL);
    149			if (!rc) {
    150				smb2_set_next_command(tcon, &rqst[num_rqst]);
    151				smb2_set_related(&rqst[num_rqst]);
    152			}
    153		}
    154
    155		if (rc)
    156			goto finished;
    157		num_rqst++;
    158		trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
    159						     full_path);
    160		break;
    161	case SMB2_OP_POSIX_QUERY_INFO:
    162		rqst[num_rqst].rq_iov = &vars->qi_iov[0];
    163		rqst[num_rqst].rq_nvec = 1;
    164
    165		if (cfile)
    166			rc = SMB2_query_info_init(tcon, server,
    167				&rqst[num_rqst],
    168				cfile->fid.persistent_fid,
    169				cfile->fid.volatile_fid,
    170				SMB_FIND_FILE_POSIX_INFO,
    171				SMB2_O_INFO_FILE, 0,
    172				/* TBD: fix following to allow for longer SIDs */
    173				sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
    174				(sizeof(struct cifs_sid) * 2), 0, NULL);
    175		else {
    176			rc = SMB2_query_info_init(tcon, server,
    177				&rqst[num_rqst],
    178				COMPOUND_FID,
    179				COMPOUND_FID,
    180				SMB_FIND_FILE_POSIX_INFO,
    181				SMB2_O_INFO_FILE, 0,
    182				sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
    183				(sizeof(struct cifs_sid) * 2), 0, NULL);
    184			if (!rc) {
    185				smb2_set_next_command(tcon, &rqst[num_rqst]);
    186				smb2_set_related(&rqst[num_rqst]);
    187			}
    188		}
    189
    190		if (rc)
    191			goto finished;
    192		num_rqst++;
    193		trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path);
    194		break;
    195	case SMB2_OP_DELETE:
    196		trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
    197		break;
    198	case SMB2_OP_MKDIR:
    199		/*
    200		 * Directories are created through parameters in the
    201		 * SMB2_open() call.
    202		 */
    203		trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
    204		break;
    205	case SMB2_OP_RMDIR:
    206		rqst[num_rqst].rq_iov = &vars->si_iov[0];
    207		rqst[num_rqst].rq_nvec = 1;
    208
    209		size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
    210		data[0] = &delete_pending[0];
    211
    212		rc = SMB2_set_info_init(tcon, server,
    213					&rqst[num_rqst], COMPOUND_FID,
    214					COMPOUND_FID, current->tgid,
    215					FILE_DISPOSITION_INFORMATION,
    216					SMB2_O_INFO_FILE, 0, data, size);
    217		if (rc)
    218			goto finished;
    219		smb2_set_next_command(tcon, &rqst[num_rqst]);
    220		smb2_set_related(&rqst[num_rqst++]);
    221		trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
    222		break;
    223	case SMB2_OP_SET_EOF:
    224		rqst[num_rqst].rq_iov = &vars->si_iov[0];
    225		rqst[num_rqst].rq_nvec = 1;
    226
    227		size[0] = 8; /* sizeof __le64 */
    228		data[0] = ptr;
    229
    230		rc = SMB2_set_info_init(tcon, server,
    231					&rqst[num_rqst], COMPOUND_FID,
    232					COMPOUND_FID, current->tgid,
    233					FILE_END_OF_FILE_INFORMATION,
    234					SMB2_O_INFO_FILE, 0, data, size);
    235		if (rc)
    236			goto finished;
    237		smb2_set_next_command(tcon, &rqst[num_rqst]);
    238		smb2_set_related(&rqst[num_rqst++]);
    239		trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
    240		break;
    241	case SMB2_OP_SET_INFO:
    242		rqst[num_rqst].rq_iov = &vars->si_iov[0];
    243		rqst[num_rqst].rq_nvec = 1;
    244
    245
    246		size[0] = sizeof(FILE_BASIC_INFO);
    247		data[0] = ptr;
    248
    249		if (cfile)
    250			rc = SMB2_set_info_init(tcon, server,
    251				&rqst[num_rqst],
    252				cfile->fid.persistent_fid,
    253				cfile->fid.volatile_fid, current->tgid,
    254				FILE_BASIC_INFORMATION,
    255				SMB2_O_INFO_FILE, 0, data, size);
    256		else {
    257			rc = SMB2_set_info_init(tcon, server,
    258				&rqst[num_rqst],
    259				COMPOUND_FID,
    260				COMPOUND_FID, current->tgid,
    261				FILE_BASIC_INFORMATION,
    262				SMB2_O_INFO_FILE, 0, data, size);
    263			if (!rc) {
    264				smb2_set_next_command(tcon, &rqst[num_rqst]);
    265				smb2_set_related(&rqst[num_rqst]);
    266			}
    267		}
    268
    269		if (rc)
    270			goto finished;
    271		num_rqst++;
    272		trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid,
    273						   full_path);
    274		break;
    275	case SMB2_OP_RENAME:
    276		rqst[num_rqst].rq_iov = &vars->si_iov[0];
    277		rqst[num_rqst].rq_nvec = 2;
    278
    279		len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX));
    280
    281		vars->rename_info.ReplaceIfExists = 1;
    282		vars->rename_info.RootDirectory = 0;
    283		vars->rename_info.FileNameLength = cpu_to_le32(len);
    284
    285		size[0] = sizeof(struct smb2_file_rename_info);
    286		data[0] = &vars->rename_info;
    287
    288		size[1] = len + 2 /* null */;
    289		data[1] = (__le16 *)ptr;
    290
    291		if (cfile)
    292			rc = SMB2_set_info_init(tcon, server,
    293						&rqst[num_rqst],
    294						cfile->fid.persistent_fid,
    295						cfile->fid.volatile_fid,
    296					current->tgid, FILE_RENAME_INFORMATION,
    297					SMB2_O_INFO_FILE, 0, data, size);
    298		else {
    299			rc = SMB2_set_info_init(tcon, server,
    300					&rqst[num_rqst],
    301					COMPOUND_FID, COMPOUND_FID,
    302					current->tgid, FILE_RENAME_INFORMATION,
    303					SMB2_O_INFO_FILE, 0, data, size);
    304			if (!rc) {
    305				smb2_set_next_command(tcon, &rqst[num_rqst]);
    306				smb2_set_related(&rqst[num_rqst]);
    307			}
    308		}
    309		if (rc)
    310			goto finished;
    311		num_rqst++;
    312		trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
    313		break;
    314	case SMB2_OP_HARDLINK:
    315		rqst[num_rqst].rq_iov = &vars->si_iov[0];
    316		rqst[num_rqst].rq_nvec = 2;
    317
    318		len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX));
    319
    320		vars->link_info.ReplaceIfExists = 0;
    321		vars->link_info.RootDirectory = 0;
    322		vars->link_info.FileNameLength = cpu_to_le32(len);
    323
    324		size[0] = sizeof(struct smb2_file_link_info);
    325		data[0] = &vars->link_info;
    326
    327		size[1] = len + 2 /* null */;
    328		data[1] = (__le16 *)ptr;
    329
    330		rc = SMB2_set_info_init(tcon, server,
    331					&rqst[num_rqst], COMPOUND_FID,
    332					COMPOUND_FID, current->tgid,
    333					FILE_LINK_INFORMATION,
    334					SMB2_O_INFO_FILE, 0, data, size);
    335		if (rc)
    336			goto finished;
    337		smb2_set_next_command(tcon, &rqst[num_rqst]);
    338		smb2_set_related(&rqst[num_rqst++]);
    339		trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
    340		break;
    341	default:
    342		cifs_dbg(VFS, "Invalid command\n");
    343		rc = -EINVAL;
    344	}
    345	if (rc)
    346		goto finished;
    347
    348	/* We already have a handle so we can skip the close */
    349	if (cfile)
    350		goto after_close;
    351	/* Close */
    352	flags |= CIFS_CP_CREATE_CLOSE_OP;
    353	rqst[num_rqst].rq_iov = &vars->close_iov[0];
    354	rqst[num_rqst].rq_nvec = 1;
    355	rc = SMB2_close_init(tcon, server,
    356			     &rqst[num_rqst], COMPOUND_FID,
    357			     COMPOUND_FID, false);
    358	smb2_set_related(&rqst[num_rqst]);
    359	if (rc)
    360		goto finished;
    361 after_close:
    362	num_rqst++;
    363
    364	if (cfile) {
    365		rc = compound_send_recv(xid, ses, server,
    366					flags, num_rqst - 2,
    367					&rqst[1], &resp_buftype[1],
    368					&rsp_iov[1]);
    369	} else
    370		rc = compound_send_recv(xid, ses, server,
    371					flags, num_rqst,
    372					rqst, resp_buftype,
    373					rsp_iov);
    374
    375 finished:
    376	if (cfile)
    377		cifsFileInfo_put(cfile);
    378
    379	SMB2_open_free(&rqst[0]);
    380	if (rc == -EREMCHG) {
    381		pr_warn_once("server share %s deleted\n", tcon->treeName);
    382		tcon->need_reconnect = true;
    383	}
    384
    385	switch (command) {
    386	case SMB2_OP_QUERY_INFO:
    387		if (rc == 0) {
    388			qi_rsp = (struct smb2_query_info_rsp *)
    389				rsp_iov[1].iov_base;
    390			rc = smb2_validate_and_copy_iov(
    391				le16_to_cpu(qi_rsp->OutputBufferOffset),
    392				le32_to_cpu(qi_rsp->OutputBufferLength),
    393				&rsp_iov[1], sizeof(struct smb2_file_all_info),
    394				ptr);
    395		}
    396		if (rqst[1].rq_iov)
    397			SMB2_query_info_free(&rqst[1]);
    398		if (rqst[2].rq_iov)
    399			SMB2_close_free(&rqst[2]);
    400		if (rc)
    401			trace_smb3_query_info_compound_err(xid,  ses->Suid,
    402						tcon->tid, rc);
    403		else
    404			trace_smb3_query_info_compound_done(xid, ses->Suid,
    405						tcon->tid);
    406		break;
    407	case SMB2_OP_POSIX_QUERY_INFO:
    408		if (rc == 0) {
    409			qi_rsp = (struct smb2_query_info_rsp *)
    410				rsp_iov[1].iov_base;
    411			rc = smb2_validate_and_copy_iov(
    412				le16_to_cpu(qi_rsp->OutputBufferOffset),
    413				le32_to_cpu(qi_rsp->OutputBufferLength),
    414				&rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr);
    415		}
    416		if (rqst[1].rq_iov)
    417			SMB2_query_info_free(&rqst[1]);
    418		if (rqst[2].rq_iov)
    419			SMB2_close_free(&rqst[2]);
    420		if (rc)
    421			trace_smb3_posix_query_info_compound_err(xid,  ses->Suid, tcon->tid, rc);
    422		else
    423			trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid);
    424		break;
    425	case SMB2_OP_DELETE:
    426		if (rc)
    427			trace_smb3_delete_err(xid,  ses->Suid, tcon->tid, rc);
    428		else
    429			trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
    430		if (rqst[1].rq_iov)
    431			SMB2_close_free(&rqst[1]);
    432		break;
    433	case SMB2_OP_MKDIR:
    434		if (rc)
    435			trace_smb3_mkdir_err(xid,  ses->Suid, tcon->tid, rc);
    436		else
    437			trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
    438		if (rqst[1].rq_iov)
    439			SMB2_close_free(&rqst[1]);
    440		break;
    441	case SMB2_OP_HARDLINK:
    442		if (rc)
    443			trace_smb3_hardlink_err(xid,  ses->Suid, tcon->tid, rc);
    444		else
    445			trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
    446		free_set_inf_compound(rqst);
    447		break;
    448	case SMB2_OP_RENAME:
    449		if (rc)
    450			trace_smb3_rename_err(xid,  ses->Suid, tcon->tid, rc);
    451		else
    452			trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
    453		free_set_inf_compound(rqst);
    454		break;
    455	case SMB2_OP_RMDIR:
    456		if (rc)
    457			trace_smb3_rmdir_err(xid,  ses->Suid, tcon->tid, rc);
    458		else
    459			trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
    460		free_set_inf_compound(rqst);
    461		break;
    462	case SMB2_OP_SET_EOF:
    463		if (rc)
    464			trace_smb3_set_eof_err(xid,  ses->Suid, tcon->tid, rc);
    465		else
    466			trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
    467		free_set_inf_compound(rqst);
    468		break;
    469	case SMB2_OP_SET_INFO:
    470		if (rc)
    471			trace_smb3_set_info_compound_err(xid,  ses->Suid,
    472						tcon->tid, rc);
    473		else
    474			trace_smb3_set_info_compound_done(xid, ses->Suid,
    475						tcon->tid);
    476		free_set_inf_compound(rqst);
    477		break;
    478	}
    479	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
    480	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
    481	free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
    482	kfree(vars);
    483	return rc;
    484}
    485
    486void
    487move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
    488{
    489	memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
    490	dst->CurrentByteOffset = src->CurrentByteOffset;
    491	dst->Mode = src->Mode;
    492	dst->AlignmentRequirement = src->AlignmentRequirement;
    493	dst->IndexNumber1 = 0; /* we don't use it */
    494}
    495
    496int
    497smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
    498		     struct cifs_sb_info *cifs_sb, const char *full_path,
    499		     FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse)
    500{
    501	int rc;
    502	struct smb2_file_all_info *smb2_data;
    503	__u32 create_options = 0;
    504	struct cifsFileInfo *cfile;
    505	struct cached_fid *cfid = NULL;
    506
    507	*adjust_tz = false;
    508	*reparse = false;
    509
    510	smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
    511			    GFP_KERNEL);
    512	if (smb2_data == NULL)
    513		return -ENOMEM;
    514
    515	if (strcmp(full_path, ""))
    516		rc = -ENOENT;
    517	else
    518		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
    519	/* If it is a root and its handle is cached then use it */
    520	if (!rc) {
    521		if (tcon->crfid.file_all_info_is_valid) {
    522			move_smb2_info_to_cifs(data,
    523					       &tcon->crfid.file_all_info);
    524		} else {
    525			rc = SMB2_query_info(xid, tcon,
    526					     cfid->fid->persistent_fid,
    527					     cfid->fid->volatile_fid, smb2_data);
    528			if (!rc)
    529				move_smb2_info_to_cifs(data, smb2_data);
    530		}
    531		close_cached_dir(cfid);
    532		goto out;
    533	}
    534
    535	cifs_get_readable_path(tcon, full_path, &cfile);
    536	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
    537			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
    538			      ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
    539	if (rc == -EOPNOTSUPP) {
    540		*reparse = true;
    541		create_options |= OPEN_REPARSE_POINT;
    542
    543		/* Failed on a symbolic link - query a reparse point info */
    544		cifs_get_readable_path(tcon, full_path, &cfile);
    545		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
    546				      FILE_READ_ATTRIBUTES, FILE_OPEN,
    547				      create_options, ACL_NO_MODE,
    548				      smb2_data, SMB2_OP_QUERY_INFO, cfile);
    549	}
    550	if (rc)
    551		goto out;
    552
    553	move_smb2_info_to_cifs(data, smb2_data);
    554out:
    555	kfree(smb2_data);
    556	return rc;
    557}
    558
    559
    560int
    561smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
    562		     struct cifs_sb_info *cifs_sb, const char *full_path,
    563		     struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse)
    564{
    565	int rc;
    566	__u32 create_options = 0;
    567	struct cifsFileInfo *cfile;
    568	struct smb311_posix_qinfo *smb2_data;
    569
    570	*adjust_tz = false;
    571	*reparse = false;
    572
    573	/* BB TODO: Make struct larger when add support for parsing owner SIDs */
    574	smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo),
    575			    GFP_KERNEL);
    576	if (smb2_data == NULL)
    577		return -ENOMEM;
    578
    579	/*
    580	 * BB TODO: Add support for using the cached root handle.
    581	 * Create SMB2_query_posix_info worker function to do non-compounded query
    582	 * when we already have an open file handle for this. For now this is fast enough
    583	 * (always using the compounded version).
    584	 */
    585
    586	cifs_get_readable_path(tcon, full_path, &cfile);
    587	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
    588			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
    589			      ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
    590	if (rc == -EOPNOTSUPP) {
    591		/* BB TODO: When support for special files added to Samba re-verify this path */
    592		*reparse = true;
    593		create_options |= OPEN_REPARSE_POINT;
    594
    595		/* Failed on a symbolic link - query a reparse point info */
    596		cifs_get_readable_path(tcon, full_path, &cfile);
    597		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
    598				      FILE_READ_ATTRIBUTES, FILE_OPEN,
    599				      create_options, ACL_NO_MODE,
    600				      smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
    601	}
    602	if (rc)
    603		goto out;
    604
    605	 /* TODO: will need to allow for the 2 SIDs when add support for getting owner UID/GID */
    606	memcpy(data, smb2_data, sizeof(struct smb311_posix_qinfo));
    607
    608out:
    609	kfree(smb2_data);
    610	return rc;
    611}
    612
    613int
    614smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
    615	   struct cifs_tcon *tcon, const char *name,
    616	   struct cifs_sb_info *cifs_sb)
    617{
    618	return smb2_compound_op(xid, tcon, cifs_sb, name,
    619				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
    620				CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
    621				NULL);
    622}
    623
    624void
    625smb2_mkdir_setinfo(struct inode *inode, const char *name,
    626		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
    627		   const unsigned int xid)
    628{
    629	FILE_BASIC_INFO data;
    630	struct cifsInodeInfo *cifs_i;
    631	struct cifsFileInfo *cfile;
    632	u32 dosattrs;
    633	int tmprc;
    634
    635	memset(&data, 0, sizeof(data));
    636	cifs_i = CIFS_I(inode);
    637	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
    638	data.Attributes = cpu_to_le32(dosattrs);
    639	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
    640	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
    641				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
    642				 CREATE_NOT_FILE, ACL_NO_MODE,
    643				 &data, SMB2_OP_SET_INFO, cfile);
    644	if (tmprc == 0)
    645		cifs_i->cifsAttrs = dosattrs;
    646}
    647
    648int
    649smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
    650	   struct cifs_sb_info *cifs_sb)
    651{
    652	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
    653				CREATE_NOT_FILE, ACL_NO_MODE,
    654				NULL, SMB2_OP_RMDIR, NULL);
    655}
    656
    657int
    658smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
    659	    struct cifs_sb_info *cifs_sb)
    660{
    661	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
    662				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
    663				ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL);
    664}
    665
    666static int
    667smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
    668		   const char *from_name, const char *to_name,
    669		   struct cifs_sb_info *cifs_sb, __u32 access, int command,
    670		   struct cifsFileInfo *cfile)
    671{
    672	__le16 *smb2_to_name = NULL;
    673	int rc;
    674
    675	smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
    676	if (smb2_to_name == NULL) {
    677		rc = -ENOMEM;
    678		goto smb2_rename_path;
    679	}
    680	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
    681			      FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
    682			      command, cfile);
    683smb2_rename_path:
    684	kfree(smb2_to_name);
    685	return rc;
    686}
    687
    688int
    689smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
    690		 const char *from_name, const char *to_name,
    691		 struct cifs_sb_info *cifs_sb)
    692{
    693	struct cifsFileInfo *cfile;
    694
    695	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
    696
    697	return smb2_set_path_attr(xid, tcon, from_name, to_name,
    698				  cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
    699}
    700
    701int
    702smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
    703		     const char *from_name, const char *to_name,
    704		     struct cifs_sb_info *cifs_sb)
    705{
    706	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
    707				  FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
    708				  NULL);
    709}
    710
    711int
    712smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
    713		   const char *full_path, __u64 size,
    714		   struct cifs_sb_info *cifs_sb, bool set_alloc)
    715{
    716	__le64 eof = cpu_to_le64(size);
    717	struct cifsFileInfo *cfile;
    718
    719	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
    720	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
    721				FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
    722				&eof, SMB2_OP_SET_EOF, cfile);
    723}
    724
    725int
    726smb2_set_file_info(struct inode *inode, const char *full_path,
    727		   FILE_BASIC_INFO *buf, const unsigned int xid)
    728{
    729	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
    730	struct tcon_link *tlink;
    731	struct cifs_tcon *tcon;
    732	struct cifsFileInfo *cfile;
    733	int rc;
    734
    735	if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
    736	    (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
    737	    (buf->Attributes == 0))
    738		return 0; /* would be a no op, no sense sending this */
    739
    740	tlink = cifs_sb_tlink(cifs_sb);
    741	if (IS_ERR(tlink))
    742		return PTR_ERR(tlink);
    743	tcon = tlink_tcon(tlink);
    744
    745	cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
    746	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
    747			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
    748			      0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile);
    749	cifs_put_tlink(tlink);
    750	return rc;
    751}