| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362 |
- // SPDX-License-Identifier: LGPL-2.1
- /*
- *
- * Copyright (C) International Business Machines Corp., 2002, 2011
- * Etersoft, 2012
- * Author(s): Pavel Shilovsky (pshilovsky@samba.org),
- * Steve French (sfrench@us.ibm.com)
- *
- */
- #include <linux/fs.h>
- #include <linux/stat.h>
- #include <linux/slab.h>
- #include <linux/pagemap.h>
- #include <asm/div64.h>
- #include "cifsfs.h"
- #include "cifspdu.h"
- #include "cifsglob.h"
- #include "cifsproto.h"
- #include "cifs_debug.h"
- #include "cifs_fs_sb.h"
- #include "cifs_unicode.h"
- #include "fscache.h"
- #include "smb2glob.h"
- #include "smb2pdu.h"
- #include "smb2proto.h"
- #include "cached_dir.h"
- #include "../common/smb2status.h"
- static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
- {
- struct reparse_data_buffer *buf;
- struct smb2_ioctl_rsp *io = iov->iov_base;
- u32 off, count, len;
- count = le32_to_cpu(io->OutputCount);
- off = le32_to_cpu(io->OutputOffset);
- if (check_add_overflow(off, count, &len) || len > iov->iov_len)
- return ERR_PTR(-EIO);
- buf = (struct reparse_data_buffer *)((u8 *)io + off);
- len = sizeof(*buf);
- if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
- return ERR_PTR(-EIO);
- return buf;
- }
- static inline __u32 file_create_options(struct dentry *dentry)
- {
- struct cifsInodeInfo *ci;
- if (dentry) {
- ci = CIFS_I(d_inode(dentry));
- if (ci->cifsAttrs & ATTR_REPARSE)
- return OPEN_REPARSE_POINT;
- }
- return 0;
- }
- /* Parse owner and group from SMB3.1.1 POSIX query info */
- static int parse_posix_sids(struct cifs_open_info_data *data,
- struct kvec *rsp_iov)
- {
- struct smb2_query_info_rsp *qi = rsp_iov->iov_base;
- unsigned int out_len = le32_to_cpu(qi->OutputBufferLength);
- unsigned int qi_len = sizeof(data->posix_fi);
- int owner_len, group_len;
- u8 *sidsbuf, *sidsbuf_end;
- if (out_len <= qi_len)
- return -EINVAL;
- sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
- sidsbuf_end = sidsbuf + out_len - qi_len;
- owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
- if (owner_len == -1)
- return -EINVAL;
- memcpy(&data->posix_owner, sidsbuf, owner_len);
- group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end);
- if (group_len == -1)
- return -EINVAL;
- memcpy(&data->posix_group, sidsbuf + owner_len, group_len);
- return 0;
- }
- struct wsl_query_ea {
- __le32 next;
- __u8 name_len;
- __u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];
- } __packed;
- #define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
- static const struct wsl_query_ea wsl_query_eas[] = {
- { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
- { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
- { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
- { .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
- };
- static int check_wsl_eas(struct kvec *rsp_iov)
- {
- struct smb2_file_full_ea_info *ea;
- struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
- unsigned long addr;
- u32 outlen, next;
- u16 vlen;
- u8 nlen;
- u8 *end;
- outlen = le32_to_cpu(rsp->OutputBufferLength);
- if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
- outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
- return -EINVAL;
- ea = (void *)((u8 *)rsp_iov->iov_base +
- le16_to_cpu(rsp->OutputBufferOffset));
- end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
- for (;;) {
- if ((u8 *)ea > end - sizeof(*ea))
- return -EINVAL;
- nlen = ea->ea_name_length;
- vlen = le16_to_cpu(ea->ea_value_length);
- if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
- (u8 *)ea + nlen + 1 + vlen > end)
- return -EINVAL;
- switch (vlen) {
- case 4:
- if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
- strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
- strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
- return -EINVAL;
- break;
- case 8:
- if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
- return -EINVAL;
- break;
- case 0:
- if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
- !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
- !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
- !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
- break;
- fallthrough;
- default:
- return -EINVAL;
- }
- next = le32_to_cpu(ea->next_entry_offset);
- if (!next)
- break;
- if (!IS_ALIGNED(next, 4) ||
- check_add_overflow((unsigned long)ea, next, &addr))
- return -EINVAL;
- ea = (void *)addr;
- }
- return 0;
- }
- /*
- * note: If cfile is passed, the reference to it is dropped here.
- * So make sure that you do not reuse cfile after return from this func.
- *
- * If passing @out_iov and @out_buftype, ensure to make them both large enough
- * (>= 3) to hold all compounded responses. Caller is also responsible for
- * freeing them up with free_rsp_buf().
- */
- static int 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)
- {
- struct smb2_query_info_rsp *qi_rsp = NULL;
- struct smb2_compound_vars *vars = NULL;
- __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
- struct cifs_open_info_data *idata;
- struct cifs_ses *ses = tcon->ses;
- struct reparse_data_buffer *rbuf;
- struct TCP_Server_Info *server;
- int resp_buftype[MAX_COMPOUND];
- int retries = 0, cur_sleep = 1;
- __u8 delete_pending[8] = {1,};
- struct kvec *rsp_iov, *iov;
- struct inode *inode = NULL;
- __le16 *utf16_path = NULL;
- struct smb_rqst *rqst;
- unsigned int size[2];
- struct cifs_fid fid;
- int num_rqst = 0, i;
- unsigned int len;
- int tmp_rc, rc;
- int flags = 0;
- void *data[2];
- replay_again:
- /* reinitialize for possible replay */
- flags = 0;
- oplock = SMB2_OPLOCK_LEVEL_NONE;
- num_rqst = 0;
- server = cifs_pick_channel(ses);
- vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
- if (vars == NULL) {
- rc = -ENOMEM;
- goto out;
- }
- rqst = &vars->rqst[0];
- rsp_iov = &vars->rsp_iov[0];
- if (smb3_encryption_required(tcon))
- flags |= CIFS_TRANSFORM_REQ;
- for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
- resp_buftype[i] = CIFS_NO_BUFFER;
- /* We already have a handle so we can skip the open */
- if (cfile)
- goto after_open;
- /* Open */
- utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
- if (!utf16_path) {
- rc = -ENOMEM;
- goto finished;
- }
- /* if there is an existing lease, reuse it */
- /*
- * note: files with hardlinks cause unexpected behaviour. As per MS-SMB2,
- * lease keys are associated with the filepath. We are maintaining lease keys
- * with the inode on the client. If the file has hardlinks, it is possible
- * that the lease for a file be reused for an operation on its hardlink or
- * vice versa.
- * As a workaround, send request using an existing lease key and if the server
- * returns STATUS_INVALID_PARAMETER, which maps to EINVAL, send the request
- * again without the lease.
- */
- if (dentry) {
- inode = d_inode(dentry);
- if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
- oplock = SMB2_OPLOCK_LEVEL_LEASE;
- server->ops->get_lease_key(inode, &fid);
- }
- }
- vars->oparms = *oparms;
- vars->oparms.fid = &fid;
- rqst[num_rqst].rq_iov = &vars->open_iov[0];
- rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
- rc = SMB2_open_init(tcon, server,
- &rqst[num_rqst], &oplock, &vars->oparms,
- utf16_path);
- kfree(utf16_path);
- if (rc)
- goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- after_open:
- num_rqst++;
- rc = 0;
- for (i = 0; i < num_cmds; i++) {
- /* Operation */
- switch (cmds[i]) {
- case SMB2_OP_QUERY_INFO:
- rqst[num_rqst].rq_iov = &vars->qi_iov;
- rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_query_info_compound_enter(xid, tcon->tid,
- ses->Suid, full_path);
- break;
- case SMB2_OP_POSIX_QUERY_INFO:
- rqst[num_rqst].rq_iov = &vars->qi_iov;
- rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- /* TBD: fix following to allow for longer SIDs */
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo *) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo *) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
- ses->Suid, full_path);
- break;
- case SMB2_OP_DELETE:
- trace_smb3_delete_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_MKDIR:
- /*
- * Directories are created through parameters in the
- * SMB2_open() call.
- */
- trace_smb3_mkdir_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_RMDIR:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
- rqst[num_rqst].rq_nvec = 1;
- size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
- data[0] = &delete_pending[0];
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- if (rc)
- goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_rmdir_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_SET_EOF:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
- rqst[num_rqst].rq_nvec = 1;
- size[0] = in_iov[i].iov_len;
- data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_SET_INFO:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
- rqst[num_rqst].rq_nvec = 1;
- size[0] = in_iov[i].iov_len;
- data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_set_info_compound_enter(xid, tcon->tid,
- ses->Suid, full_path);
- break;
- case SMB2_OP_RENAME:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
- rqst[num_rqst].rq_nvec = 2;
- len = in_iov[i].iov_len;
- vars->rename_info.ReplaceIfExists = 1;
- vars->rename_info.RootDirectory = 0;
- vars->rename_info.FileNameLength = cpu_to_le32(len);
- size[0] = sizeof(struct smb2_file_rename_info);
- data[0] = &vars->rename_info;
- size[1] = len + 2 /* null */;
- data[1] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_HARDLINK:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
- rqst[num_rqst].rq_nvec = 2;
- len = in_iov[i].iov_len;
- vars->link_info.ReplaceIfExists = 0;
- vars->link_info.RootDirectory = 0;
- vars->link_info.FileNameLength = cpu_to_le32(len);
- size[0] = sizeof(struct smb2_file_link_info);
- data[0] = &vars->link_info;
- size[1] = len + 2 /* null */;
- data[1] = in_iov[i].iov_base;
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_LINK_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- if (rc)
- goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
- break;
- case SMB2_OP_SET_REPARSE:
- rqst[num_rqst].rq_iov = vars->io_iov;
- rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
- ses->Suid, full_path);
- break;
- case SMB2_OP_GET_REPARSE:
- rqst[num_rqst].rq_iov = vars->io_iov;
- rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
- ses->Suid, full_path);
- break;
- case SMB2_OP_QUERY_WSL_EA:
- rqst[num_rqst].rq_iov = &vars->ea_iov;
- rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
- goto finished;
- }
- num_rqst++;
- break;
- default:
- cifs_dbg(VFS, "Invalid command\n");
- rc = -EINVAL;
- }
- }
- if (rc)
- goto finished;
- /* We already have a handle so we can skip the close */
- if (cfile)
- goto after_close;
- /* Close */
- flags |= CIFS_CP_CREATE_CLOSE_OP;
- rqst[num_rqst].rq_iov = &vars->close_iov;
- rqst[num_rqst].rq_nvec = 1;
- rc = SMB2_close_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, false);
- smb2_set_related(&rqst[num_rqst]);
- if (rc)
- goto finished;
- after_close:
- num_rqst++;
- if (cfile) {
- if (retries)
- for (i = 1; i < num_rqst - 2; i++)
- smb2_set_replay(server, &rqst[i]);
- rc = compound_send_recv(xid, ses, server,
- flags, num_rqst - 2,
- &rqst[1], &resp_buftype[1],
- &rsp_iov[1]);
- } else {
- if (retries)
- for (i = 0; i < num_rqst; i++)
- smb2_set_replay(server, &rqst[i]);
- rc = compound_send_recv(xid, ses, server,
- flags, num_rqst,
- rqst, resp_buftype,
- rsp_iov);
- }
- finished:
- num_rqst = 0;
- SMB2_open_free(&rqst[num_rqst++]);
- if (rc == -EREMCHG) {
- pr_warn_once("server share %s deleted\n", tcon->tree_name);
- tcon->need_reconnect = true;
- }
- tmp_rc = rc;
- for (i = 0; i < num_cmds; i++) {
- char *buf = rsp_iov[i + 1].iov_base;
- if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
- rc = server->ops->map_error(buf, false);
- else
- rc = tmp_rc;
- switch (cmds[i]) {
- case SMB2_OP_QUERY_INFO:
- idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = false;
- if (rc == 0 && cfile && cfile->symlink_target) {
- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
- if (!idata->symlink_target)
- rc = -ENOMEM;
- }
- if (rc == 0) {
- qi_rsp = (struct smb2_query_info_rsp *)
- rsp_iov[i + 1].iov_base;
- rc = smb2_validate_and_copy_iov(
- le16_to_cpu(qi_rsp->OutputBufferOffset),
- le32_to_cpu(qi_rsp->OutputBufferLength),
- &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
- }
- SMB2_query_info_free(&rqst[num_rqst++]);
- if (rc)
- trace_smb3_query_info_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- else
- trace_smb3_query_info_compound_done(xid, tcon->tid,
- ses->Suid);
- break;
- case SMB2_OP_POSIX_QUERY_INFO:
- idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = true;
- if (rc == 0 && cfile && cfile->symlink_target) {
- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
- if (!idata->symlink_target)
- rc = -ENOMEM;
- }
- if (rc == 0) {
- qi_rsp = (struct smb2_query_info_rsp *)
- rsp_iov[i + 1].iov_base;
- rc = smb2_validate_and_copy_iov(
- le16_to_cpu(qi_rsp->OutputBufferOffset),
- le32_to_cpu(qi_rsp->OutputBufferLength),
- &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
- (char *)&idata->posix_fi);
- }
- if (rc == 0)
- rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
- SMB2_query_info_free(&rqst[num_rqst++]);
- if (rc)
- trace_smb3_posix_query_info_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- else
- trace_smb3_posix_query_info_compound_done(xid, tcon->tid,
- ses->Suid);
- break;
- case SMB2_OP_DELETE:
- if (rc)
- trace_smb3_delete_err(xid, tcon->tid, ses->Suid, rc);
- else {
- /*
- * If dentry (hence, inode) is NULL, lease break is going to
- * take care of degrading leases on handles for deleted files.
- */
- if (inode)
- cifs_mark_open_handles_for_deleted_file(inode, full_path);
- trace_smb3_delete_done(xid, tcon->tid, ses->Suid);
- }
- break;
- case SMB2_OP_MKDIR:
- if (rc)
- trace_smb3_mkdir_err(xid, tcon->tid, ses->Suid, rc);
- else
- trace_smb3_mkdir_done(xid, tcon->tid, ses->Suid);
- break;
- case SMB2_OP_HARDLINK:
- if (rc)
- trace_smb3_hardlink_err(xid, tcon->tid, ses->Suid, rc);
- else
- trace_smb3_hardlink_done(xid, tcon->tid, ses->Suid);
- SMB2_set_info_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_RENAME:
- if (rc)
- trace_smb3_rename_err(xid, tcon->tid, ses->Suid, rc);
- else
- trace_smb3_rename_done(xid, tcon->tid, ses->Suid);
- SMB2_set_info_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_RMDIR:
- if (rc)
- trace_smb3_rmdir_err(xid, tcon->tid, ses->Suid, rc);
- else
- trace_smb3_rmdir_done(xid, tcon->tid, ses->Suid);
- SMB2_set_info_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_SET_EOF:
- if (rc)
- trace_smb3_set_eof_err(xid, tcon->tid, ses->Suid, rc);
- else
- trace_smb3_set_eof_done(xid, tcon->tid, ses->Suid);
- SMB2_set_info_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_SET_INFO:
- if (rc)
- trace_smb3_set_info_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- else
- trace_smb3_set_info_compound_done(xid, tcon->tid,
- ses->Suid);
- SMB2_set_info_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_SET_REPARSE:
- if (rc) {
- trace_smb3_set_reparse_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- } else {
- trace_smb3_set_reparse_compound_done(xid, tcon->tid,
- ses->Suid);
- }
- SMB2_ioctl_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_GET_REPARSE:
- if (!rc) {
- iov = &rsp_iov[i + 1];
- idata = in_iov[i].iov_base;
- idata->reparse.io.iov = *iov;
- idata->reparse.io.buftype = resp_buftype[i + 1];
- idata->contains_posix_file_info = false; /* BB VERIFY */
- rbuf = reparse_buf_ptr(iov);
- if (IS_ERR(rbuf)) {
- rc = PTR_ERR(rbuf);
- trace_smb3_get_reparse_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- } else {
- idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
- trace_smb3_get_reparse_compound_done(xid, tcon->tid,
- ses->Suid);
- }
- memset(iov, 0, sizeof(*iov));
- resp_buftype[i + 1] = CIFS_NO_BUFFER;
- } else {
- trace_smb3_get_reparse_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- }
- SMB2_ioctl_free(&rqst[num_rqst++]);
- break;
- case SMB2_OP_QUERY_WSL_EA:
- if (!rc) {
- idata = in_iov[i].iov_base;
- idata->contains_posix_file_info = false;
- qi_rsp = rsp_iov[i + 1].iov_base;
- data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
- size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
- rc = check_wsl_eas(&rsp_iov[i + 1]);
- if (!rc) {
- memcpy(idata->wsl.eas, data[0], size[0]);
- idata->wsl.eas_len = size[0];
- }
- }
- if (!rc) {
- trace_smb3_query_wsl_ea_compound_done(xid, tcon->tid,
- ses->Suid);
- } else {
- trace_smb3_query_wsl_ea_compound_err(xid, tcon->tid,
- ses->Suid, rc);
- }
- SMB2_query_info_free(&rqst[num_rqst++]);
- break;
- }
- }
- SMB2_close_free(&rqst[num_rqst]);
- rc = tmp_rc;
- num_cmds += 2;
- if (out_iov && out_buftype) {
- memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
- memcpy(out_buftype, resp_buftype,
- num_cmds * sizeof(*out_buftype));
- } else {
- for (i = 0; i < num_cmds; i++)
- free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
- }
- num_cmds -= 2; /* correct num_cmds as there could be a retry */
- kfree(vars);
- if (is_replayable_error(rc) &&
- smb2_should_replay(tcon, &retries, &cur_sleep))
- goto replay_again;
- out:
- if (cfile)
- cifsFileInfo_put(cfile);
- return rc;
- }
- static int parse_create_response(struct cifs_open_info_data *data,
- struct cifs_sb_info *cifs_sb,
- const char *full_path,
- const struct kvec *iov)
- {
- struct smb2_create_rsp *rsp = iov->iov_base;
- bool reparse_point = false;
- u32 tag = 0;
- int rc = 0;
- switch (rsp->hdr.Status) {
- case STATUS_IO_REPARSE_TAG_NOT_HANDLED:
- reparse_point = true;
- break;
- case STATUS_STOPPED_ON_SYMLINK:
- rc = smb2_parse_symlink_response(cifs_sb, iov,
- full_path,
- &data->symlink_target);
- if (rc)
- return rc;
- tag = IO_REPARSE_TAG_SYMLINK;
- reparse_point = true;
- break;
- case STATUS_SUCCESS:
- reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
- break;
- }
- data->reparse_point = reparse_point;
- data->reparse.tag = tag;
- return rc;
- }
- /* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
- static bool ea_unsupported(int *cmds, int num_cmds,
- struct kvec *out_iov, int *out_buftype)
- {
- int i;
- if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
- return false;
- for (i = 1; i < num_cmds - 1; i++) {
- struct smb2_hdr *hdr = out_iov[i].iov_base;
- if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
- hdr->Status != STATUS_SUCCESS)
- return false;
- }
- return true;
- }
- static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
- {
- int i;
- for (i = 0; i < count; i++) {
- free_rsp_buf(buftype[i], iovs[i].iov_base);
- memset(&iovs[i], 0, sizeof(*iovs));
- buftype[i] = CIFS_NO_BUFFER;
- }
- }
- int 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)
- {
- struct kvec in_iov[3], out_iov[5] = {};
- struct cached_fid *cfid = NULL;
- struct cifs_open_parms oparms;
- struct cifsFileInfo *cfile;
- __u32 create_options = 0;
- int out_buftype[5] = {};
- struct smb2_hdr *hdr;
- int num_cmds = 0;
- int cmds[3];
- bool islink;
- int rc, rc2;
- data->adjust_tz = false;
- data->reparse_point = false;
- /*
- * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX.
- * Create SMB2_query_posix_info worker function to do non-compounded
- * query when we already have an open file handle for this. For now this
- * is fast enough (always using the compounded version).
- */
- if (!tcon->posix_extensions) {
- if (*full_path) {
- rc = -ENOENT;
- } else {
- rc = open_cached_dir(xid, tcon, full_path,
- cifs_sb, false, &cfid);
- }
- /* If it is a root and its handle is cached then use it */
- if (!rc) {
- if (cfid->file_all_info_is_valid) {
- memcpy(&data->fi, &cfid->file_all_info,
- sizeof(data->fi));
- } else {
- rc = SMB2_query_info(xid, tcon,
- cfid->fid.persistent_fid,
- cfid->fid.volatile_fid,
- &data->fi);
- }
- close_cached_dir(cfid);
- return rc;
- }
- cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
- } else {
- cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
- }
- in_iov[0].iov_base = data;
- in_iov[0].iov_len = sizeof(*data);
- in_iov[1] = in_iov[0];
- in_iov[2] = in_iov[0];
- cifs_get_readable_path(tcon, full_path, &cfile);
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
- FILE_OPEN, create_options, ACL_NO_MODE);
- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
- &oparms, in_iov, cmds, num_cmds,
- cfile, out_iov, out_buftype, NULL);
- hdr = out_iov[0].iov_base;
- /*
- * If first iov is unset, then SMB session was dropped or we've got a
- * cached open file (@cfile).
- */
- if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
- goto out;
- switch (rc) {
- case 0:
- rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
- break;
- case -EOPNOTSUPP:
- /*
- * BB TODO: When support for special files added to Samba
- * re-verify this path.
- */
- rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
- if (rc || !data->reparse_point)
- goto out;
- /*
- * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
- * response.
- */
- if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
- cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
- if (!tcon->posix_extensions)
- cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
- FILE_READ_ATTRIBUTES |
- FILE_READ_EA | SYNCHRONIZE,
- FILE_OPEN, create_options |
- OPEN_REPARSE_POINT, ACL_NO_MODE);
- cifs_get_readable_path(tcon, full_path, &cfile);
- free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
- &oparms, in_iov, cmds, num_cmds,
- cfile, out_iov, out_buftype, NULL);
- if (rc && ea_unsupported(cmds, num_cmds,
- out_iov, out_buftype)) {
- if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
- data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
- rc = 0;
- else
- rc = -EOPNOTSUPP;
- }
- break;
- case -EREMOTE:
- break;
- default:
- if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
- break;
- rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
- full_path, &islink);
- if (rc2) {
- rc = rc2;
- goto out;
- }
- if (islink)
- rc = -EREMOTE;
- }
- out:
- free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
- return rc;
- }
- 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)
- {
- struct cifs_open_parms oparms;
- oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
- FILE_CREATE, CREATE_NOT_FILE, mode);
- return smb2_compound_op(xid, tcon, cifs_sb,
- name, &oparms, NULL,
- &(int){SMB2_OP_MKDIR}, 1,
- NULL, NULL, NULL, NULL);
- }
- void
- smb2_mkdir_setinfo(struct inode *inode, const char *name,
- struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
- const unsigned int xid)
- {
- struct cifs_open_parms oparms;
- FILE_BASIC_INFO data = {};
- struct cifsInodeInfo *cifs_i;
- struct cifsFileInfo *cfile;
- struct kvec in_iov;
- u32 dosattrs;
- int tmprc;
- in_iov.iov_base = &data;
- in_iov.iov_len = sizeof(data);
- cifs_i = CIFS_I(inode);
- dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
- data.Attributes = cpu_to_le32(dosattrs);
- cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
- oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
- FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);
- tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
- &oparms, &in_iov,
- &(int){SMB2_OP_SET_INFO}, 1,
- cfile, NULL, NULL, NULL);
- if (tmprc == 0)
- cifs_i->cifsAttrs = dosattrs;
- }
- int
- smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
- struct cifs_sb_info *cifs_sb)
- {
- struct cifs_open_parms oparms;
- drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
- oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE,
- FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);
- return smb2_compound_op(xid, tcon, cifs_sb,
- name, &oparms, NULL,
- &(int){SMB2_OP_RMDIR}, 1,
- NULL, NULL, NULL, NULL);
- }
- int
- smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
- struct cifs_sb_info *cifs_sb, struct dentry *dentry)
- {
- struct cifs_open_parms oparms;
- oparms = CIFS_OPARMS(cifs_sb, tcon, name,
- DELETE, FILE_OPEN,
- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
- ACL_NO_MODE);
- int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
- NULL, &(int){SMB2_OP_DELETE}, 1,
- NULL, NULL, NULL, dentry);
- if (rc == -EINVAL) {
- cifs_dbg(FYI, "invalid lease key, resending request without lease");
- rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
- NULL, &(int){SMB2_OP_DELETE}, 1,
- NULL, NULL, NULL, NULL);
- }
- return rc;
- }
- static int 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)
- {
- struct cifs_open_parms oparms;
- struct kvec in_iov;
- __le16 *smb2_to_name = NULL;
- int rc;
- smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
- if (smb2_to_name == NULL) {
- rc = -ENOMEM;
- goto smb2_rename_path;
- }
- in_iov.iov_base = smb2_to_name;
- in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
- oparms = CIFS_OPARMS(cifs_sb, tcon, from_name, access, FILE_OPEN,
- create_options, ACL_NO_MODE);
- rc = smb2_compound_op(xid, tcon, cifs_sb, from_name,
- &oparms, &in_iov, &command, 1,
- cfile, NULL, NULL, dentry);
- smb2_rename_path:
- kfree(smb2_to_name);
- return rc;
- }
- int 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)
- {
- struct cifsFileInfo *cfile;
- __u32 co = file_create_options(source_dentry);
- drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
- cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
- int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
- co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
- if (rc == -EINVAL) {
- cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, from_name,
- FIND_WR_WITH_DELETE, &cfile);
- rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
- co, DELETE, SMB2_OP_RENAME, cfile, NULL);
- }
- return rc;
- }
- int 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)
- {
- __u32 co = file_create_options(source_dentry);
- return smb2_set_path_attr(xid, tcon, from_name, to_name,
- cifs_sb, co, FILE_READ_ATTRIBUTES,
- SMB2_OP_HARDLINK, NULL, NULL);
- }
- 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)
- {
- struct cifs_open_parms oparms;
- struct cifsFileInfo *cfile;
- struct kvec in_iov;
- __le64 eof = cpu_to_le64(size);
- int rc;
- in_iov.iov_base = &eof;
- in_iov.iov_len = sizeof(eof);
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
- FILE_OPEN, 0, ACL_NO_MODE);
- rc = smb2_compound_op(xid, tcon, cifs_sb,
- full_path, &oparms, &in_iov,
- &(int){SMB2_OP_SET_EOF}, 1,
- cfile, NULL, NULL, dentry);
- if (rc == -EINVAL) {
- cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
- rc = smb2_compound_op(xid, tcon, cifs_sb,
- full_path, &oparms, &in_iov,
- &(int){SMB2_OP_SET_EOF}, 1,
- cfile, NULL, NULL, NULL);
- }
- return rc;
- }
- int
- smb2_set_file_info(struct inode *inode, const char *full_path,
- FILE_BASIC_INFO *buf, const unsigned int xid)
- {
- struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct cifsFileInfo *cfile = NULL;
- struct cifs_open_parms oparms;
- struct tcon_link *tlink;
- struct cifs_tcon *tcon;
- int rc = 0;
- tlink = cifs_sb_tlink(cifs_sb);
- if (IS_ERR(tlink))
- return PTR_ERR(tlink);
- tcon = tlink_tcon(tlink);
- if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
- (buf->LastWriteTime == 0) && (buf->ChangeTime == 0)) {
- if (buf->Attributes == 0)
- goto out; /* would be a no op, no sense sending this */
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
- }
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
- FILE_OPEN, 0, ACL_NO_MODE);
- rc = smb2_compound_op(xid, tcon, cifs_sb,
- full_path, &oparms, &in_iov,
- &(int){SMB2_OP_SET_INFO}, 1,
- cfile, NULL, NULL, NULL);
- out:
- cifs_put_tlink(tlink);
- return rc;
- }
- struct inode *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)
- {
- struct cifs_open_parms oparms;
- struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
- struct cifsFileInfo *cfile;
- struct inode *new = NULL;
- int out_buftype[4] = {};
- struct kvec out_iov[4] = {};
- struct kvec in_iov[2];
- int cmds[2];
- int rc;
- int i;
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
- SYNCHRONIZE | DELETE |
- FILE_READ_ATTRIBUTES |
- FILE_WRITE_ATTRIBUTES,
- FILE_CREATE,
- (directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,
- ACL_NO_MODE);
- if (xattr_iov)
- oparms.ea_cctx = xattr_iov;
- cmds[0] = SMB2_OP_SET_REPARSE;
- in_iov[0] = *reparse_iov;
- in_iov[1].iov_base = data;
- in_iov[1].iov_len = sizeof(*data);
- if (tcon->posix_extensions) {
- cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
- in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
- if (!rc) {
- rc = smb311_posix_get_inode_info(&new, full_path,
- data, sb, xid);
- }
- } else {
- cmds[1] = SMB2_OP_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
- in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
- if (!rc) {
- rc = cifs_get_inode_info(&new, full_path,
- data, sb, xid, NULL);
- }
- }
- /*
- * If CREATE was successful but SMB2_OP_SET_REPARSE failed then
- * remove the intermediate object created by CREATE. Otherwise
- * empty object stay on the server when reparse call failed.
- */
- if (rc &&
- out_iov[0].iov_base != NULL && out_buftype[0] != CIFS_NO_BUFFER &&
- ((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_SUCCESS &&
- (out_iov[1].iov_base == NULL || out_buftype[1] == CIFS_NO_BUFFER ||
- ((struct smb2_hdr *)out_iov[1].iov_base)->Status != STATUS_SUCCESS))
- smb2_unlink(xid, tcon, full_path, cifs_sb, NULL);
- for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
- free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
- return rc ? ERR_PTR(rc) : new;
- }
- int 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)
- {
- struct cifs_open_parms oparms;
- struct cifs_open_info_data data = {};
- struct cifsFileInfo *cfile;
- struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };
- int rc;
- cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
- cifs_get_readable_path(tcon, full_path, &cfile);
- oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
- FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
- FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE);
- rc = smb2_compound_op(xid, tcon, cifs_sb,
- full_path, &oparms, &in_iov,
- &(int){SMB2_OP_GET_REPARSE}, 1,
- cfile, NULL, NULL, NULL);
- if (rc)
- goto out;
- *tag = data.reparse.tag;
- *rsp = data.reparse.io.iov;
- *rsp_buftype = data.reparse.io.buftype;
- memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov));
- data.reparse.io.buftype = CIFS_NO_BUFFER;
- out:
- cifs_free_open_info(&data);
- return rc;
- }
|