1305 lines · c
1// SPDX-License-Identifier: LGPL-2.12/*3 *4 * Copyright (C) International Business Machines Corp., 2002, 20115 * Etersoft, 20126 * Author(s): Pavel Shilovsky (pshilovsky@samba.org),7 * Steve French (sfrench@us.ibm.com)8 *9 */10#include <linux/fs.h>11#include <linux/stat.h>12#include <linux/slab.h>13#include <linux/pagemap.h>14#include <asm/div64.h>15#include "cifsfs.h"16#include "cifspdu.h"17#include "cifsglob.h"18#include "cifsproto.h"19#include "cifs_debug.h"20#include "cifs_fs_sb.h"21#include "cifs_unicode.h"22#include "fscache.h"23#include "smb2glob.h"24#include "smb2pdu.h"25#include "smb2proto.h"26#include "cached_dir.h"27#include "../common/smb2status.h"28 29static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)30{31 struct reparse_data_buffer *buf;32 struct smb2_ioctl_rsp *io = iov->iov_base;33 u32 off, count, len;34 35 count = le32_to_cpu(io->OutputCount);36 off = le32_to_cpu(io->OutputOffset);37 if (check_add_overflow(off, count, &len) || len > iov->iov_len)38 return ERR_PTR(-EIO);39 40 buf = (struct reparse_data_buffer *)((u8 *)io + off);41 len = sizeof(*buf);42 if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)43 return ERR_PTR(-EIO);44 return buf;45}46 47static inline __u32 file_create_options(struct dentry *dentry)48{49 struct cifsInodeInfo *ci;50 51 if (dentry) {52 ci = CIFS_I(d_inode(dentry));53 if (ci->cifsAttrs & ATTR_REPARSE)54 return OPEN_REPARSE_POINT;55 }56 return 0;57}58 59/* Parse owner and group from SMB3.1.1 POSIX query info */60static int parse_posix_sids(struct cifs_open_info_data *data,61 struct kvec *rsp_iov)62{63 struct smb2_query_info_rsp *qi = rsp_iov->iov_base;64 unsigned int out_len = le32_to_cpu(qi->OutputBufferLength);65 unsigned int qi_len = sizeof(data->posix_fi);66 int owner_len, group_len;67 u8 *sidsbuf, *sidsbuf_end;68 69 if (out_len <= qi_len)70 return -EINVAL;71 72 sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;73 sidsbuf_end = sidsbuf + out_len - qi_len;74 75 owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);76 if (owner_len == -1)77 return -EINVAL;78 79 memcpy(&data->posix_owner, sidsbuf, owner_len);80 group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end);81 if (group_len == -1)82 return -EINVAL;83 84 memcpy(&data->posix_group, sidsbuf + owner_len, group_len);85 return 0;86}87 88struct wsl_query_ea {89 __le32 next;90 __u8 name_len;91 __u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];92} __packed;93 94#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))95 96static const struct wsl_query_ea wsl_query_eas[] = {97 { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },98 { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },99 { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },100 { .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },101};102 103static int check_wsl_eas(struct kvec *rsp_iov)104{105 struct smb2_file_full_ea_info *ea;106 struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;107 unsigned long addr;108 u32 outlen, next;109 u16 vlen;110 u8 nlen;111 u8 *end;112 113 outlen = le32_to_cpu(rsp->OutputBufferLength);114 if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||115 outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)116 return -EINVAL;117 118 ea = (void *)((u8 *)rsp_iov->iov_base +119 le16_to_cpu(rsp->OutputBufferOffset));120 end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;121 for (;;) {122 if ((u8 *)ea > end - sizeof(*ea))123 return -EINVAL;124 125 nlen = ea->ea_name_length;126 vlen = le16_to_cpu(ea->ea_value_length);127 if (nlen != SMB2_WSL_XATTR_NAME_LEN ||128 (u8 *)ea + nlen + 1 + vlen > end)129 return -EINVAL;130 131 switch (vlen) {132 case 4:133 if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&134 strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&135 strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))136 return -EINVAL;137 break;138 case 8:139 if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))140 return -EINVAL;141 break;142 case 0:143 if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||144 !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||145 !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||146 !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))147 break;148 fallthrough;149 default:150 return -EINVAL;151 }152 153 next = le32_to_cpu(ea->next_entry_offset);154 if (!next)155 break;156 if (!IS_ALIGNED(next, 4) ||157 check_add_overflow((unsigned long)ea, next, &addr))158 return -EINVAL;159 ea = (void *)addr;160 }161 return 0;162}163 164/*165 * note: If cfile is passed, the reference to it is dropped here.166 * So make sure that you do not reuse cfile after return from this func.167 *168 * If passing @out_iov and @out_buftype, ensure to make them both large enough169 * (>= 3) to hold all compounded responses. Caller is also responsible for170 * freeing them up with free_rsp_buf().171 */172static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,173 struct cifs_sb_info *cifs_sb, const char *full_path,174 struct cifs_open_parms *oparms, struct kvec *in_iov,175 int *cmds, int num_cmds, struct cifsFileInfo *cfile,176 struct kvec *out_iov, int *out_buftype, struct dentry *dentry)177{178 179 struct reparse_data_buffer *rbuf;180 struct smb2_compound_vars *vars = NULL;181 struct kvec *rsp_iov, *iov;182 struct smb_rqst *rqst;183 int rc;184 __le16 *utf16_path = NULL;185 __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;186 struct cifs_fid fid;187 struct cifs_ses *ses = tcon->ses;188 struct TCP_Server_Info *server;189 int num_rqst = 0, i;190 int resp_buftype[MAX_COMPOUND];191 struct smb2_query_info_rsp *qi_rsp = NULL;192 struct cifs_open_info_data *idata;193 struct inode *inode = NULL;194 int flags = 0;195 __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};196 unsigned int size[2];197 void *data[2];198 unsigned int len;199 int retries = 0, cur_sleep = 1;200 201replay_again:202 /* reinitialize for possible replay */203 flags = 0;204 oplock = SMB2_OPLOCK_LEVEL_NONE;205 num_rqst = 0;206 server = cifs_pick_channel(ses);207 208 vars = kzalloc(sizeof(*vars), GFP_ATOMIC);209 if (vars == NULL)210 return -ENOMEM;211 rqst = &vars->rqst[0];212 rsp_iov = &vars->rsp_iov[0];213 214 if (smb3_encryption_required(tcon))215 flags |= CIFS_TRANSFORM_REQ;216 217 for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)218 resp_buftype[i] = CIFS_NO_BUFFER;219 220 /* We already have a handle so we can skip the open */221 if (cfile)222 goto after_open;223 224 /* Open */225 utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);226 if (!utf16_path) {227 rc = -ENOMEM;228 goto finished;229 }230 231 /* if there is an existing lease, reuse it */232 233 /*234 * note: files with hardlinks cause unexpected behaviour. As per MS-SMB2,235 * lease keys are associated with the filepath. We are maintaining lease keys236 * with the inode on the client. If the file has hardlinks, it is possible237 * that the lease for a file be reused for an operation on its hardlink or238 * vice versa.239 * As a workaround, send request using an existing lease key and if the server240 * returns STATUS_INVALID_PARAMETER, which maps to EINVAL, send the request241 * again without the lease.242 */243 if (dentry) {244 inode = d_inode(dentry);245 if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {246 oplock = SMB2_OPLOCK_LEVEL_LEASE;247 server->ops->get_lease_key(inode, &fid);248 }249 }250 251 vars->oparms = *oparms;252 vars->oparms.fid = &fid;253 254 rqst[num_rqst].rq_iov = &vars->open_iov[0];255 rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;256 rc = SMB2_open_init(tcon, server,257 &rqst[num_rqst], &oplock, &vars->oparms,258 utf16_path);259 kfree(utf16_path);260 if (rc)261 goto finished;262 263 smb2_set_next_command(tcon, &rqst[num_rqst]);264 after_open:265 num_rqst++;266 rc = 0;267 268 for (i = 0; i < num_cmds; i++) {269 /* Operation */270 switch (cmds[i]) {271 case SMB2_OP_QUERY_INFO:272 rqst[num_rqst].rq_iov = &vars->qi_iov;273 rqst[num_rqst].rq_nvec = 1;274 275 if (cfile) {276 rc = SMB2_query_info_init(tcon, server,277 &rqst[num_rqst],278 cfile->fid.persistent_fid,279 cfile->fid.volatile_fid,280 FILE_ALL_INFORMATION,281 SMB2_O_INFO_FILE, 0,282 sizeof(struct smb2_file_all_info) +283 PATH_MAX * 2, 0, NULL);284 } else {285 rc = SMB2_query_info_init(tcon, server,286 &rqst[num_rqst],287 COMPOUND_FID,288 COMPOUND_FID,289 FILE_ALL_INFORMATION,290 SMB2_O_INFO_FILE, 0,291 sizeof(struct smb2_file_all_info) +292 PATH_MAX * 2, 0, NULL);293 }294 if (!rc && (!cfile || num_rqst > 1)) {295 smb2_set_next_command(tcon, &rqst[num_rqst]);296 smb2_set_related(&rqst[num_rqst]);297 } else if (rc) {298 goto finished;299 }300 num_rqst++;301 trace_smb3_query_info_compound_enter(xid, ses->Suid,302 tcon->tid, full_path);303 break;304 case SMB2_OP_POSIX_QUERY_INFO:305 rqst[num_rqst].rq_iov = &vars->qi_iov;306 rqst[num_rqst].rq_nvec = 1;307 308 if (cfile) {309 /* TBD: fix following to allow for longer SIDs */310 rc = SMB2_query_info_init(tcon, server,311 &rqst[num_rqst],312 cfile->fid.persistent_fid,313 cfile->fid.volatile_fid,314 SMB_FIND_FILE_POSIX_INFO,315 SMB2_O_INFO_FILE, 0,316 sizeof(struct smb311_posix_qinfo *) +317 (PATH_MAX * 2) +318 (sizeof(struct smb_sid) * 2), 0, NULL);319 } else {320 rc = SMB2_query_info_init(tcon, server,321 &rqst[num_rqst],322 COMPOUND_FID,323 COMPOUND_FID,324 SMB_FIND_FILE_POSIX_INFO,325 SMB2_O_INFO_FILE, 0,326 sizeof(struct smb311_posix_qinfo *) +327 (PATH_MAX * 2) +328 (sizeof(struct smb_sid) * 2), 0, NULL);329 }330 if (!rc && (!cfile || num_rqst > 1)) {331 smb2_set_next_command(tcon, &rqst[num_rqst]);332 smb2_set_related(&rqst[num_rqst]);333 } else if (rc) {334 goto finished;335 }336 num_rqst++;337 trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,338 tcon->tid, full_path);339 break;340 case SMB2_OP_DELETE:341 trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);342 break;343 case SMB2_OP_MKDIR:344 /*345 * Directories are created through parameters in the346 * SMB2_open() call.347 */348 trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);349 break;350 case SMB2_OP_RMDIR:351 rqst[num_rqst].rq_iov = &vars->si_iov[0];352 rqst[num_rqst].rq_nvec = 1;353 354 size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */355 data[0] = &delete_pending[0];356 357 rc = SMB2_set_info_init(tcon, server,358 &rqst[num_rqst], COMPOUND_FID,359 COMPOUND_FID, current->tgid,360 FILE_DISPOSITION_INFORMATION,361 SMB2_O_INFO_FILE, 0, data, size);362 if (rc)363 goto finished;364 smb2_set_next_command(tcon, &rqst[num_rqst]);365 smb2_set_related(&rqst[num_rqst++]);366 trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);367 break;368 case SMB2_OP_SET_EOF:369 rqst[num_rqst].rq_iov = &vars->si_iov[0];370 rqst[num_rqst].rq_nvec = 1;371 372 size[0] = in_iov[i].iov_len;373 data[0] = in_iov[i].iov_base;374 375 if (cfile) {376 rc = SMB2_set_info_init(tcon, server,377 &rqst[num_rqst],378 cfile->fid.persistent_fid,379 cfile->fid.volatile_fid,380 current->tgid,381 FILE_END_OF_FILE_INFORMATION,382 SMB2_O_INFO_FILE, 0,383 data, size);384 } else {385 rc = SMB2_set_info_init(tcon, server,386 &rqst[num_rqst],387 COMPOUND_FID,388 COMPOUND_FID,389 current->tgid,390 FILE_END_OF_FILE_INFORMATION,391 SMB2_O_INFO_FILE, 0,392 data, size);393 }394 if (!rc && (!cfile || num_rqst > 1)) {395 smb2_set_next_command(tcon, &rqst[num_rqst]);396 smb2_set_related(&rqst[num_rqst]);397 } else if (rc) {398 goto finished;399 }400 num_rqst++;401 trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);402 break;403 case SMB2_OP_SET_INFO:404 rqst[num_rqst].rq_iov = &vars->si_iov[0];405 rqst[num_rqst].rq_nvec = 1;406 407 size[0] = in_iov[i].iov_len;408 data[0] = in_iov[i].iov_base;409 410 if (cfile) {411 rc = SMB2_set_info_init(tcon, server,412 &rqst[num_rqst],413 cfile->fid.persistent_fid,414 cfile->fid.volatile_fid, current->tgid,415 FILE_BASIC_INFORMATION,416 SMB2_O_INFO_FILE, 0, data, size);417 } else {418 rc = SMB2_set_info_init(tcon, server,419 &rqst[num_rqst],420 COMPOUND_FID,421 COMPOUND_FID, current->tgid,422 FILE_BASIC_INFORMATION,423 SMB2_O_INFO_FILE, 0, data, size);424 }425 if (!rc && (!cfile || num_rqst > 1)) {426 smb2_set_next_command(tcon, &rqst[num_rqst]);427 smb2_set_related(&rqst[num_rqst]);428 } else if (rc) {429 goto finished;430 }431 num_rqst++;432 trace_smb3_set_info_compound_enter(xid, ses->Suid,433 tcon->tid, full_path);434 break;435 case SMB2_OP_RENAME:436 rqst[num_rqst].rq_iov = &vars->si_iov[0];437 rqst[num_rqst].rq_nvec = 2;438 439 len = in_iov[i].iov_len;440 441 vars->rename_info.ReplaceIfExists = 1;442 vars->rename_info.RootDirectory = 0;443 vars->rename_info.FileNameLength = cpu_to_le32(len);444 445 size[0] = sizeof(struct smb2_file_rename_info);446 data[0] = &vars->rename_info;447 448 size[1] = len + 2 /* null */;449 data[1] = in_iov[i].iov_base;450 451 if (cfile) {452 rc = SMB2_set_info_init(tcon, server,453 &rqst[num_rqst],454 cfile->fid.persistent_fid,455 cfile->fid.volatile_fid,456 current->tgid, FILE_RENAME_INFORMATION,457 SMB2_O_INFO_FILE, 0, data, size);458 } else {459 rc = SMB2_set_info_init(tcon, server,460 &rqst[num_rqst],461 COMPOUND_FID, COMPOUND_FID,462 current->tgid, FILE_RENAME_INFORMATION,463 SMB2_O_INFO_FILE, 0, data, size);464 }465 if (!rc && (!cfile || num_rqst > 1)) {466 smb2_set_next_command(tcon, &rqst[num_rqst]);467 smb2_set_related(&rqst[num_rqst]);468 } else if (rc) {469 goto finished;470 }471 num_rqst++;472 trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);473 break;474 case SMB2_OP_HARDLINK:475 rqst[num_rqst].rq_iov = &vars->si_iov[0];476 rqst[num_rqst].rq_nvec = 2;477 478 len = in_iov[i].iov_len;479 480 vars->link_info.ReplaceIfExists = 0;481 vars->link_info.RootDirectory = 0;482 vars->link_info.FileNameLength = cpu_to_le32(len);483 484 size[0] = sizeof(struct smb2_file_link_info);485 data[0] = &vars->link_info;486 487 size[1] = len + 2 /* null */;488 data[1] = in_iov[i].iov_base;489 490 rc = SMB2_set_info_init(tcon, server,491 &rqst[num_rqst], COMPOUND_FID,492 COMPOUND_FID, current->tgid,493 FILE_LINK_INFORMATION,494 SMB2_O_INFO_FILE, 0, data, size);495 if (rc)496 goto finished;497 smb2_set_next_command(tcon, &rqst[num_rqst]);498 smb2_set_related(&rqst[num_rqst++]);499 trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);500 break;501 case SMB2_OP_SET_REPARSE:502 rqst[num_rqst].rq_iov = vars->io_iov;503 rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);504 505 if (cfile) {506 rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],507 cfile->fid.persistent_fid,508 cfile->fid.volatile_fid,509 FSCTL_SET_REPARSE_POINT,510 in_iov[i].iov_base,511 in_iov[i].iov_len, 0);512 } else {513 rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],514 COMPOUND_FID, COMPOUND_FID,515 FSCTL_SET_REPARSE_POINT,516 in_iov[i].iov_base,517 in_iov[i].iov_len, 0);518 }519 if (!rc && (!cfile || num_rqst > 1)) {520 smb2_set_next_command(tcon, &rqst[num_rqst]);521 smb2_set_related(&rqst[num_rqst]);522 } else if (rc) {523 goto finished;524 }525 num_rqst++;526 trace_smb3_set_reparse_compound_enter(xid, ses->Suid,527 tcon->tid, full_path);528 break;529 case SMB2_OP_GET_REPARSE:530 rqst[num_rqst].rq_iov = vars->io_iov;531 rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);532 533 if (cfile) {534 rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],535 cfile->fid.persistent_fid,536 cfile->fid.volatile_fid,537 FSCTL_GET_REPARSE_POINT,538 NULL, 0, CIFSMaxBufSize);539 } else {540 rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],541 COMPOUND_FID, COMPOUND_FID,542 FSCTL_GET_REPARSE_POINT,543 NULL, 0, CIFSMaxBufSize);544 }545 if (!rc && (!cfile || num_rqst > 1)) {546 smb2_set_next_command(tcon, &rqst[num_rqst]);547 smb2_set_related(&rqst[num_rqst]);548 } else if (rc) {549 goto finished;550 }551 num_rqst++;552 trace_smb3_get_reparse_compound_enter(xid, ses->Suid,553 tcon->tid, full_path);554 break;555 case SMB2_OP_QUERY_WSL_EA:556 rqst[num_rqst].rq_iov = &vars->ea_iov;557 rqst[num_rqst].rq_nvec = 1;558 559 if (cfile) {560 rc = SMB2_query_info_init(tcon, server,561 &rqst[num_rqst],562 cfile->fid.persistent_fid,563 cfile->fid.volatile_fid,564 FILE_FULL_EA_INFORMATION,565 SMB2_O_INFO_FILE, 0,566 SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,567 sizeof(wsl_query_eas),568 (void *)wsl_query_eas);569 } else {570 rc = SMB2_query_info_init(tcon, server,571 &rqst[num_rqst],572 COMPOUND_FID,573 COMPOUND_FID,574 FILE_FULL_EA_INFORMATION,575 SMB2_O_INFO_FILE, 0,576 SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,577 sizeof(wsl_query_eas),578 (void *)wsl_query_eas);579 }580 if (!rc && (!cfile || num_rqst > 1)) {581 smb2_set_next_command(tcon, &rqst[num_rqst]);582 smb2_set_related(&rqst[num_rqst]);583 } else if (rc) {584 goto finished;585 }586 num_rqst++;587 break;588 default:589 cifs_dbg(VFS, "Invalid command\n");590 rc = -EINVAL;591 }592 }593 if (rc)594 goto finished;595 596 /* We already have a handle so we can skip the close */597 if (cfile)598 goto after_close;599 /* Close */600 flags |= CIFS_CP_CREATE_CLOSE_OP;601 rqst[num_rqst].rq_iov = &vars->close_iov;602 rqst[num_rqst].rq_nvec = 1;603 rc = SMB2_close_init(tcon, server,604 &rqst[num_rqst], COMPOUND_FID,605 COMPOUND_FID, false);606 smb2_set_related(&rqst[num_rqst]);607 if (rc)608 goto finished;609 after_close:610 num_rqst++;611 612 if (cfile) {613 if (retries)614 for (i = 1; i < num_rqst - 2; i++)615 smb2_set_replay(server, &rqst[i]);616 617 rc = compound_send_recv(xid, ses, server,618 flags, num_rqst - 2,619 &rqst[1], &resp_buftype[1],620 &rsp_iov[1]);621 } else {622 if (retries)623 for (i = 0; i < num_rqst; i++)624 smb2_set_replay(server, &rqst[i]);625 626 rc = compound_send_recv(xid, ses, server,627 flags, num_rqst,628 rqst, resp_buftype,629 rsp_iov);630 }631 632finished:633 num_rqst = 0;634 SMB2_open_free(&rqst[num_rqst++]);635 if (rc == -EREMCHG) {636 pr_warn_once("server share %s deleted\n", tcon->tree_name);637 tcon->need_reconnect = true;638 }639 640 for (i = 0; i < num_cmds; i++) {641 switch (cmds[i]) {642 case SMB2_OP_QUERY_INFO:643 idata = in_iov[i].iov_base;644 if (rc == 0 && cfile && cfile->symlink_target) {645 idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);646 if (!idata->symlink_target)647 rc = -ENOMEM;648 }649 if (rc == 0) {650 qi_rsp = (struct smb2_query_info_rsp *)651 rsp_iov[i + 1].iov_base;652 rc = smb2_validate_and_copy_iov(653 le16_to_cpu(qi_rsp->OutputBufferOffset),654 le32_to_cpu(qi_rsp->OutputBufferLength),655 &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);656 }657 SMB2_query_info_free(&rqst[num_rqst++]);658 if (rc)659 trace_smb3_query_info_compound_err(xid, ses->Suid,660 tcon->tid, rc);661 else662 trace_smb3_query_info_compound_done(xid, ses->Suid,663 tcon->tid);664 break;665 case SMB2_OP_POSIX_QUERY_INFO:666 idata = in_iov[i].iov_base;667 if (rc == 0 && cfile && cfile->symlink_target) {668 idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);669 if (!idata->symlink_target)670 rc = -ENOMEM;671 }672 if (rc == 0) {673 qi_rsp = (struct smb2_query_info_rsp *)674 rsp_iov[i + 1].iov_base;675 rc = smb2_validate_and_copy_iov(676 le16_to_cpu(qi_rsp->OutputBufferOffset),677 le32_to_cpu(qi_rsp->OutputBufferLength),678 &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,679 (char *)&idata->posix_fi);680 }681 if (rc == 0)682 rc = parse_posix_sids(idata, &rsp_iov[i + 1]);683 684 SMB2_query_info_free(&rqst[num_rqst++]);685 if (rc)686 trace_smb3_posix_query_info_compound_err(xid, ses->Suid,687 tcon->tid, rc);688 else689 trace_smb3_posix_query_info_compound_done(xid, ses->Suid,690 tcon->tid);691 break;692 case SMB2_OP_DELETE:693 if (rc)694 trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);695 else {696 /*697 * If dentry (hence, inode) is NULL, lease break is going to698 * take care of degrading leases on handles for deleted files.699 */700 if (inode)701 cifs_mark_open_handles_for_deleted_file(inode, full_path);702 trace_smb3_delete_done(xid, ses->Suid, tcon->tid);703 }704 break;705 case SMB2_OP_MKDIR:706 if (rc)707 trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);708 else709 trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);710 break;711 case SMB2_OP_HARDLINK:712 if (rc)713 trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);714 else715 trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);716 SMB2_set_info_free(&rqst[num_rqst++]);717 break;718 case SMB2_OP_RENAME:719 if (rc)720 trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);721 else722 trace_smb3_rename_done(xid, ses->Suid, tcon->tid);723 SMB2_set_info_free(&rqst[num_rqst++]);724 break;725 case SMB2_OP_RMDIR:726 if (rc)727 trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);728 else729 trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);730 SMB2_set_info_free(&rqst[num_rqst++]);731 break;732 case SMB2_OP_SET_EOF:733 if (rc)734 trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);735 else736 trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);737 SMB2_set_info_free(&rqst[num_rqst++]);738 break;739 case SMB2_OP_SET_INFO:740 if (rc)741 trace_smb3_set_info_compound_err(xid, ses->Suid,742 tcon->tid, rc);743 else744 trace_smb3_set_info_compound_done(xid, ses->Suid,745 tcon->tid);746 SMB2_set_info_free(&rqst[num_rqst++]);747 break;748 case SMB2_OP_SET_REPARSE:749 if (rc) {750 trace_smb3_set_reparse_compound_err(xid, ses->Suid,751 tcon->tid, rc);752 } else {753 trace_smb3_set_reparse_compound_done(xid, ses->Suid,754 tcon->tid);755 }756 SMB2_ioctl_free(&rqst[num_rqst++]);757 break;758 case SMB2_OP_GET_REPARSE:759 if (!rc) {760 iov = &rsp_iov[i + 1];761 idata = in_iov[i].iov_base;762 idata->reparse.io.iov = *iov;763 idata->reparse.io.buftype = resp_buftype[i + 1];764 rbuf = reparse_buf_ptr(iov);765 if (IS_ERR(rbuf)) {766 rc = PTR_ERR(rbuf);767 trace_smb3_set_reparse_compound_err(xid, ses->Suid,768 tcon->tid, rc);769 } else {770 idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);771 trace_smb3_set_reparse_compound_done(xid, ses->Suid,772 tcon->tid);773 }774 memset(iov, 0, sizeof(*iov));775 resp_buftype[i + 1] = CIFS_NO_BUFFER;776 } else {777 trace_smb3_set_reparse_compound_err(xid, ses->Suid,778 tcon->tid, rc);779 }780 SMB2_ioctl_free(&rqst[num_rqst++]);781 break;782 case SMB2_OP_QUERY_WSL_EA:783 if (!rc) {784 idata = in_iov[i].iov_base;785 qi_rsp = rsp_iov[i + 1].iov_base;786 data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);787 size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);788 rc = check_wsl_eas(&rsp_iov[i + 1]);789 if (!rc) {790 memcpy(idata->wsl.eas, data[0], size[0]);791 idata->wsl.eas_len = size[0];792 }793 }794 if (!rc) {795 trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,796 tcon->tid);797 } else {798 trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,799 tcon->tid, rc);800 }801 SMB2_query_info_free(&rqst[num_rqst++]);802 break;803 }804 }805 SMB2_close_free(&rqst[num_rqst]);806 807 num_cmds += 2;808 if (out_iov && out_buftype) {809 memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));810 memcpy(out_buftype, resp_buftype,811 num_cmds * sizeof(*out_buftype));812 } else {813 for (i = 0; i < num_cmds; i++)814 free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);815 }816 num_cmds -= 2; /* correct num_cmds as there could be a retry */817 kfree(vars);818 819 if (is_replayable_error(rc) &&820 smb2_should_replay(tcon, &retries, &cur_sleep))821 goto replay_again;822 823 if (cfile)824 cifsFileInfo_put(cfile);825 826 return rc;827}828 829static int parse_create_response(struct cifs_open_info_data *data,830 struct cifs_sb_info *cifs_sb,831 const struct kvec *iov)832{833 struct smb2_create_rsp *rsp = iov->iov_base;834 bool reparse_point = false;835 u32 tag = 0;836 int rc = 0;837 838 switch (rsp->hdr.Status) {839 case STATUS_IO_REPARSE_TAG_NOT_HANDLED:840 reparse_point = true;841 break;842 case STATUS_STOPPED_ON_SYMLINK:843 rc = smb2_parse_symlink_response(cifs_sb, iov,844 &data->symlink_target);845 if (rc)846 return rc;847 tag = IO_REPARSE_TAG_SYMLINK;848 reparse_point = true;849 break;850 case STATUS_SUCCESS:851 reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);852 break;853 }854 data->reparse_point = reparse_point;855 data->reparse.tag = tag;856 return rc;857}858 859int smb2_query_path_info(const unsigned int xid,860 struct cifs_tcon *tcon,861 struct cifs_sb_info *cifs_sb,862 const char *full_path,863 struct cifs_open_info_data *data)864{865 struct cifs_open_parms oparms;866 __u32 create_options = 0;867 struct cifsFileInfo *cfile;868 struct cached_fid *cfid = NULL;869 struct smb2_hdr *hdr;870 struct kvec in_iov[3], out_iov[3] = {};871 int out_buftype[3] = {};872 int cmds[3];873 bool islink;874 int i, num_cmds = 0;875 int rc, rc2;876 877 data->adjust_tz = false;878 data->reparse_point = false;879 880 /*881 * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX.882 * Create SMB2_query_posix_info worker function to do non-compounded883 * query when we already have an open file handle for this. For now this884 * is fast enough (always using the compounded version).885 */886 if (!tcon->posix_extensions) {887 if (*full_path) {888 rc = -ENOENT;889 } else {890 rc = open_cached_dir(xid, tcon, full_path,891 cifs_sb, false, &cfid);892 }893 /* If it is a root and its handle is cached then use it */894 if (!rc) {895 if (cfid->file_all_info_is_valid) {896 memcpy(&data->fi, &cfid->file_all_info,897 sizeof(data->fi));898 } else {899 rc = SMB2_query_info(xid, tcon,900 cfid->fid.persistent_fid,901 cfid->fid.volatile_fid,902 &data->fi);903 }904 close_cached_dir(cfid);905 return rc;906 }907 cmds[num_cmds++] = SMB2_OP_QUERY_INFO;908 } else {909 cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;910 }911 912 in_iov[0].iov_base = data;913 in_iov[0].iov_len = sizeof(*data);914 in_iov[1] = in_iov[0];915 in_iov[2] = in_iov[0];916 917 cifs_get_readable_path(tcon, full_path, &cfile);918 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,919 FILE_OPEN, create_options, ACL_NO_MODE);920 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,921 &oparms, in_iov, cmds, num_cmds,922 cfile, out_iov, out_buftype, NULL);923 hdr = out_iov[0].iov_base;924 /*925 * If first iov is unset, then SMB session was dropped or we've got a926 * cached open file (@cfile).927 */928 if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)929 goto out;930 931 switch (rc) {932 case 0:933 rc = parse_create_response(data, cifs_sb, &out_iov[0]);934 break;935 case -EOPNOTSUPP:936 /*937 * BB TODO: When support for special files added to Samba938 * re-verify this path.939 */940 rc = parse_create_response(data, cifs_sb, &out_iov[0]);941 if (rc || !data->reparse_point)942 goto out;943 944 cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;945 /*946 * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create947 * response.948 */949 if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)950 cmds[num_cmds++] = SMB2_OP_GET_REPARSE;951 952 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,953 FILE_READ_ATTRIBUTES |954 FILE_READ_EA | SYNCHRONIZE,955 FILE_OPEN, create_options |956 OPEN_REPARSE_POINT, ACL_NO_MODE);957 cifs_get_readable_path(tcon, full_path, &cfile);958 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,959 &oparms, in_iov, cmds, num_cmds,960 cfile, NULL, NULL, NULL);961 break;962 case -EREMOTE:963 break;964 default:965 if (hdr->Status != STATUS_OBJECT_NAME_INVALID)966 break;967 rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,968 full_path, &islink);969 if (rc2) {970 rc = rc2;971 goto out;972 }973 if (islink)974 rc = -EREMOTE;975 }976 977out:978 for (i = 0; i < ARRAY_SIZE(out_buftype); i++)979 free_rsp_buf(out_buftype[i], out_iov[i].iov_base);980 return rc;981}982 983int984smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,985 struct cifs_tcon *tcon, const char *name,986 struct cifs_sb_info *cifs_sb)987{988 struct cifs_open_parms oparms;989 990 oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,991 FILE_CREATE, CREATE_NOT_FILE, mode);992 return smb2_compound_op(xid, tcon, cifs_sb,993 name, &oparms, NULL,994 &(int){SMB2_OP_MKDIR}, 1,995 NULL, NULL, NULL, NULL);996}997 998void999smb2_mkdir_setinfo(struct inode *inode, const char *name,1000 struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,1001 const unsigned int xid)1002{1003 struct cifs_open_parms oparms;1004 FILE_BASIC_INFO data = {};1005 struct cifsInodeInfo *cifs_i;1006 struct cifsFileInfo *cfile;1007 struct kvec in_iov;1008 u32 dosattrs;1009 int tmprc;1010 1011 in_iov.iov_base = &data;1012 in_iov.iov_len = sizeof(data);1013 cifs_i = CIFS_I(inode);1014 dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;1015 data.Attributes = cpu_to_le32(dosattrs);1016 cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);1017 oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,1018 FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);1019 tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,1020 &oparms, &in_iov,1021 &(int){SMB2_OP_SET_INFO}, 1,1022 cfile, NULL, NULL, NULL);1023 if (tmprc == 0)1024 cifs_i->cifsAttrs = dosattrs;1025}1026 1027int1028smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,1029 struct cifs_sb_info *cifs_sb)1030{1031 struct cifs_open_parms oparms;1032 1033 drop_cached_dir_by_name(xid, tcon, name, cifs_sb);1034 oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE,1035 FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);1036 return smb2_compound_op(xid, tcon, cifs_sb,1037 name, &oparms, NULL,1038 &(int){SMB2_OP_RMDIR}, 1,1039 NULL, NULL, NULL, NULL);1040}1041 1042int1043smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,1044 struct cifs_sb_info *cifs_sb, struct dentry *dentry)1045{1046 struct cifs_open_parms oparms;1047 1048 oparms = CIFS_OPARMS(cifs_sb, tcon, name,1049 DELETE, FILE_OPEN,1050 CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,1051 ACL_NO_MODE);1052 int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,1053 NULL, &(int){SMB2_OP_DELETE}, 1,1054 NULL, NULL, NULL, dentry);1055 if (rc == -EINVAL) {1056 cifs_dbg(FYI, "invalid lease key, resending request without lease");1057 rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,1058 NULL, &(int){SMB2_OP_DELETE}, 1,1059 NULL, NULL, NULL, NULL);1060 }1061 return rc;1062}1063 1064static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,1065 const char *from_name, const char *to_name,1066 struct cifs_sb_info *cifs_sb,1067 __u32 create_options, __u32 access,1068 int command, struct cifsFileInfo *cfile,1069 struct dentry *dentry)1070{1071 struct cifs_open_parms oparms;1072 struct kvec in_iov;1073 __le16 *smb2_to_name = NULL;1074 int rc;1075 1076 smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);1077 if (smb2_to_name == NULL) {1078 rc = -ENOMEM;1079 goto smb2_rename_path;1080 }1081 in_iov.iov_base = smb2_to_name;1082 in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);1083 oparms = CIFS_OPARMS(cifs_sb, tcon, from_name, access, FILE_OPEN,1084 create_options, ACL_NO_MODE);1085 rc = smb2_compound_op(xid, tcon, cifs_sb, from_name,1086 &oparms, &in_iov, &command, 1,1087 cfile, NULL, NULL, dentry);1088smb2_rename_path:1089 kfree(smb2_to_name);1090 return rc;1091}1092 1093int smb2_rename_path(const unsigned int xid,1094 struct cifs_tcon *tcon,1095 struct dentry *source_dentry,1096 const char *from_name, const char *to_name,1097 struct cifs_sb_info *cifs_sb)1098{1099 struct cifsFileInfo *cfile;1100 __u32 co = file_create_options(source_dentry);1101 1102 drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);1103 cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);1104 1105 int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,1106 co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);1107 if (rc == -EINVAL) {1108 cifs_dbg(FYI, "invalid lease key, resending request without lease");1109 cifs_get_writable_path(tcon, from_name,1110 FIND_WR_WITH_DELETE, &cfile);1111 rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,1112 co, DELETE, SMB2_OP_RENAME, cfile, NULL);1113 }1114 return rc;1115}1116 1117int smb2_create_hardlink(const unsigned int xid,1118 struct cifs_tcon *tcon,1119 struct dentry *source_dentry,1120 const char *from_name, const char *to_name,1121 struct cifs_sb_info *cifs_sb)1122{1123 __u32 co = file_create_options(source_dentry);1124 1125 return smb2_set_path_attr(xid, tcon, from_name, to_name,1126 cifs_sb, co, FILE_READ_ATTRIBUTES,1127 SMB2_OP_HARDLINK, NULL, NULL);1128}1129 1130int1131smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,1132 const char *full_path, __u64 size,1133 struct cifs_sb_info *cifs_sb, bool set_alloc,1134 struct dentry *dentry)1135{1136 struct cifs_open_parms oparms;1137 struct cifsFileInfo *cfile;1138 struct kvec in_iov;1139 __le64 eof = cpu_to_le64(size);1140 int rc;1141 1142 in_iov.iov_base = &eof;1143 in_iov.iov_len = sizeof(eof);1144 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);1145 1146 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,1147 FILE_OPEN, 0, ACL_NO_MODE);1148 rc = smb2_compound_op(xid, tcon, cifs_sb,1149 full_path, &oparms, &in_iov,1150 &(int){SMB2_OP_SET_EOF}, 1,1151 cfile, NULL, NULL, dentry);1152 if (rc == -EINVAL) {1153 cifs_dbg(FYI, "invalid lease key, resending request without lease");1154 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);1155 rc = smb2_compound_op(xid, tcon, cifs_sb,1156 full_path, &oparms, &in_iov,1157 &(int){SMB2_OP_SET_EOF}, 1,1158 cfile, NULL, NULL, NULL);1159 }1160 return rc;1161}1162 1163int1164smb2_set_file_info(struct inode *inode, const char *full_path,1165 FILE_BASIC_INFO *buf, const unsigned int xid)1166{1167 struct cifs_open_parms oparms;1168 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);1169 struct tcon_link *tlink;1170 struct cifs_tcon *tcon;1171 struct cifsFileInfo *cfile;1172 struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };1173 int rc;1174 1175 if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&1176 (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&1177 (buf->Attributes == 0))1178 return 0; /* would be a no op, no sense sending this */1179 1180 tlink = cifs_sb_tlink(cifs_sb);1181 if (IS_ERR(tlink))1182 return PTR_ERR(tlink);1183 tcon = tlink_tcon(tlink);1184 1185 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);1186 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,1187 FILE_OPEN, 0, ACL_NO_MODE);1188 rc = smb2_compound_op(xid, tcon, cifs_sb,1189 full_path, &oparms, &in_iov,1190 &(int){SMB2_OP_SET_INFO}, 1,1191 cfile, NULL, NULL, NULL);1192 cifs_put_tlink(tlink);1193 return rc;1194}1195 1196struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,1197 struct super_block *sb,1198 const unsigned int xid,1199 struct cifs_tcon *tcon,1200 const char *full_path,1201 bool directory,1202 struct kvec *reparse_iov,1203 struct kvec *xattr_iov)1204{1205 struct cifs_open_parms oparms;1206 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);1207 struct cifsFileInfo *cfile;1208 struct inode *new = NULL;1209 int out_buftype[4] = {};1210 struct kvec out_iov[4] = {};1211 struct kvec in_iov[2];1212 int cmds[2];1213 int rc;1214 int i;1215 1216 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,1217 SYNCHRONIZE | DELETE |1218 FILE_READ_ATTRIBUTES |1219 FILE_WRITE_ATTRIBUTES,1220 FILE_CREATE,1221 (directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,1222 ACL_NO_MODE);1223 if (xattr_iov)1224 oparms.ea_cctx = xattr_iov;1225 1226 cmds[0] = SMB2_OP_SET_REPARSE;1227 in_iov[0] = *reparse_iov;1228 in_iov[1].iov_base = data;1229 in_iov[1].iov_len = sizeof(*data);1230 1231 if (tcon->posix_extensions) {1232 cmds[1] = SMB2_OP_POSIX_QUERY_INFO;1233 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);1234 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,1235 in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);1236 if (!rc) {1237 rc = smb311_posix_get_inode_info(&new, full_path,1238 data, sb, xid);1239 }1240 } else {1241 cmds[1] = SMB2_OP_QUERY_INFO;1242 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);1243 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,1244 in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);1245 if (!rc) {1246 rc = cifs_get_inode_info(&new, full_path,1247 data, sb, xid, NULL);1248 }1249 }1250 1251 1252 /*1253 * If CREATE was successful but SMB2_OP_SET_REPARSE failed then1254 * remove the intermediate object created by CREATE. Otherwise1255 * empty object stay on the server when reparse call failed.1256 */1257 if (rc &&1258 out_iov[0].iov_base != NULL && out_buftype[0] != CIFS_NO_BUFFER &&1259 ((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_SUCCESS &&1260 (out_iov[1].iov_base == NULL || out_buftype[1] == CIFS_NO_BUFFER ||1261 ((struct smb2_hdr *)out_iov[1].iov_base)->Status != STATUS_SUCCESS))1262 smb2_unlink(xid, tcon, full_path, cifs_sb, NULL);1263 1264 for (i = 0; i < ARRAY_SIZE(out_buftype); i++)1265 free_rsp_buf(out_buftype[i], out_iov[i].iov_base);1266 1267 return rc ? ERR_PTR(rc) : new;1268}1269 1270int smb2_query_reparse_point(const unsigned int xid,1271 struct cifs_tcon *tcon,1272 struct cifs_sb_info *cifs_sb,1273 const char *full_path,1274 u32 *tag, struct kvec *rsp,1275 int *rsp_buftype)1276{1277 struct cifs_open_parms oparms;1278 struct cifs_open_info_data data = {};1279 struct cifsFileInfo *cfile;1280 struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };1281 int rc;1282 1283 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);1284 1285 cifs_get_readable_path(tcon, full_path, &cfile);1286 oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,1287 FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,1288 FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE);1289 rc = smb2_compound_op(xid, tcon, cifs_sb,1290 full_path, &oparms, &in_iov,1291 &(int){SMB2_OP_GET_REPARSE}, 1,1292 cfile, NULL, NULL, NULL);1293 if (rc)1294 goto out;1295 1296 *tag = data.reparse.tag;1297 *rsp = data.reparse.io.iov;1298 *rsp_buftype = data.reparse.io.buftype;1299 memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov));1300 data.reparse.io.buftype = CIFS_NO_BUFFER;1301out:1302 cifs_free_open_info(&data);1303 return rc;1304}1305