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
file_create_options(struct dentry * dentry)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
reparse_buf_ptr(struct kvec * iov)41a158bb66SSteve French static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
42a158bb66SSteve French {
43a158bb66SSteve French struct reparse_data_buffer *buf;
44a158bb66SSteve French struct smb2_ioctl_rsp *io = iov->iov_base;
45a158bb66SSteve French u32 off, count, len;
46a158bb66SSteve French
47a158bb66SSteve French count = le32_to_cpu(io->OutputCount);
48a158bb66SSteve French off = le32_to_cpu(io->OutputOffset);
49a158bb66SSteve French if (check_add_overflow(off, count, &len) || len > iov->iov_len)
50a158bb66SSteve French return ERR_PTR(-EIO);
51a158bb66SSteve French
52a158bb66SSteve French buf = (struct reparse_data_buffer *)((u8 *)io + off);
53a158bb66SSteve French len = sizeof(*buf);
54a158bb66SSteve French if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
55a158bb66SSteve French return ERR_PTR(-EIO);
56a158bb66SSteve French return buf;
57a158bb66SSteve French }
58a158bb66SSteve French
59a90f37e3SSteve French /* Parse owner and group from SMB3.1.1 POSIX query info */
parse_posix_sids(struct cifs_open_info_data * data,struct kvec * rsp_iov)60a90f37e3SSteve French static int parse_posix_sids(struct cifs_open_info_data *data,
61a90f37e3SSteve French struct kvec *rsp_iov)
62a90f37e3SSteve French {
63a90f37e3SSteve French struct smb2_query_info_rsp *qi = rsp_iov->iov_base;
64a90f37e3SSteve French unsigned int out_len = le32_to_cpu(qi->OutputBufferLength);
65a90f37e3SSteve French unsigned int qi_len = sizeof(data->posix_fi);
66a90f37e3SSteve French int owner_len, group_len;
67a90f37e3SSteve French u8 *sidsbuf, *sidsbuf_end;
68a90f37e3SSteve French
69a90f37e3SSteve French if (out_len <= qi_len)
70a90f37e3SSteve French return -EINVAL;
71a90f37e3SSteve French
72a90f37e3SSteve French sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
73a90f37e3SSteve French sidsbuf_end = sidsbuf + out_len - qi_len;
74a90f37e3SSteve French
75a90f37e3SSteve French owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
76a90f37e3SSteve French if (owner_len == -1)
77a90f37e3SSteve French return -EINVAL;
78a90f37e3SSteve French
79a90f37e3SSteve French memcpy(&data->posix_owner, sidsbuf, owner_len);
80a90f37e3SSteve French group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end);
81a90f37e3SSteve French if (group_len == -1)
82a90f37e3SSteve French return -EINVAL;
83a90f37e3SSteve French
84a90f37e3SSteve French memcpy(&data->posix_group, sidsbuf + owner_len, group_len);
85a90f37e3SSteve French return 0;
86a90f37e3SSteve French }
87a90f37e3SSteve French
887449d736SSteve French struct wsl_query_ea {
897449d736SSteve French __le32 next;
907449d736SSteve French __u8 name_len;
917449d736SSteve French __u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];
927449d736SSteve French } __packed;
937449d736SSteve French
947449d736SSteve French #define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
957449d736SSteve French
967449d736SSteve French static const struct wsl_query_ea wsl_query_eas[] = {
977449d736SSteve French { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
987449d736SSteve French { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
997449d736SSteve French { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
1007449d736SSteve French { .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
1017449d736SSteve French };
1027449d736SSteve French
check_wsl_eas(struct kvec * rsp_iov)1037449d736SSteve French static int check_wsl_eas(struct kvec *rsp_iov)
1047449d736SSteve French {
1057449d736SSteve French struct smb2_file_full_ea_info *ea;
1067449d736SSteve French struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
1077449d736SSteve French unsigned long addr;
1087449d736SSteve French u32 outlen, next;
1097449d736SSteve French u16 vlen;
1107449d736SSteve French u8 nlen;
1117449d736SSteve French u8 *end;
1127449d736SSteve French
1137449d736SSteve French outlen = le32_to_cpu(rsp->OutputBufferLength);
1147449d736SSteve French if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
1157449d736SSteve French outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
1167449d736SSteve French return -EINVAL;
1177449d736SSteve French
1187449d736SSteve French ea = (void *)((u8 *)rsp_iov->iov_base +
1197449d736SSteve French le16_to_cpu(rsp->OutputBufferOffset));
1207449d736SSteve French end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
1217449d736SSteve French for (;;) {
1227449d736SSteve French if ((u8 *)ea > end - sizeof(*ea))
1237449d736SSteve French return -EINVAL;
1247449d736SSteve French
1257449d736SSteve French nlen = ea->ea_name_length;
1267449d736SSteve French vlen = le16_to_cpu(ea->ea_value_length);
1277449d736SSteve French if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
1287449d736SSteve French (u8 *)ea + nlen + 1 + vlen > end)
1297449d736SSteve French return -EINVAL;
1307449d736SSteve French
1317449d736SSteve French switch (vlen) {
1327449d736SSteve French case 4:
1337449d736SSteve French if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
1347449d736SSteve French strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
1357449d736SSteve French strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
1367449d736SSteve French return -EINVAL;
1377449d736SSteve French break;
1387449d736SSteve French case 8:
1397449d736SSteve French if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
1407449d736SSteve French return -EINVAL;
1417449d736SSteve French break;
1427449d736SSteve French case 0:
1437449d736SSteve French if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
1447449d736SSteve French !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
1457449d736SSteve French !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
1467449d736SSteve French !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
1477449d736SSteve French break;
1487449d736SSteve French fallthrough;
1497449d736SSteve French default:
1507449d736SSteve French return -EINVAL;
1517449d736SSteve French }
1527449d736SSteve French
1537449d736SSteve French next = le32_to_cpu(ea->next_entry_offset);
1547449d736SSteve French if (!next)
1557449d736SSteve French break;
1567449d736SSteve French if (!IS_ALIGNED(next, 4) ||
1577449d736SSteve French check_add_overflow((unsigned long)ea, next, &addr))
1587449d736SSteve French return -EINVAL;
1597449d736SSteve French ea = (void *)addr;
1607449d736SSteve French }
1617449d736SSteve French return 0;
1627449d736SSteve French }
1637449d736SSteve French
16438c8a9a5SSteve French /*
16538c8a9a5SSteve French * note: If cfile is passed, the reference to it is dropped here.
16638c8a9a5SSteve French * So make sure that you do not reuse cfile after return from this func.
16738c8a9a5SSteve French *
168c5f44a3dSPaulo Alcantara * If passing @out_iov and @out_buftype, ensure to make them both large enough
169c5f44a3dSPaulo Alcantara * (>= 3) to hold all compounded responses. Caller is also responsible for
170c5f44a3dSPaulo Alcantara * freeing them up with free_rsp_buf().
17138c8a9a5SSteve French */
smb2_compound_op(const unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const char * full_path,struct cifs_open_parms * oparms,struct kvec * in_iov,int * cmds,int num_cmds,struct cifsFileInfo * cfile,struct kvec * out_iov,int * out_buftype,struct dentry * dentry)17238c8a9a5SSteve French static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
17338c8a9a5SSteve French struct cifs_sb_info *cifs_sb, const char *full_path,
174831ffbd1SPaulo Alcantara struct cifs_open_parms *oparms, struct kvec *in_iov,
175886b7fb4SSteve French int *cmds, int num_cmds, struct cifsFileInfo *cfile,
176f93d145fSMeetakshi Setiya struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
17738c8a9a5SSteve French {
178a158bb66SSteve French
17938c8a9a5SSteve French struct smb2_query_info_rsp *qi_rsp = NULL;
1805ddcc9e9SPaulo Alcantara struct smb2_compound_vars *vars = NULL;
1815ddcc9e9SPaulo Alcantara __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
18238c8a9a5SSteve French struct cifs_open_info_data *idata;
1835ddcc9e9SPaulo Alcantara struct cifs_ses *ses = tcon->ses;
1845ddcc9e9SPaulo Alcantara struct reparse_data_buffer *rbuf;
1855ddcc9e9SPaulo Alcantara struct TCP_Server_Info *server;
1865ddcc9e9SPaulo Alcantara int resp_buftype[MAX_COMPOUND];
187433042a9SShyam Prasad N int retries = 0, cur_sleep = 1;
1885ddcc9e9SPaulo Alcantara __u8 delete_pending[8] = {1,};
1895ddcc9e9SPaulo Alcantara struct kvec *rsp_iov, *iov;
1905ddcc9e9SPaulo Alcantara struct inode *inode = NULL;
1915ddcc9e9SPaulo Alcantara __le16 *utf16_path = NULL;
1925ddcc9e9SPaulo Alcantara struct smb_rqst *rqst;
1935ddcc9e9SPaulo Alcantara unsigned int size[2];
1945ddcc9e9SPaulo Alcantara struct cifs_fid fid;
1955ddcc9e9SPaulo Alcantara int num_rqst = 0, i;
1965ddcc9e9SPaulo Alcantara unsigned int len;
1975ddcc9e9SPaulo Alcantara int tmp_rc, rc;
1985ddcc9e9SPaulo Alcantara int flags = 0;
1995ddcc9e9SPaulo Alcantara void *data[2];
200433042a9SShyam Prasad N
201433042a9SShyam Prasad N replay_again:
202433042a9SShyam Prasad N /* reinitialize for possible replay */
203433042a9SShyam Prasad N flags = 0;
204433042a9SShyam Prasad N oplock = SMB2_OPLOCK_LEVEL_NONE;
205433042a9SShyam Prasad N num_rqst = 0;
206433042a9SShyam Prasad N server = cifs_pick_channel(ses);
20738c8a9a5SSteve French
20838c8a9a5SSteve French vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
20938c8a9a5SSteve French if (vars == NULL)
21038c8a9a5SSteve French return -ENOMEM;
21138c8a9a5SSteve French rqst = &vars->rqst[0];
21238c8a9a5SSteve French rsp_iov = &vars->rsp_iov[0];
21338c8a9a5SSteve French
21438c8a9a5SSteve French if (smb3_encryption_required(tcon))
21538c8a9a5SSteve French flags |= CIFS_TRANSFORM_REQ;
21638c8a9a5SSteve French
217886b7fb4SSteve French for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
218886b7fb4SSteve French resp_buftype[i] = CIFS_NO_BUFFER;
21938c8a9a5SSteve French
22038c8a9a5SSteve French /* We already have a handle so we can skip the open */
22138c8a9a5SSteve French if (cfile)
22238c8a9a5SSteve French goto after_open;
22338c8a9a5SSteve French
22438c8a9a5SSteve French /* Open */
22538c8a9a5SSteve French utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
22638c8a9a5SSteve French if (!utf16_path) {
22738c8a9a5SSteve French rc = -ENOMEM;
22838c8a9a5SSteve French goto finished;
22938c8a9a5SSteve French }
23038c8a9a5SSteve French
231f93d145fSMeetakshi Setiya /* if there is an existing lease, reuse it */
2321e60bc0eSMeetakshi Setiya
2331e60bc0eSMeetakshi Setiya /*
2341e60bc0eSMeetakshi Setiya * note: files with hardlinks cause unexpected behaviour. As per MS-SMB2,
2351e60bc0eSMeetakshi Setiya * lease keys are associated with the filepath. We are maintaining lease keys
2361e60bc0eSMeetakshi Setiya * with the inode on the client. If the file has hardlinks, it is possible
2371e60bc0eSMeetakshi Setiya * that the lease for a file be reused for an operation on its hardlink or
2381e60bc0eSMeetakshi Setiya * vice versa.
2391e60bc0eSMeetakshi Setiya * As a workaround, send request using an existing lease key and if the server
2401e60bc0eSMeetakshi Setiya * returns STATUS_INVALID_PARAMETER, which maps to EINVAL, send the request
2411e60bc0eSMeetakshi Setiya * again without the lease.
2421e60bc0eSMeetakshi Setiya */
243f93d145fSMeetakshi Setiya if (dentry) {
244f93d145fSMeetakshi Setiya inode = d_inode(dentry);
245f93d145fSMeetakshi Setiya if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
246f93d145fSMeetakshi Setiya oplock = SMB2_OPLOCK_LEVEL_LEASE;
247f93d145fSMeetakshi Setiya server->ops->get_lease_key(inode, &fid);
248f93d145fSMeetakshi Setiya }
249f93d145fSMeetakshi Setiya }
250f93d145fSMeetakshi Setiya
251831ffbd1SPaulo Alcantara vars->oparms = *oparms;
252831ffbd1SPaulo Alcantara vars->oparms.fid = &fid;
25338c8a9a5SSteve French
25438c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->open_iov[0];
25538c8a9a5SSteve French rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
25638c8a9a5SSteve French rc = SMB2_open_init(tcon, server,
25738c8a9a5SSteve French &rqst[num_rqst], &oplock, &vars->oparms,
25838c8a9a5SSteve French utf16_path);
25938c8a9a5SSteve French kfree(utf16_path);
26038c8a9a5SSteve French if (rc)
26138c8a9a5SSteve French goto finished;
26238c8a9a5SSteve French
26338c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
26438c8a9a5SSteve French after_open:
26538c8a9a5SSteve French num_rqst++;
26638c8a9a5SSteve French rc = 0;
26738c8a9a5SSteve French
268886b7fb4SSteve French for (i = 0; i < num_cmds; i++) {
26938c8a9a5SSteve French /* Operation */
270886b7fb4SSteve French switch (cmds[i]) {
27138c8a9a5SSteve French case SMB2_OP_QUERY_INFO:
272f4e5ceb6SPaulo Alcantara rqst[num_rqst].rq_iov = &vars->qi_iov;
27338c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
27438c8a9a5SSteve French
275886b7fb4SSteve French if (cfile) {
27638c8a9a5SSteve French rc = SMB2_query_info_init(tcon, server,
27738c8a9a5SSteve French &rqst[num_rqst],
27838c8a9a5SSteve French cfile->fid.persistent_fid,
27938c8a9a5SSteve French cfile->fid.volatile_fid,
28038c8a9a5SSteve French FILE_ALL_INFORMATION,
28138c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
28238c8a9a5SSteve French sizeof(struct smb2_file_all_info) +
28338c8a9a5SSteve French PATH_MAX * 2, 0, NULL);
284886b7fb4SSteve French } else {
28538c8a9a5SSteve French rc = SMB2_query_info_init(tcon, server,
28638c8a9a5SSteve French &rqst[num_rqst],
28738c8a9a5SSteve French COMPOUND_FID,
28838c8a9a5SSteve French COMPOUND_FID,
28938c8a9a5SSteve French FILE_ALL_INFORMATION,
29038c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
29138c8a9a5SSteve French sizeof(struct smb2_file_all_info) +
29238c8a9a5SSteve French PATH_MAX * 2, 0, NULL);
29384c597f7SPaulo Alcantara }
29484c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
29538c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
29638c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
29784c597f7SPaulo Alcantara } else if (rc) {
29838c8a9a5SSteve French goto finished;
29984c597f7SPaulo Alcantara }
30038c8a9a5SSteve French num_rqst++;
301*7a5f926bSRuben Devos trace_smb3_query_info_compound_enter(xid, tcon->tid,
302*7a5f926bSRuben Devos ses->Suid, full_path);
30338c8a9a5SSteve French break;
30438c8a9a5SSteve French case SMB2_OP_POSIX_QUERY_INFO:
305f4e5ceb6SPaulo Alcantara rqst[num_rqst].rq_iov = &vars->qi_iov;
30638c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
30738c8a9a5SSteve French
308886b7fb4SSteve French if (cfile) {
309886b7fb4SSteve French /* TBD: fix following to allow for longer SIDs */
31038c8a9a5SSteve French rc = SMB2_query_info_init(tcon, server,
31138c8a9a5SSteve French &rqst[num_rqst],
31238c8a9a5SSteve French cfile->fid.persistent_fid,
31338c8a9a5SSteve French cfile->fid.volatile_fid,
31438c8a9a5SSteve French SMB_FIND_FILE_POSIX_INFO,
31538c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
316886b7fb4SSteve French sizeof(struct smb311_posix_qinfo *) +
317886b7fb4SSteve French (PATH_MAX * 2) +
31846c22d37SChenXiaoSong (sizeof(struct smb_sid) * 2), 0, NULL);
319886b7fb4SSteve French } else {
32038c8a9a5SSteve French rc = SMB2_query_info_init(tcon, server,
32138c8a9a5SSteve French &rqst[num_rqst],
32238c8a9a5SSteve French COMPOUND_FID,
32338c8a9a5SSteve French COMPOUND_FID,
32438c8a9a5SSteve French SMB_FIND_FILE_POSIX_INFO,
32538c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
326886b7fb4SSteve French sizeof(struct smb311_posix_qinfo *) +
327886b7fb4SSteve French (PATH_MAX * 2) +
32846c22d37SChenXiaoSong (sizeof(struct smb_sid) * 2), 0, NULL);
32984c597f7SPaulo Alcantara }
33084c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
33138c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
33238c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
33384c597f7SPaulo Alcantara } else if (rc) {
33438c8a9a5SSteve French goto finished;
33584c597f7SPaulo Alcantara }
33638c8a9a5SSteve French num_rqst++;
337*7a5f926bSRuben Devos trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
338*7a5f926bSRuben Devos ses->Suid, full_path);
33938c8a9a5SSteve French break;
34038c8a9a5SSteve French case SMB2_OP_DELETE:
341*7a5f926bSRuben Devos trace_smb3_delete_enter(xid, tcon->tid, ses->Suid, full_path);
34238c8a9a5SSteve French break;
34338c8a9a5SSteve French case SMB2_OP_MKDIR:
34438c8a9a5SSteve French /*
34538c8a9a5SSteve French * Directories are created through parameters in the
34638c8a9a5SSteve French * SMB2_open() call.
34738c8a9a5SSteve French */
348*7a5f926bSRuben Devos trace_smb3_mkdir_enter(xid, tcon->tid, ses->Suid, full_path);
34938c8a9a5SSteve French break;
35038c8a9a5SSteve French case SMB2_OP_RMDIR:
35138c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->si_iov[0];
35238c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
35338c8a9a5SSteve French
35438c8a9a5SSteve French size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
35538c8a9a5SSteve French data[0] = &delete_pending[0];
35638c8a9a5SSteve French
35738c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
35838c8a9a5SSteve French &rqst[num_rqst], COMPOUND_FID,
35938c8a9a5SSteve French COMPOUND_FID, current->tgid,
36038c8a9a5SSteve French FILE_DISPOSITION_INFORMATION,
36138c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
36238c8a9a5SSteve French if (rc)
36338c8a9a5SSteve French goto finished;
36438c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
36538c8a9a5SSteve French smb2_set_related(&rqst[num_rqst++]);
366*7a5f926bSRuben Devos trace_smb3_rmdir_enter(xid, tcon->tid, ses->Suid, full_path);
36738c8a9a5SSteve French break;
36838c8a9a5SSteve French case SMB2_OP_SET_EOF:
36938c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->si_iov[0];
37038c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
37138c8a9a5SSteve French
372886b7fb4SSteve French size[0] = in_iov[i].iov_len;
373886b7fb4SSteve French data[0] = in_iov[i].iov_base;
37438c8a9a5SSteve French
37538c8a9a5SSteve French if (cfile) {
37638c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
37738c8a9a5SSteve French &rqst[num_rqst],
37838c8a9a5SSteve French cfile->fid.persistent_fid,
37938c8a9a5SSteve French cfile->fid.volatile_fid,
38038c8a9a5SSteve French current->tgid,
38138c8a9a5SSteve French FILE_END_OF_FILE_INFORMATION,
38238c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
38338c8a9a5SSteve French data, size);
38438c8a9a5SSteve French } else {
38538c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
38638c8a9a5SSteve French &rqst[num_rqst],
38738c8a9a5SSteve French COMPOUND_FID,
38838c8a9a5SSteve French COMPOUND_FID,
38938c8a9a5SSteve French current->tgid,
39038c8a9a5SSteve French FILE_END_OF_FILE_INFORMATION,
39138c8a9a5SSteve French SMB2_O_INFO_FILE, 0,
39238c8a9a5SSteve French data, size);
39384c597f7SPaulo Alcantara }
39484c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
39538c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
39638c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
39784c597f7SPaulo Alcantara } else if (rc) {
39838c8a9a5SSteve French goto finished;
39984c597f7SPaulo Alcantara }
40038c8a9a5SSteve French num_rqst++;
401*7a5f926bSRuben Devos trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
40238c8a9a5SSteve French break;
40338c8a9a5SSteve French case SMB2_OP_SET_INFO:
40438c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->si_iov[0];
40538c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
40638c8a9a5SSteve French
407886b7fb4SSteve French size[0] = in_iov[i].iov_len;
408886b7fb4SSteve French data[0] = in_iov[i].iov_base;
40938c8a9a5SSteve French
410886b7fb4SSteve French if (cfile) {
41138c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
41238c8a9a5SSteve French &rqst[num_rqst],
41338c8a9a5SSteve French cfile->fid.persistent_fid,
41438c8a9a5SSteve French cfile->fid.volatile_fid, current->tgid,
41538c8a9a5SSteve French FILE_BASIC_INFORMATION,
41638c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
417886b7fb4SSteve French } else {
41838c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
41938c8a9a5SSteve French &rqst[num_rqst],
42038c8a9a5SSteve French COMPOUND_FID,
42138c8a9a5SSteve French COMPOUND_FID, current->tgid,
42238c8a9a5SSteve French FILE_BASIC_INFORMATION,
42338c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
42484c597f7SPaulo Alcantara }
42584c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
42638c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
42738c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
42884c597f7SPaulo Alcantara } else if (rc) {
42938c8a9a5SSteve French goto finished;
43084c597f7SPaulo Alcantara }
43138c8a9a5SSteve French num_rqst++;
432*7a5f926bSRuben Devos trace_smb3_set_info_compound_enter(xid, tcon->tid,
433*7a5f926bSRuben Devos ses->Suid, full_path);
43438c8a9a5SSteve French break;
43538c8a9a5SSteve French case SMB2_OP_RENAME:
43638c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->si_iov[0];
43738c8a9a5SSteve French rqst[num_rqst].rq_nvec = 2;
43838c8a9a5SSteve French
439886b7fb4SSteve French len = in_iov[i].iov_len;
44038c8a9a5SSteve French
44138c8a9a5SSteve French vars->rename_info.ReplaceIfExists = 1;
44238c8a9a5SSteve French vars->rename_info.RootDirectory = 0;
44338c8a9a5SSteve French vars->rename_info.FileNameLength = cpu_to_le32(len);
44438c8a9a5SSteve French
44538c8a9a5SSteve French size[0] = sizeof(struct smb2_file_rename_info);
44638c8a9a5SSteve French data[0] = &vars->rename_info;
44738c8a9a5SSteve French
44838c8a9a5SSteve French size[1] = len + 2 /* null */;
449886b7fb4SSteve French data[1] = in_iov[i].iov_base;
45038c8a9a5SSteve French
451886b7fb4SSteve French if (cfile) {
45238c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
45338c8a9a5SSteve French &rqst[num_rqst],
45438c8a9a5SSteve French cfile->fid.persistent_fid,
45538c8a9a5SSteve French cfile->fid.volatile_fid,
45638c8a9a5SSteve French current->tgid, FILE_RENAME_INFORMATION,
45738c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
458886b7fb4SSteve French } else {
45938c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
46038c8a9a5SSteve French &rqst[num_rqst],
46138c8a9a5SSteve French COMPOUND_FID, COMPOUND_FID,
46238c8a9a5SSteve French current->tgid, FILE_RENAME_INFORMATION,
46338c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
46484c597f7SPaulo Alcantara }
46584c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
46638c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
46738c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
46884c597f7SPaulo Alcantara } else if (rc) {
46938c8a9a5SSteve French goto finished;
47084c597f7SPaulo Alcantara }
47138c8a9a5SSteve French num_rqst++;
472*7a5f926bSRuben Devos trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
47338c8a9a5SSteve French break;
47438c8a9a5SSteve French case SMB2_OP_HARDLINK:
47538c8a9a5SSteve French rqst[num_rqst].rq_iov = &vars->si_iov[0];
47638c8a9a5SSteve French rqst[num_rqst].rq_nvec = 2;
47738c8a9a5SSteve French
478886b7fb4SSteve French len = in_iov[i].iov_len;
47938c8a9a5SSteve French
48038c8a9a5SSteve French vars->link_info.ReplaceIfExists = 0;
48138c8a9a5SSteve French vars->link_info.RootDirectory = 0;
48238c8a9a5SSteve French vars->link_info.FileNameLength = cpu_to_le32(len);
48338c8a9a5SSteve French
48438c8a9a5SSteve French size[0] = sizeof(struct smb2_file_link_info);
48538c8a9a5SSteve French data[0] = &vars->link_info;
48638c8a9a5SSteve French
48738c8a9a5SSteve French size[1] = len + 2 /* null */;
488886b7fb4SSteve French data[1] = in_iov[i].iov_base;
48938c8a9a5SSteve French
49038c8a9a5SSteve French rc = SMB2_set_info_init(tcon, server,
49138c8a9a5SSteve French &rqst[num_rqst], COMPOUND_FID,
49238c8a9a5SSteve French COMPOUND_FID, current->tgid,
49338c8a9a5SSteve French FILE_LINK_INFORMATION,
49438c8a9a5SSteve French SMB2_O_INFO_FILE, 0, data, size);
49538c8a9a5SSteve French if (rc)
49638c8a9a5SSteve French goto finished;
49738c8a9a5SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
49838c8a9a5SSteve French smb2_set_related(&rqst[num_rqst++]);
499*7a5f926bSRuben Devos trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
50038c8a9a5SSteve French break;
50102bcf865SSteve French case SMB2_OP_SET_REPARSE:
50202bcf865SSteve French rqst[num_rqst].rq_iov = vars->io_iov;
50302bcf865SSteve French rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
50402bcf865SSteve French
50584c597f7SPaulo Alcantara if (cfile) {
50684c597f7SPaulo Alcantara rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
50784c597f7SPaulo Alcantara cfile->fid.persistent_fid,
50884c597f7SPaulo Alcantara cfile->fid.volatile_fid,
50984c597f7SPaulo Alcantara FSCTL_SET_REPARSE_POINT,
51084c597f7SPaulo Alcantara in_iov[i].iov_base,
51184c597f7SPaulo Alcantara in_iov[i].iov_len, 0);
51284c597f7SPaulo Alcantara } else {
51302bcf865SSteve French rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
51402bcf865SSteve French COMPOUND_FID, COMPOUND_FID,
51502bcf865SSteve French FSCTL_SET_REPARSE_POINT,
51602bcf865SSteve French in_iov[i].iov_base,
51702bcf865SSteve French in_iov[i].iov_len, 0);
51884c597f7SPaulo Alcantara }
51984c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
52002bcf865SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
52184c597f7SPaulo Alcantara smb2_set_related(&rqst[num_rqst]);
52284c597f7SPaulo Alcantara } else if (rc) {
52384c597f7SPaulo Alcantara goto finished;
52484c597f7SPaulo Alcantara }
52584c597f7SPaulo Alcantara num_rqst++;
526*7a5f926bSRuben Devos trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
527*7a5f926bSRuben Devos ses->Suid, full_path);
52802bcf865SSteve French break;
529a158bb66SSteve French case SMB2_OP_GET_REPARSE:
530a158bb66SSteve French rqst[num_rqst].rq_iov = vars->io_iov;
531a158bb66SSteve French rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
532a158bb66SSteve French
53384c597f7SPaulo Alcantara if (cfile) {
53484c597f7SPaulo Alcantara rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
53584c597f7SPaulo Alcantara cfile->fid.persistent_fid,
53684c597f7SPaulo Alcantara cfile->fid.volatile_fid,
53784c597f7SPaulo Alcantara FSCTL_GET_REPARSE_POINT,
53884c597f7SPaulo Alcantara NULL, 0, CIFSMaxBufSize);
53984c597f7SPaulo Alcantara } else {
540a158bb66SSteve French rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
541a158bb66SSteve French COMPOUND_FID, COMPOUND_FID,
542a158bb66SSteve French FSCTL_GET_REPARSE_POINT,
543a158bb66SSteve French NULL, 0, CIFSMaxBufSize);
54484c597f7SPaulo Alcantara }
54584c597f7SPaulo Alcantara if (!rc && (!cfile || num_rqst > 1)) {
546a158bb66SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
54784c597f7SPaulo Alcantara smb2_set_related(&rqst[num_rqst]);
54884c597f7SPaulo Alcantara } else if (rc) {
54984c597f7SPaulo Alcantara goto finished;
55084c597f7SPaulo Alcantara }
55184c597f7SPaulo Alcantara num_rqst++;
552*7a5f926bSRuben Devos trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
553*7a5f926bSRuben Devos ses->Suid, full_path);
554a158bb66SSteve French break;
5557449d736SSteve French case SMB2_OP_QUERY_WSL_EA:
5567449d736SSteve French rqst[num_rqst].rq_iov = &vars->ea_iov;
5577449d736SSteve French rqst[num_rqst].rq_nvec = 1;
5587449d736SSteve French
5597449d736SSteve French if (cfile) {
5607449d736SSteve French rc = SMB2_query_info_init(tcon, server,
5617449d736SSteve French &rqst[num_rqst],
5627449d736SSteve French cfile->fid.persistent_fid,
5637449d736SSteve French cfile->fid.volatile_fid,
5647449d736SSteve French FILE_FULL_EA_INFORMATION,
5657449d736SSteve French SMB2_O_INFO_FILE, 0,
5667449d736SSteve French SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
5677449d736SSteve French sizeof(wsl_query_eas),
5687449d736SSteve French (void *)wsl_query_eas);
5697449d736SSteve French } else {
5707449d736SSteve French rc = SMB2_query_info_init(tcon, server,
5717449d736SSteve French &rqst[num_rqst],
5727449d736SSteve French COMPOUND_FID,
5737449d736SSteve French COMPOUND_FID,
5747449d736SSteve French FILE_FULL_EA_INFORMATION,
5757449d736SSteve French SMB2_O_INFO_FILE, 0,
5767449d736SSteve French SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
5777449d736SSteve French sizeof(wsl_query_eas),
5787449d736SSteve French (void *)wsl_query_eas);
5797449d736SSteve French }
5807449d736SSteve French if (!rc && (!cfile || num_rqst > 1)) {
5817449d736SSteve French smb2_set_next_command(tcon, &rqst[num_rqst]);
5827449d736SSteve French smb2_set_related(&rqst[num_rqst]);
5837449d736SSteve French } else if (rc) {
5847449d736SSteve French goto finished;
5857449d736SSteve French }
5867449d736SSteve French num_rqst++;
5877449d736SSteve French break;
58838c8a9a5SSteve French default:
58938c8a9a5SSteve French cifs_dbg(VFS, "Invalid command\n");
59038c8a9a5SSteve French rc = -EINVAL;
59138c8a9a5SSteve French }
592886b7fb4SSteve French }
59338c8a9a5SSteve French if (rc)
59438c8a9a5SSteve French goto finished;
59538c8a9a5SSteve French
59638c8a9a5SSteve French /* We already have a handle so we can skip the close */
59738c8a9a5SSteve French if (cfile)
59838c8a9a5SSteve French goto after_close;
59938c8a9a5SSteve French /* Close */
60038c8a9a5SSteve French flags |= CIFS_CP_CREATE_CLOSE_OP;
601f4e5ceb6SPaulo Alcantara rqst[num_rqst].rq_iov = &vars->close_iov;
60238c8a9a5SSteve French rqst[num_rqst].rq_nvec = 1;
60338c8a9a5SSteve French rc = SMB2_close_init(tcon, server,
60438c8a9a5SSteve French &rqst[num_rqst], COMPOUND_FID,
60538c8a9a5SSteve French COMPOUND_FID, false);
60638c8a9a5SSteve French smb2_set_related(&rqst[num_rqst]);
60738c8a9a5SSteve French if (rc)
60838c8a9a5SSteve French goto finished;
60938c8a9a5SSteve French after_close:
61038c8a9a5SSteve French num_rqst++;
61138c8a9a5SSteve French
61238c8a9a5SSteve French if (cfile) {
613433042a9SShyam Prasad N if (retries)
614433042a9SShyam Prasad N for (i = 1; i < num_rqst - 2; i++)
615433042a9SShyam Prasad N smb2_set_replay(server, &rqst[i]);
616433042a9SShyam Prasad N
61738c8a9a5SSteve French rc = compound_send_recv(xid, ses, server,
61838c8a9a5SSteve French flags, num_rqst - 2,
61938c8a9a5SSteve French &rqst[1], &resp_buftype[1],
62038c8a9a5SSteve French &rsp_iov[1]);
621433042a9SShyam Prasad N } else {
622433042a9SShyam Prasad N if (retries)
623433042a9SShyam Prasad N for (i = 0; i < num_rqst; i++)
624433042a9SShyam Prasad N smb2_set_replay(server, &rqst[i]);
625433042a9SShyam Prasad N
62638c8a9a5SSteve French rc = compound_send_recv(xid, ses, server,
62738c8a9a5SSteve French flags, num_rqst,
62838c8a9a5SSteve French rqst, resp_buftype,
62938c8a9a5SSteve French rsp_iov);
630433042a9SShyam Prasad N }
63138c8a9a5SSteve French
63238c8a9a5SSteve French finished:
633886b7fb4SSteve French num_rqst = 0;
634886b7fb4SSteve French SMB2_open_free(&rqst[num_rqst++]);
63538c8a9a5SSteve French if (rc == -EREMCHG) {
63638c8a9a5SSteve French pr_warn_once("server share %s deleted\n", tcon->tree_name);
63738c8a9a5SSteve French tcon->need_reconnect = true;
63838c8a9a5SSteve French }
63938c8a9a5SSteve French
6405ddcc9e9SPaulo Alcantara tmp_rc = rc;
641886b7fb4SSteve French for (i = 0; i < num_cmds; i++) {
6425ddcc9e9SPaulo Alcantara char *buf = rsp_iov[i + i].iov_base;
6435ddcc9e9SPaulo Alcantara
6445ddcc9e9SPaulo Alcantara if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
6455ddcc9e9SPaulo Alcantara rc = server->ops->map_error(buf, false);
6465ddcc9e9SPaulo Alcantara else
6475ddcc9e9SPaulo Alcantara rc = tmp_rc;
648886b7fb4SSteve French switch (cmds[i]) {
64938c8a9a5SSteve French case SMB2_OP_QUERY_INFO:
650886b7fb4SSteve French idata = in_iov[i].iov_base;
65138c8a9a5SSteve French if (rc == 0 && cfile && cfile->symlink_target) {
65238c8a9a5SSteve French idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
65338c8a9a5SSteve French if (!idata->symlink_target)
65438c8a9a5SSteve French rc = -ENOMEM;
65538c8a9a5SSteve French }
65638c8a9a5SSteve French if (rc == 0) {
65738c8a9a5SSteve French qi_rsp = (struct smb2_query_info_rsp *)
658886b7fb4SSteve French rsp_iov[i + 1].iov_base;
65938c8a9a5SSteve French rc = smb2_validate_and_copy_iov(
66038c8a9a5SSteve French le16_to_cpu(qi_rsp->OutputBufferOffset),
66138c8a9a5SSteve French le32_to_cpu(qi_rsp->OutputBufferLength),
662886b7fb4SSteve French &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
66338c8a9a5SSteve French }
664886b7fb4SSteve French SMB2_query_info_free(&rqst[num_rqst++]);
66538c8a9a5SSteve French if (rc)
666*7a5f926bSRuben Devos trace_smb3_query_info_compound_err(xid, tcon->tid,
667*7a5f926bSRuben Devos ses->Suid, rc);
66838c8a9a5SSteve French else
669*7a5f926bSRuben Devos trace_smb3_query_info_compound_done(xid, tcon->tid,
670*7a5f926bSRuben Devos ses->Suid);
67138c8a9a5SSteve French break;
67238c8a9a5SSteve French case SMB2_OP_POSIX_QUERY_INFO:
673886b7fb4SSteve French idata = in_iov[i].iov_base;
67438c8a9a5SSteve French if (rc == 0 && cfile && cfile->symlink_target) {
67538c8a9a5SSteve French idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
67638c8a9a5SSteve French if (!idata->symlink_target)
67738c8a9a5SSteve French rc = -ENOMEM;
67838c8a9a5SSteve French }
67938c8a9a5SSteve French if (rc == 0) {
68038c8a9a5SSteve French qi_rsp = (struct smb2_query_info_rsp *)
681886b7fb4SSteve French rsp_iov[i + 1].iov_base;
68238c8a9a5SSteve French rc = smb2_validate_and_copy_iov(
68338c8a9a5SSteve French le16_to_cpu(qi_rsp->OutputBufferOffset),
68438c8a9a5SSteve French le32_to_cpu(qi_rsp->OutputBufferLength),
685886b7fb4SSteve French &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
68638c8a9a5SSteve French (char *)&idata->posix_fi);
68738c8a9a5SSteve French }
688a90f37e3SSteve French if (rc == 0)
689a90f37e3SSteve French rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
69038c8a9a5SSteve French
691886b7fb4SSteve French SMB2_query_info_free(&rqst[num_rqst++]);
69238c8a9a5SSteve French if (rc)
693*7a5f926bSRuben Devos trace_smb3_posix_query_info_compound_err(xid, tcon->tid,
694*7a5f926bSRuben Devos ses->Suid, rc);
69538c8a9a5SSteve French else
696*7a5f926bSRuben Devos trace_smb3_posix_query_info_compound_done(xid, tcon->tid,
697*7a5f926bSRuben Devos ses->Suid);
69838c8a9a5SSteve French break;
69938c8a9a5SSteve French case SMB2_OP_DELETE:
70038c8a9a5SSteve French if (rc)
701*7a5f926bSRuben Devos trace_smb3_delete_err(xid, tcon->tid, ses->Suid, rc);
702b6e27f7fSSteve French else {
703b6e27f7fSSteve French /*
704b6e27f7fSSteve French * If dentry (hence, inode) is NULL, lease break is going to
705b6e27f7fSSteve French * take care of degrading leases on handles for deleted files.
706b6e27f7fSSteve French */
707b6e27f7fSSteve French if (inode)
708b6e27f7fSSteve French cifs_mark_open_handles_for_deleted_file(inode, full_path);
709*7a5f926bSRuben Devos trace_smb3_delete_done(xid, tcon->tid, ses->Suid);
710b6e27f7fSSteve French }
71138c8a9a5SSteve French break;
71238c8a9a5SSteve French case SMB2_OP_MKDIR:
71338c8a9a5SSteve French if (rc)
714*7a5f926bSRuben Devos trace_smb3_mkdir_err(xid, tcon->tid, ses->Suid, rc);
71538c8a9a5SSteve French else
716*7a5f926bSRuben Devos trace_smb3_mkdir_done(xid, tcon->tid, ses->Suid);
71738c8a9a5SSteve French break;
71838c8a9a5SSteve French case SMB2_OP_HARDLINK:
71938c8a9a5SSteve French if (rc)
720*7a5f926bSRuben Devos trace_smb3_hardlink_err(xid, tcon->tid, ses->Suid, rc);
72138c8a9a5SSteve French else
722*7a5f926bSRuben Devos trace_smb3_hardlink_done(xid, tcon->tid, ses->Suid);
723886b7fb4SSteve French SMB2_set_info_free(&rqst[num_rqst++]);
72438c8a9a5SSteve French break;
72538c8a9a5SSteve French case SMB2_OP_RENAME:
72638c8a9a5SSteve French if (rc)
727*7a5f926bSRuben Devos trace_smb3_rename_err(xid, tcon->tid, ses->Suid, rc);
72838c8a9a5SSteve French else
729*7a5f926bSRuben Devos trace_smb3_rename_done(xid, tcon->tid, ses->Suid);
730886b7fb4SSteve French SMB2_set_info_free(&rqst[num_rqst++]);
73138c8a9a5SSteve French break;
73238c8a9a5SSteve French case SMB2_OP_RMDIR:
73338c8a9a5SSteve French if (rc)
734*7a5f926bSRuben Devos trace_smb3_rmdir_err(xid, tcon->tid, ses->Suid, rc);
73538c8a9a5SSteve French else
736*7a5f926bSRuben Devos trace_smb3_rmdir_done(xid, tcon->tid, ses->Suid);
737886b7fb4SSteve French SMB2_set_info_free(&rqst[num_rqst++]);
73838c8a9a5SSteve French break;
73938c8a9a5SSteve French case SMB2_OP_SET_EOF:
74038c8a9a5SSteve French if (rc)
741*7a5f926bSRuben Devos trace_smb3_set_eof_err(xid, tcon->tid, ses->Suid, rc);
74238c8a9a5SSteve French else
743*7a5f926bSRuben Devos trace_smb3_set_eof_done(xid, tcon->tid, ses->Suid);
744886b7fb4SSteve French SMB2_set_info_free(&rqst[num_rqst++]);
74538c8a9a5SSteve French break;
74638c8a9a5SSteve French case SMB2_OP_SET_INFO:
74738c8a9a5SSteve French if (rc)
748*7a5f926bSRuben Devos trace_smb3_set_info_compound_err(xid, tcon->tid,
749*7a5f926bSRuben Devos ses->Suid, rc);
75038c8a9a5SSteve French else
751*7a5f926bSRuben Devos trace_smb3_set_info_compound_done(xid, tcon->tid,
752*7a5f926bSRuben Devos ses->Suid);
753886b7fb4SSteve French SMB2_set_info_free(&rqst[num_rqst++]);
75438c8a9a5SSteve French break;
75502bcf865SSteve French case SMB2_OP_SET_REPARSE:
75602bcf865SSteve French if (rc) {
757*7a5f926bSRuben Devos trace_smb3_set_reparse_compound_err(xid, tcon->tid,
758*7a5f926bSRuben Devos ses->Suid, rc);
75902bcf865SSteve French } else {
760*7a5f926bSRuben Devos trace_smb3_set_reparse_compound_done(xid, tcon->tid,
761*7a5f926bSRuben Devos ses->Suid);
76202bcf865SSteve French }
76302bcf865SSteve French SMB2_ioctl_free(&rqst[num_rqst++]);
76402bcf865SSteve French break;
765a158bb66SSteve French case SMB2_OP_GET_REPARSE:
766a158bb66SSteve French if (!rc) {
767a158bb66SSteve French iov = &rsp_iov[i + 1];
768a158bb66SSteve French idata = in_iov[i].iov_base;
769a158bb66SSteve French idata->reparse.io.iov = *iov;
770a158bb66SSteve French idata->reparse.io.buftype = resp_buftype[i + 1];
771a158bb66SSteve French rbuf = reparse_buf_ptr(iov);
772a158bb66SSteve French if (IS_ERR(rbuf)) {
773a158bb66SSteve French rc = PTR_ERR(rbuf);
774*7a5f926bSRuben Devos trace_smb3_get_reparse_compound_err(xid, tcon->tid,
775*7a5f926bSRuben Devos ses->Suid, rc);
776a158bb66SSteve French } else {
777a158bb66SSteve French idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
778*7a5f926bSRuben Devos trace_smb3_get_reparse_compound_done(xid, tcon->tid,
779*7a5f926bSRuben Devos ses->Suid);
780a158bb66SSteve French }
781a158bb66SSteve French memset(iov, 0, sizeof(*iov));
782a158bb66SSteve French resp_buftype[i + 1] = CIFS_NO_BUFFER;
783a158bb66SSteve French } else {
784*7a5f926bSRuben Devos trace_smb3_get_reparse_compound_err(xid, tcon->tid,
785*7a5f926bSRuben Devos ses->Suid, rc);
786a158bb66SSteve French }
787a158bb66SSteve French SMB2_ioctl_free(&rqst[num_rqst++]);
788a158bb66SSteve French break;
7897449d736SSteve French case SMB2_OP_QUERY_WSL_EA:
7907449d736SSteve French if (!rc) {
7917449d736SSteve French idata = in_iov[i].iov_base;
7927449d736SSteve French qi_rsp = rsp_iov[i + 1].iov_base;
7937449d736SSteve French data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
7947449d736SSteve French size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
7957449d736SSteve French rc = check_wsl_eas(&rsp_iov[i + 1]);
7967449d736SSteve French if (!rc) {
7977449d736SSteve French memcpy(idata->wsl.eas, data[0], size[0]);
7987449d736SSteve French idata->wsl.eas_len = size[0];
7997449d736SSteve French }
8007449d736SSteve French }
8017449d736SSteve French if (!rc) {
802*7a5f926bSRuben Devos trace_smb3_query_wsl_ea_compound_done(xid, tcon->tid,
803*7a5f926bSRuben Devos ses->Suid);
8047449d736SSteve French } else {
805*7a5f926bSRuben Devos trace_smb3_query_wsl_ea_compound_err(xid, tcon->tid,
806*7a5f926bSRuben Devos ses->Suid, rc);
8077449d736SSteve French }
8087449d736SSteve French SMB2_query_info_free(&rqst[num_rqst++]);
8097449d736SSteve French break;
81038c8a9a5SSteve French }
811886b7fb4SSteve French }
812886b7fb4SSteve French SMB2_close_free(&rqst[num_rqst]);
8135ddcc9e9SPaulo Alcantara rc = tmp_rc;
81438c8a9a5SSteve French
815886b7fb4SSteve French num_cmds += 2;
816c5f44a3dSPaulo Alcantara if (out_iov && out_buftype) {
817886b7fb4SSteve French memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
818886b7fb4SSteve French memcpy(out_buftype, resp_buftype,
819886b7fb4SSteve French num_cmds * sizeof(*out_buftype));
82038c8a9a5SSteve French } else {
821886b7fb4SSteve French for (i = 0; i < num_cmds; i++)
822886b7fb4SSteve French free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
82338c8a9a5SSteve French }
824433042a9SShyam Prasad N num_cmds -= 2; /* correct num_cmds as there could be a retry */
82538c8a9a5SSteve French kfree(vars);
826433042a9SShyam Prasad N
827433042a9SShyam Prasad N if (is_replayable_error(rc) &&
828433042a9SShyam Prasad N smb2_should_replay(tcon, &retries, &cur_sleep))
829433042a9SShyam Prasad N goto replay_again;
830433042a9SShyam Prasad N
831433042a9SShyam Prasad N if (cfile)
832433042a9SShyam Prasad N cifsFileInfo_put(cfile);
833433042a9SShyam Prasad N
83438c8a9a5SSteve French return rc;
83538c8a9a5SSteve French }
83638c8a9a5SSteve French
parse_create_response(struct cifs_open_info_data * data,struct cifs_sb_info * cifs_sb,const char * full_path,const struct kvec * iov)8375f71ebc4SPaulo Alcantara static int parse_create_response(struct cifs_open_info_data *data,
8385f71ebc4SPaulo Alcantara struct cifs_sb_info *cifs_sb,
839430afd3eSPali Rohár const char *full_path,
8405f71ebc4SPaulo Alcantara const struct kvec *iov)
8415f71ebc4SPaulo Alcantara {
8425f71ebc4SPaulo Alcantara struct smb2_create_rsp *rsp = iov->iov_base;
8435f71ebc4SPaulo Alcantara bool reparse_point = false;
8445f71ebc4SPaulo Alcantara u32 tag = 0;
8455f71ebc4SPaulo Alcantara int rc = 0;
8465f71ebc4SPaulo Alcantara
8475f71ebc4SPaulo Alcantara switch (rsp->hdr.Status) {
8487fb77d9cSPaulo Alcantara case STATUS_IO_REPARSE_TAG_NOT_HANDLED:
8497fb77d9cSPaulo Alcantara reparse_point = true;
8507fb77d9cSPaulo Alcantara break;
8515f71ebc4SPaulo Alcantara case STATUS_STOPPED_ON_SYMLINK:
8525f71ebc4SPaulo Alcantara rc = smb2_parse_symlink_response(cifs_sb, iov,
853430afd3eSPali Rohár full_path,
8545f71ebc4SPaulo Alcantara &data->symlink_target);
8555f71ebc4SPaulo Alcantara if (rc)
8565f71ebc4SPaulo Alcantara return rc;
8575f71ebc4SPaulo Alcantara tag = IO_REPARSE_TAG_SYMLINK;
8585f71ebc4SPaulo Alcantara reparse_point = true;
8595f71ebc4SPaulo Alcantara break;
8605f71ebc4SPaulo Alcantara case STATUS_SUCCESS:
8615f71ebc4SPaulo Alcantara reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
8625f71ebc4SPaulo Alcantara break;
8635f71ebc4SPaulo Alcantara }
8645f71ebc4SPaulo Alcantara data->reparse_point = reparse_point;
865df32e887SPaulo Alcantara data->reparse.tag = tag;
8665f71ebc4SPaulo Alcantara return rc;
8675f71ebc4SPaulo Alcantara }
8685f71ebc4SPaulo Alcantara
8695ddcc9e9SPaulo Alcantara /* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
ea_unsupported(int * cmds,int num_cmds,struct kvec * out_iov,int * out_buftype)8705ddcc9e9SPaulo Alcantara static bool ea_unsupported(int *cmds, int num_cmds,
8715ddcc9e9SPaulo Alcantara struct kvec *out_iov, int *out_buftype)
8725ddcc9e9SPaulo Alcantara {
8735ddcc9e9SPaulo Alcantara int i;
8745ddcc9e9SPaulo Alcantara
8755ddcc9e9SPaulo Alcantara if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
8765ddcc9e9SPaulo Alcantara return false;
8775ddcc9e9SPaulo Alcantara
8785ddcc9e9SPaulo Alcantara for (i = 1; i < num_cmds - 1; i++) {
8795ddcc9e9SPaulo Alcantara struct smb2_hdr *hdr = out_iov[i].iov_base;
8805ddcc9e9SPaulo Alcantara
8815ddcc9e9SPaulo Alcantara if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
8825ddcc9e9SPaulo Alcantara hdr->Status != STATUS_SUCCESS)
8835ddcc9e9SPaulo Alcantara return false;
8845ddcc9e9SPaulo Alcantara }
8855ddcc9e9SPaulo Alcantara return true;
8865ddcc9e9SPaulo Alcantara }
8875ddcc9e9SPaulo Alcantara
free_rsp_iov(struct kvec * iovs,int * buftype,int count)8885ddcc9e9SPaulo Alcantara static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
8895ddcc9e9SPaulo Alcantara {
8905ddcc9e9SPaulo Alcantara int i;
8915ddcc9e9SPaulo Alcantara
8925ddcc9e9SPaulo Alcantara for (i = 0; i < count; i++) {
8935ddcc9e9SPaulo Alcantara free_rsp_buf(buftype[i], iovs[i].iov_base);
8945ddcc9e9SPaulo Alcantara memset(&iovs[i], 0, sizeof(*iovs));
8955ddcc9e9SPaulo Alcantara buftype[i] = CIFS_NO_BUFFER;
8965ddcc9e9SPaulo Alcantara }
8975ddcc9e9SPaulo Alcantara }
8985ddcc9e9SPaulo Alcantara
smb2_query_path_info(const unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const char * full_path,struct cifs_open_info_data * data)8998b4e285dSPaulo Alcantara int smb2_query_path_info(const unsigned int xid,
9008b4e285dSPaulo Alcantara struct cifs_tcon *tcon,
9018b4e285dSPaulo Alcantara struct cifs_sb_info *cifs_sb,
9028b4e285dSPaulo Alcantara const char *full_path,
9038b4e285dSPaulo Alcantara struct cifs_open_info_data *data)
90438c8a9a5SSteve French {
9055ddcc9e9SPaulo Alcantara struct kvec in_iov[3], out_iov[5] = {};
90638c8a9a5SSteve French struct cached_fid *cfid = NULL;
9075ddcc9e9SPaulo Alcantara struct cifs_open_parms oparms;
9085ddcc9e9SPaulo Alcantara struct cifsFileInfo *cfile;
9095ddcc9e9SPaulo Alcantara __u32 create_options = 0;
9105ddcc9e9SPaulo Alcantara int out_buftype[5] = {};
9115f71ebc4SPaulo Alcantara struct smb2_hdr *hdr;
9125ddcc9e9SPaulo Alcantara int num_cmds = 0;
9137449d736SSteve French int cmds[3];
91438c8a9a5SSteve French bool islink;
91538c8a9a5SSteve French int rc, rc2;
91638c8a9a5SSteve French
9178b4e285dSPaulo Alcantara data->adjust_tz = false;
9188b4e285dSPaulo Alcantara data->reparse_point = false;
91938c8a9a5SSteve French
920cfb8f73dSPaulo Alcantara /*
921cfb8f73dSPaulo Alcantara * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX.
922cfb8f73dSPaulo Alcantara * Create SMB2_query_posix_info worker function to do non-compounded
923cfb8f73dSPaulo Alcantara * query when we already have an open file handle for this. For now this
924cfb8f73dSPaulo Alcantara * is fast enough (always using the compounded version).
925cfb8f73dSPaulo Alcantara */
926cfb8f73dSPaulo Alcantara if (!tcon->posix_extensions) {
927cfb8f73dSPaulo Alcantara if (*full_path) {
92838c8a9a5SSteve French rc = -ENOENT;
929cfb8f73dSPaulo Alcantara } else {
930cfb8f73dSPaulo Alcantara rc = open_cached_dir(xid, tcon, full_path,
931cfb8f73dSPaulo Alcantara cifs_sb, false, &cfid);
932cfb8f73dSPaulo Alcantara }
93338c8a9a5SSteve French /* If it is a root and its handle is cached then use it */
93438c8a9a5SSteve French if (!rc) {
93538c8a9a5SSteve French if (cfid->file_all_info_is_valid) {
936cfb8f73dSPaulo Alcantara memcpy(&data->fi, &cfid->file_all_info,
937cfb8f73dSPaulo Alcantara sizeof(data->fi));
93838c8a9a5SSteve French } else {
939cfb8f73dSPaulo Alcantara rc = SMB2_query_info(xid, tcon,
940cfb8f73dSPaulo Alcantara cfid->fid.persistent_fid,
941cfb8f73dSPaulo Alcantara cfid->fid.volatile_fid,
942cfb8f73dSPaulo Alcantara &data->fi);
94338c8a9a5SSteve French }
94438c8a9a5SSteve French close_cached_dir(cfid);
94538c8a9a5SSteve French return rc;
94638c8a9a5SSteve French }
9477449d736SSteve French cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
948cfb8f73dSPaulo Alcantara } else {
9497449d736SSteve French cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
950cfb8f73dSPaulo Alcantara }
95138c8a9a5SSteve French
952a158bb66SSteve French in_iov[0].iov_base = data;
953a158bb66SSteve French in_iov[0].iov_len = sizeof(*data);
954a158bb66SSteve French in_iov[1] = in_iov[0];
9557449d736SSteve French in_iov[2] = in_iov[0];
956886b7fb4SSteve French
95738c8a9a5SSteve French cifs_get_readable_path(tcon, full_path, &cfile);
958831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
959831ffbd1SPaulo Alcantara FILE_OPEN, create_options, ACL_NO_MODE);
960886b7fb4SSteve French rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
9617449d736SSteve French &oparms, in_iov, cmds, num_cmds,
9627449d736SSteve French cfile, out_iov, out_buftype, NULL);
9635f71ebc4SPaulo Alcantara hdr = out_iov[0].iov_base;
9645f71ebc4SPaulo Alcantara /*
9655f71ebc4SPaulo Alcantara * If first iov is unset, then SMB session was dropped or we've got a
9665f71ebc4SPaulo Alcantara * cached open file (@cfile).
9675f71ebc4SPaulo Alcantara */
9685f71ebc4SPaulo Alcantara if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
96938c8a9a5SSteve French goto out;
97038c8a9a5SSteve French
9715f71ebc4SPaulo Alcantara switch (rc) {
9725f71ebc4SPaulo Alcantara case 0:
973430afd3eSPali Rohár rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
9740397a48cSPaulo Alcantara break;
9755f71ebc4SPaulo Alcantara case -EOPNOTSUPP:
976cfb8f73dSPaulo Alcantara /*
977cfb8f73dSPaulo Alcantara * BB TODO: When support for special files added to Samba
978cfb8f73dSPaulo Alcantara * re-verify this path.
979cfb8f73dSPaulo Alcantara */
980430afd3eSPali Rohár rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
9815f71ebc4SPaulo Alcantara if (rc || !data->reparse_point)
9825f71ebc4SPaulo Alcantara goto out;
9835f71ebc4SPaulo Alcantara
9847449d736SSteve French /*
9857449d736SSteve French * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
9867449d736SSteve French * response.
9877449d736SSteve French */
9887449d736SSteve French if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
9897449d736SSteve French cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
9905ddcc9e9SPaulo Alcantara if (!tcon->posix_extensions)
9915ddcc9e9SPaulo Alcantara cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
9927449d736SSteve French
993831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
99477e5d743SPaulo Alcantara FILE_READ_ATTRIBUTES |
99577e5d743SPaulo Alcantara FILE_READ_EA | SYNCHRONIZE,
9967449d736SSteve French FILE_OPEN, create_options |
9977449d736SSteve French OPEN_REPARSE_POINT, ACL_NO_MODE);
99838c8a9a5SSteve French cifs_get_readable_path(tcon, full_path, &cfile);
9995ddcc9e9SPaulo Alcantara free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
100038c8a9a5SSteve French rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
1001831ffbd1SPaulo Alcantara &oparms, in_iov, cmds, num_cmds,
10025ddcc9e9SPaulo Alcantara cfile, out_iov, out_buftype, NULL);
10035ddcc9e9SPaulo Alcantara if (rc && ea_unsupported(cmds, num_cmds,
10045ddcc9e9SPaulo Alcantara out_iov, out_buftype)) {
10055ddcc9e9SPaulo Alcantara if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
10065ddcc9e9SPaulo Alcantara data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
10075ddcc9e9SPaulo Alcantara rc = 0;
10085ddcc9e9SPaulo Alcantara else
10095ddcc9e9SPaulo Alcantara rc = -EOPNOTSUPP;
10105ddcc9e9SPaulo Alcantara }
10115f71ebc4SPaulo Alcantara break;
10125f71ebc4SPaulo Alcantara case -EREMOTE:
10135f71ebc4SPaulo Alcantara break;
10145f71ebc4SPaulo Alcantara default:
10155f71ebc4SPaulo Alcantara if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
10165f71ebc4SPaulo Alcantara break;
101738c8a9a5SSteve French rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
101838c8a9a5SSteve French full_path, &islink);
101938c8a9a5SSteve French if (rc2) {
102038c8a9a5SSteve French rc = rc2;
102138c8a9a5SSteve French goto out;
102238c8a9a5SSteve French }
102338c8a9a5SSteve French if (islink)
102438c8a9a5SSteve French rc = -EREMOTE;
102538c8a9a5SSteve French }
102638c8a9a5SSteve French
102738c8a9a5SSteve French out:
10285ddcc9e9SPaulo Alcantara free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
102938c8a9a5SSteve French return rc;
103038c8a9a5SSteve French }
103138c8a9a5SSteve French
103238c8a9a5SSteve French int
smb2_mkdir(const unsigned int xid,struct inode * parent_inode,umode_t mode,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb)103338c8a9a5SSteve French smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
103438c8a9a5SSteve French struct cifs_tcon *tcon, const char *name,
103538c8a9a5SSteve French struct cifs_sb_info *cifs_sb)
103638c8a9a5SSteve French {
1037831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1038831ffbd1SPaulo Alcantara
1039831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
1040831ffbd1SPaulo Alcantara FILE_CREATE, CREATE_NOT_FILE, mode);
1041831ffbd1SPaulo Alcantara return smb2_compound_op(xid, tcon, cifs_sb,
1042831ffbd1SPaulo Alcantara name, &oparms, NULL,
1043831ffbd1SPaulo Alcantara &(int){SMB2_OP_MKDIR}, 1,
1044f93d145fSMeetakshi Setiya NULL, NULL, NULL, NULL);
104538c8a9a5SSteve French }
104638c8a9a5SSteve French
104738c8a9a5SSteve French void
smb2_mkdir_setinfo(struct inode * inode,const char * name,struct cifs_sb_info * cifs_sb,struct cifs_tcon * tcon,const unsigned int xid)104838c8a9a5SSteve French smb2_mkdir_setinfo(struct inode *inode, const char *name,
104938c8a9a5SSteve French struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
105038c8a9a5SSteve French const unsigned int xid)
105138c8a9a5SSteve French {
1052831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1053886b7fb4SSteve French FILE_BASIC_INFO data = {};
105438c8a9a5SSteve French struct cifsInodeInfo *cifs_i;
105538c8a9a5SSteve French struct cifsFileInfo *cfile;
1056886b7fb4SSteve French struct kvec in_iov;
105738c8a9a5SSteve French u32 dosattrs;
105838c8a9a5SSteve French int tmprc;
105938c8a9a5SSteve French
1060886b7fb4SSteve French in_iov.iov_base = &data;
1061886b7fb4SSteve French in_iov.iov_len = sizeof(data);
106238c8a9a5SSteve French cifs_i = CIFS_I(inode);
106338c8a9a5SSteve French dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
106438c8a9a5SSteve French data.Attributes = cpu_to_le32(dosattrs);
106538c8a9a5SSteve French cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
1066831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
1067831ffbd1SPaulo Alcantara FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);
106838c8a9a5SSteve French tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
1069831ffbd1SPaulo Alcantara &oparms, &in_iov,
1070886b7fb4SSteve French &(int){SMB2_OP_SET_INFO}, 1,
1071f93d145fSMeetakshi Setiya cfile, NULL, NULL, NULL);
107238c8a9a5SSteve French if (tmprc == 0)
107338c8a9a5SSteve French cifs_i->cifsAttrs = dosattrs;
107438c8a9a5SSteve French }
107538c8a9a5SSteve French
107638c8a9a5SSteve French int
smb2_rmdir(const unsigned int xid,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb)107738c8a9a5SSteve French smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
107838c8a9a5SSteve French struct cifs_sb_info *cifs_sb)
107938c8a9a5SSteve French {
1080831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1081831ffbd1SPaulo Alcantara
108238c8a9a5SSteve French drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
1083831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE,
1084831ffbd1SPaulo Alcantara FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);
1085831ffbd1SPaulo Alcantara return smb2_compound_op(xid, tcon, cifs_sb,
1086831ffbd1SPaulo Alcantara name, &oparms, NULL,
1087a90f37e3SSteve French &(int){SMB2_OP_RMDIR}, 1,
1088f93d145fSMeetakshi Setiya NULL, NULL, NULL, NULL);
108938c8a9a5SSteve French }
109038c8a9a5SSteve French
109138c8a9a5SSteve French int
smb2_unlink(const unsigned int xid,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb,struct dentry * dentry)109238c8a9a5SSteve French smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
1093f93d145fSMeetakshi Setiya struct cifs_sb_info *cifs_sb, struct dentry *dentry)
109438c8a9a5SSteve French {
1095831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1096831ffbd1SPaulo Alcantara
1097831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, name,
1098831ffbd1SPaulo Alcantara DELETE, FILE_OPEN,
109938c8a9a5SSteve French CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
1100831ffbd1SPaulo Alcantara ACL_NO_MODE);
1101831ffbd1SPaulo Alcantara int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
1102831ffbd1SPaulo Alcantara NULL, &(int){SMB2_OP_DELETE}, 1,
1103f93d145fSMeetakshi Setiya NULL, NULL, NULL, dentry);
11041e60bc0eSMeetakshi Setiya if (rc == -EINVAL) {
11051e60bc0eSMeetakshi Setiya cifs_dbg(FYI, "invalid lease key, resending request without lease");
1106831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
1107831ffbd1SPaulo Alcantara NULL, &(int){SMB2_OP_DELETE}, 1,
11081e60bc0eSMeetakshi Setiya NULL, NULL, NULL, NULL);
11091e60bc0eSMeetakshi Setiya }
11101e60bc0eSMeetakshi Setiya return rc;
111138c8a9a5SSteve French }
111238c8a9a5SSteve French
smb2_set_path_attr(const unsigned int xid,struct cifs_tcon * tcon,const char * from_name,const char * to_name,struct cifs_sb_info * cifs_sb,__u32 create_options,__u32 access,int command,struct cifsFileInfo * cfile,struct dentry * dentry)1113c586b0c7SPaulo Alcantara static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
111438c8a9a5SSteve French const char *from_name, const char *to_name,
1115c586b0c7SPaulo Alcantara struct cifs_sb_info *cifs_sb,
1116c586b0c7SPaulo Alcantara __u32 create_options, __u32 access,
1117f93d145fSMeetakshi Setiya int command, struct cifsFileInfo *cfile,
1118f93d145fSMeetakshi Setiya struct dentry *dentry)
111938c8a9a5SSteve French {
1120831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1121886b7fb4SSteve French struct kvec in_iov;
112238c8a9a5SSteve French __le16 *smb2_to_name = NULL;
112338c8a9a5SSteve French int rc;
112438c8a9a5SSteve French
112538c8a9a5SSteve French smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
112638c8a9a5SSteve French if (smb2_to_name == NULL) {
112738c8a9a5SSteve French rc = -ENOMEM;
112838c8a9a5SSteve French goto smb2_rename_path;
112938c8a9a5SSteve French }
1130886b7fb4SSteve French in_iov.iov_base = smb2_to_name;
1131886b7fb4SSteve French in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
1132831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, from_name, access, FILE_OPEN,
1133831ffbd1SPaulo Alcantara create_options, ACL_NO_MODE);
1134831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb, from_name,
1135831ffbd1SPaulo Alcantara &oparms, &in_iov, &command, 1,
1136831ffbd1SPaulo Alcantara cfile, NULL, NULL, dentry);
113738c8a9a5SSteve French smb2_rename_path:
113838c8a9a5SSteve French kfree(smb2_to_name);
113938c8a9a5SSteve French return rc;
114038c8a9a5SSteve French }
114138c8a9a5SSteve French
smb2_rename_path(const unsigned int xid,struct cifs_tcon * tcon,struct dentry * source_dentry,const char * from_name,const char * to_name,struct cifs_sb_info * cifs_sb)1142c586b0c7SPaulo Alcantara int smb2_rename_path(const unsigned int xid,
1143c586b0c7SPaulo Alcantara struct cifs_tcon *tcon,
1144c586b0c7SPaulo Alcantara struct dentry *source_dentry,
114538c8a9a5SSteve French const char *from_name, const char *to_name,
114638c8a9a5SSteve French struct cifs_sb_info *cifs_sb)
114738c8a9a5SSteve French {
114838c8a9a5SSteve French struct cifsFileInfo *cfile;
11498c944f8aSPaulo Alcantara __u32 co = file_create_options(source_dentry);
115038c8a9a5SSteve French
115138c8a9a5SSteve French drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
115238c8a9a5SSteve French cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
115338c8a9a5SSteve French
11541e60bc0eSMeetakshi Setiya int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
1155f93d145fSMeetakshi Setiya co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
11561e60bc0eSMeetakshi Setiya if (rc == -EINVAL) {
11571e60bc0eSMeetakshi Setiya cifs_dbg(FYI, "invalid lease key, resending request without lease");
1158b27ea9c9SPaulo Alcantara cifs_get_writable_path(tcon, from_name,
1159b27ea9c9SPaulo Alcantara FIND_WR_WITH_DELETE, &cfile);
11601e60bc0eSMeetakshi Setiya rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
11611e60bc0eSMeetakshi Setiya co, DELETE, SMB2_OP_RENAME, cfile, NULL);
11621e60bc0eSMeetakshi Setiya }
11631e60bc0eSMeetakshi Setiya return rc;
116438c8a9a5SSteve French }
116538c8a9a5SSteve French
smb2_create_hardlink(const unsigned int xid,struct cifs_tcon * tcon,struct dentry * source_dentry,const char * from_name,const char * to_name,struct cifs_sb_info * cifs_sb)11668c944f8aSPaulo Alcantara int smb2_create_hardlink(const unsigned int xid,
11678c944f8aSPaulo Alcantara struct cifs_tcon *tcon,
11688c944f8aSPaulo Alcantara struct dentry *source_dentry,
116938c8a9a5SSteve French const char *from_name, const char *to_name,
117038c8a9a5SSteve French struct cifs_sb_info *cifs_sb)
117138c8a9a5SSteve French {
11728c944f8aSPaulo Alcantara __u32 co = file_create_options(source_dentry);
11738c944f8aSPaulo Alcantara
1174c586b0c7SPaulo Alcantara return smb2_set_path_attr(xid, tcon, from_name, to_name,
11758c944f8aSPaulo Alcantara cifs_sb, co, FILE_READ_ATTRIBUTES,
1176f93d145fSMeetakshi Setiya SMB2_OP_HARDLINK, NULL, NULL);
117738c8a9a5SSteve French }
117838c8a9a5SSteve French
117938c8a9a5SSteve French int
smb2_set_path_size(const unsigned int xid,struct cifs_tcon * tcon,const char * full_path,__u64 size,struct cifs_sb_info * cifs_sb,bool set_alloc,struct dentry * dentry)118038c8a9a5SSteve French smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
118138c8a9a5SSteve French const char *full_path, __u64 size,
1182f93d145fSMeetakshi Setiya struct cifs_sb_info *cifs_sb, bool set_alloc,
1183f93d145fSMeetakshi Setiya struct dentry *dentry)
118438c8a9a5SSteve French {
1185831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
118638c8a9a5SSteve French struct cifsFileInfo *cfile;
1187886b7fb4SSteve French struct kvec in_iov;
1188886b7fb4SSteve French __le64 eof = cpu_to_le64(size);
1189831ffbd1SPaulo Alcantara int rc;
119038c8a9a5SSteve French
1191886b7fb4SSteve French in_iov.iov_base = &eof;
1192886b7fb4SSteve French in_iov.iov_len = sizeof(eof);
119338c8a9a5SSteve French cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
1194831ffbd1SPaulo Alcantara
1195831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
1196831ffbd1SPaulo Alcantara FILE_OPEN, 0, ACL_NO_MODE);
1197831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb,
1198831ffbd1SPaulo Alcantara full_path, &oparms, &in_iov,
1199886b7fb4SSteve French &(int){SMB2_OP_SET_EOF}, 1,
1200f93d145fSMeetakshi Setiya cfile, NULL, NULL, dentry);
12011e60bc0eSMeetakshi Setiya if (rc == -EINVAL) {
12021e60bc0eSMeetakshi Setiya cifs_dbg(FYI, "invalid lease key, resending request without lease");
12035a72d1edSPaulo Alcantara cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
1204831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb,
1205831ffbd1SPaulo Alcantara full_path, &oparms, &in_iov,
12061e60bc0eSMeetakshi Setiya &(int){SMB2_OP_SET_EOF}, 1,
12071e60bc0eSMeetakshi Setiya cfile, NULL, NULL, NULL);
12081e60bc0eSMeetakshi Setiya }
12091e60bc0eSMeetakshi Setiya return rc;
121038c8a9a5SSteve French }
121138c8a9a5SSteve French
121238c8a9a5SSteve French int
smb2_set_file_info(struct inode * inode,const char * full_path,FILE_BASIC_INFO * buf,const unsigned int xid)121338c8a9a5SSteve French smb2_set_file_info(struct inode *inode, const char *full_path,
121438c8a9a5SSteve French FILE_BASIC_INFO *buf, const unsigned int xid)
121538c8a9a5SSteve French {
1216831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
121738c8a9a5SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
121838c8a9a5SSteve French struct tcon_link *tlink;
121938c8a9a5SSteve French struct cifs_tcon *tcon;
122038c8a9a5SSteve French struct cifsFileInfo *cfile;
1221886b7fb4SSteve French struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
122238c8a9a5SSteve French int rc;
122338c8a9a5SSteve French
122438c8a9a5SSteve French if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
122538c8a9a5SSteve French (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
122638c8a9a5SSteve French (buf->Attributes == 0))
122738c8a9a5SSteve French return 0; /* would be a no op, no sense sending this */
122838c8a9a5SSteve French
122938c8a9a5SSteve French tlink = cifs_sb_tlink(cifs_sb);
123038c8a9a5SSteve French if (IS_ERR(tlink))
123138c8a9a5SSteve French return PTR_ERR(tlink);
123238c8a9a5SSteve French tcon = tlink_tcon(tlink);
123338c8a9a5SSteve French
123438c8a9a5SSteve French cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
1235831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
1236831ffbd1SPaulo Alcantara FILE_OPEN, 0, ACL_NO_MODE);
1237831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb,
1238831ffbd1SPaulo Alcantara full_path, &oparms, &in_iov,
1239a90f37e3SSteve French &(int){SMB2_OP_SET_INFO}, 1,
1240f93d145fSMeetakshi Setiya cfile, NULL, NULL, NULL);
124138c8a9a5SSteve French cifs_put_tlink(tlink);
124238c8a9a5SSteve French return rc;
124338c8a9a5SSteve French }
124402bcf865SSteve French
smb2_get_reparse_inode(struct cifs_open_info_data * data,struct super_block * sb,const unsigned int xid,struct cifs_tcon * tcon,const char * full_path,bool directory,struct kvec * reparse_iov,struct kvec * xattr_iov)124502bcf865SSteve French struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
124602bcf865SSteve French struct super_block *sb,
124702bcf865SSteve French const unsigned int xid,
124802bcf865SSteve French struct cifs_tcon *tcon,
124902bcf865SSteve French const char *full_path,
1250201430d2SPali Rohár bool directory,
1251df0a8a19SPaulo Alcantara struct kvec *reparse_iov,
1252df0a8a19SPaulo Alcantara struct kvec *xattr_iov)
125302bcf865SSteve French {
1254831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
125502bcf865SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
125602bcf865SSteve French struct cifsFileInfo *cfile;
125702bcf865SSteve French struct inode *new = NULL;
1258e4c886ddSPali Rohár int out_buftype[4] = {};
1259e4c886ddSPali Rohár struct kvec out_iov[4] = {};
126002bcf865SSteve French struct kvec in_iov[2];
126102bcf865SSteve French int cmds[2];
126202bcf865SSteve French int rc;
1263e4c886ddSPali Rohár int i;
126402bcf865SSteve French
1265831ffbd1SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
1266831ffbd1SPaulo Alcantara SYNCHRONIZE | DELETE |
126702bcf865SSteve French FILE_READ_ATTRIBUTES |
1268831ffbd1SPaulo Alcantara FILE_WRITE_ATTRIBUTES,
1269831ffbd1SPaulo Alcantara FILE_CREATE,
1270201430d2SPali Rohár (directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,
1271831ffbd1SPaulo Alcantara ACL_NO_MODE);
1272df0a8a19SPaulo Alcantara if (xattr_iov)
1273df0a8a19SPaulo Alcantara oparms.ea_cctx = xattr_iov;
1274df0a8a19SPaulo Alcantara
127502bcf865SSteve French cmds[0] = SMB2_OP_SET_REPARSE;
1276df0a8a19SPaulo Alcantara in_iov[0] = *reparse_iov;
127702bcf865SSteve French in_iov[1].iov_base = data;
127802bcf865SSteve French in_iov[1].iov_len = sizeof(*data);
127902bcf865SSteve French
128002bcf865SSteve French if (tcon->posix_extensions) {
128102bcf865SSteve French cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
128202bcf865SSteve French cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
1283831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
1284e4c886ddSPali Rohár in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
128502bcf865SSteve French if (!rc) {
128602bcf865SSteve French rc = smb311_posix_get_inode_info(&new, full_path,
128702bcf865SSteve French data, sb, xid);
128802bcf865SSteve French }
128902bcf865SSteve French } else {
129002bcf865SSteve French cmds[1] = SMB2_OP_QUERY_INFO;
129102bcf865SSteve French cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
1292831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
1293e4c886ddSPali Rohár in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
129402bcf865SSteve French if (!rc) {
129502bcf865SSteve French rc = cifs_get_inode_info(&new, full_path,
129602bcf865SSteve French data, sb, xid, NULL);
129702bcf865SSteve French }
129802bcf865SSteve French }
1299e4c886ddSPali Rohár
1300e4c886ddSPali Rohár
1301e4c886ddSPali Rohár /*
1302e4c886ddSPali Rohár * If CREATE was successful but SMB2_OP_SET_REPARSE failed then
1303e4c886ddSPali Rohár * remove the intermediate object created by CREATE. Otherwise
1304e4c886ddSPali Rohár * empty object stay on the server when reparse call failed.
1305e4c886ddSPali Rohár */
1306e4c886ddSPali Rohár if (rc &&
1307e4c886ddSPali Rohár out_iov[0].iov_base != NULL && out_buftype[0] != CIFS_NO_BUFFER &&
1308e4c886ddSPali Rohár ((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_SUCCESS &&
1309e4c886ddSPali Rohár (out_iov[1].iov_base == NULL || out_buftype[1] == CIFS_NO_BUFFER ||
1310e4c886ddSPali Rohár ((struct smb2_hdr *)out_iov[1].iov_base)->Status != STATUS_SUCCESS))
1311e4c886ddSPali Rohár smb2_unlink(xid, tcon, full_path, cifs_sb, NULL);
1312e4c886ddSPali Rohár
1313e4c886ddSPali Rohár for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
1314e4c886ddSPali Rohár free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
1315e4c886ddSPali Rohár
131602bcf865SSteve French return rc ? ERR_PTR(rc) : new;
131702bcf865SSteve French }
1318870c73abSPaulo Alcantara
smb2_query_reparse_point(const unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const char * full_path,u32 * tag,struct kvec * rsp,int * rsp_buftype)1319870c73abSPaulo Alcantara int smb2_query_reparse_point(const unsigned int xid,
1320870c73abSPaulo Alcantara struct cifs_tcon *tcon,
1321870c73abSPaulo Alcantara struct cifs_sb_info *cifs_sb,
1322870c73abSPaulo Alcantara const char *full_path,
1323870c73abSPaulo Alcantara u32 *tag, struct kvec *rsp,
1324870c73abSPaulo Alcantara int *rsp_buftype)
1325870c73abSPaulo Alcantara {
1326831ffbd1SPaulo Alcantara struct cifs_open_parms oparms;
1327870c73abSPaulo Alcantara struct cifs_open_info_data data = {};
1328870c73abSPaulo Alcantara struct cifsFileInfo *cfile;
1329870c73abSPaulo Alcantara struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };
1330870c73abSPaulo Alcantara int rc;
1331870c73abSPaulo Alcantara
1332870c73abSPaulo Alcantara cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
1333870c73abSPaulo Alcantara
1334870c73abSPaulo Alcantara cifs_get_readable_path(tcon, full_path, &cfile);
133577e5d743SPaulo Alcantara oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
133677e5d743SPaulo Alcantara FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
1337831ffbd1SPaulo Alcantara FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE);
1338831ffbd1SPaulo Alcantara rc = smb2_compound_op(xid, tcon, cifs_sb,
1339831ffbd1SPaulo Alcantara full_path, &oparms, &in_iov,
1340a90f37e3SSteve French &(int){SMB2_OP_GET_REPARSE}, 1,
1341f93d145fSMeetakshi Setiya cfile, NULL, NULL, NULL);
1342870c73abSPaulo Alcantara if (rc)
1343870c73abSPaulo Alcantara goto out;
1344870c73abSPaulo Alcantara
1345870c73abSPaulo Alcantara *tag = data.reparse.tag;
1346870c73abSPaulo Alcantara *rsp = data.reparse.io.iov;
1347870c73abSPaulo Alcantara *rsp_buftype = data.reparse.io.buftype;
1348870c73abSPaulo Alcantara memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov));
1349870c73abSPaulo Alcantara data.reparse.io.buftype = CIFS_NO_BUFFER;
1350870c73abSPaulo Alcantara out:
1351870c73abSPaulo Alcantara cifs_free_open_info(&data);
1352870c73abSPaulo Alcantara return rc;
1353870c73abSPaulo Alcantara }
1354