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