Commit 8de9e86c authored by Ronnie Sahlberg's avatar Ronnie Sahlberg Committed by Steve French

cifs: create a helper to find a writeable handle by path name

rename() takes a path for old_file and in SMB2 we used to just create
a compound for create(old_path)/rename/close().
If we already have a writable handle we can avoid the create() and close()
altogether and just use the existing handle.

For this situation, as we avoid doing the create()
we also avoid triggering an oplock break for the existing handle.
Signed-off-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 31ebdc11
...@@ -137,6 +137,8 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); ...@@ -137,6 +137,8 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only, bool fsuid_only,
struct cifsFileInfo **ret_file); struct cifsFileInfo **ret_file);
extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server); extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
extern int decode_negTokenInit(unsigned char *security_blob, int length, extern int decode_negTokenInit(unsigned char *security_blob, int length,
......
...@@ -125,7 +125,7 @@ build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix) ...@@ -125,7 +125,7 @@ build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix)
} }
rcu_read_unlock(); rcu_read_unlock();
full_path = kmalloc(namelen+1, GFP_KERNEL); full_path = kmalloc(namelen+1, GFP_ATOMIC);
if (full_path == NULL) if (full_path == NULL)
return full_path; return full_path;
full_path[namelen] = 0; /* trailing null */ full_path[namelen] = 0; /* trailing null */
......
...@@ -1973,6 +1973,41 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only) ...@@ -1973,6 +1973,41 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only)
return cfile; return cfile;
} }
int
cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file)
{
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode;
char *full_path;
*ret_file = NULL;
spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo,
tlist);
full_path = build_path_from_dentry(cfile->dentry);
if (full_path == NULL) {
spin_unlock(&tcon->open_file_lock);
return -ENOMEM;
}
if (strcmp(full_path, name)) {
kfree(full_path);
continue;
}
kfree(full_path);
cinode = CIFS_I(d_inode(cfile->dentry));
spin_unlock(&tcon->open_file_lock);
return cifs_get_writable_file(cinode, 0, ret_file);
}
spin_unlock(&tcon->open_file_lock);
return -ENOENT;
}
static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
......
...@@ -51,7 +51,8 @@ static int ...@@ -51,7 +51,8 @@ static int
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
__u32 desired_access, __u32 create_disposition, __u32 desired_access, __u32 create_disposition,
__u32 create_options, void *ptr, int command) __u32 create_options, void *ptr, int command,
struct cifsFileInfo *cfile)
{ {
int rc; int rc;
__le16 *utf16_path = NULL; __le16 *utf16_path = NULL;
...@@ -83,10 +84,16 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -83,10 +84,16 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov)); memset(rsp_iov, 0, sizeof(rsp_iov));
/* We already have a handle so we can skip the open */
if (cfile)
goto after_open;
/* Open */ /* Open */
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path) if (!utf16_path) {
return -ENOMEM; rc = -ENOMEM;
goto finished;
}
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.desired_access = desired_access; oparms.desired_access = desired_access;
...@@ -106,7 +113,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -106,7 +113,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst++]); smb2_set_next_command(tcon, &rqst[num_rqst]);
after_open:
num_rqst++;
rc = 0;
/* Operation */ /* Operation */
switch (command) { switch (command) {
...@@ -210,14 +220,23 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -210,14 +220,23 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[1] = len + 2 /* null */; size[1] = len + 2 /* null */;
data[1] = (__le16 *)ptr; data[1] = (__le16 *)ptr;
rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, if (cfile)
COMPOUND_FID, current->tgid, rc = SMB2_set_info_init(tcon, &rqst[num_rqst],
FILE_RENAME_INFORMATION, 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, &rqst[num_rqst],
COMPOUND_FID, COMPOUND_FID,
current->tgid, FILE_RENAME_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size); SMB2_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst]);
}
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]); num_rqst++;
smb2_set_related(&rqst[num_rqst++]);
trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_HARDLINK: case SMB2_OP_HARDLINK:
...@@ -254,20 +273,36 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -254,20 +273,36 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) if (rc)
goto finished; goto finished;
/* We already have a handle so we can skip the close */
if (cfile)
goto after_close;
/* Close */ /* Close */
memset(&close_iov, 0, sizeof(close_iov)); memset(&close_iov, 0, sizeof(close_iov));
rqst[num_rqst].rq_iov = close_iov; rqst[num_rqst].rq_iov = close_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID, rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID,
COMPOUND_FID); COMPOUND_FID);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst]);
if (rc) if (rc)
goto finished; goto finished;
after_close:
rc = compound_send_recv(xid, ses, flags, num_rqst, rqst, num_rqst++;
resp_buftype, rsp_iov);
if (cfile) {
cifsFileInfo_put(cfile);
cfile = NULL;
rc = compound_send_recv(xid, ses, flags, num_rqst - 2,
&rqst[1], &resp_buftype[1],
&rsp_iov[1]);
} else
rc = compound_send_recv(xid, ses, flags, num_rqst,
rqst, resp_buftype,
rsp_iov);
finished: finished:
if (cfile)
cifsFileInfo_put(cfile);
SMB2_open_free(&rqst[0]); SMB2_open_free(&rqst[0]);
switch (command) { switch (command) {
case SMB2_OP_QUERY_INFO: case SMB2_OP_QUERY_INFO:
...@@ -404,7 +439,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -404,7 +439,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
smb2_data, SMB2_OP_QUERY_INFO); smb2_data, SMB2_OP_QUERY_INFO, NULL);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
*symlink = true; *symlink = true;
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
...@@ -413,7 +448,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -413,7 +448,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES, FILE_OPEN,
create_options, smb2_data, create_options, smb2_data,
SMB2_OP_QUERY_INFO); SMB2_OP_QUERY_INFO, NULL);
} }
if (rc) if (rc)
goto out; goto out;
...@@ -430,7 +465,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -430,7 +465,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, return smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR, NULL);
} }
void void
...@@ -449,7 +484,8 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, ...@@ -449,7 +484,8 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
data.Attributes = cpu_to_le32(dosattrs); data.Attributes = cpu_to_le32(dosattrs);
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO,
NULL);
if (tmprc == 0) if (tmprc == 0)
cifs_i->cifsAttrs = dosattrs; cifs_i->cifsAttrs = dosattrs;
} }
...@@ -460,7 +496,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -460,7 +496,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_NOT_FILE, CREATE_NOT_FILE,
NULL, SMB2_OP_RMDIR); NULL, SMB2_OP_RMDIR, NULL);
} }
int int
...@@ -469,13 +505,14 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -469,13 +505,14 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
NULL, SMB2_OP_DELETE); NULL, SMB2_OP_DELETE, NULL);
} }
static int static int
smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
const char *from_name, const char *to_name, const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb, __u32 access, int command) struct cifs_sb_info *cifs_sb, __u32 access, int command,
struct cifsFileInfo *cfile)
{ {
__le16 *smb2_to_name = NULL; __le16 *smb2_to_name = NULL;
int rc; int rc;
...@@ -486,7 +523,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -486,7 +523,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
goto smb2_rename_path; goto smb2_rename_path;
} }
rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
FILE_OPEN, 0, smb2_to_name, command); FILE_OPEN, 0, smb2_to_name, command, cfile);
smb2_rename_path: smb2_rename_path:
kfree(smb2_to_name); kfree(smb2_to_name);
return rc; return rc;
...@@ -497,8 +534,12 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -497,8 +534,12 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
const char *from_name, const char *to_name, const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, struct cifsFileInfo *cfile;
DELETE, SMB2_OP_RENAME);
cifs_get_writable_path(tcon, from_name, &cfile);
return smb2_set_path_attr(xid, tcon, from_name, to_name,
cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
} }
int int
...@@ -507,7 +548,8 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -507,7 +548,8 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK); FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
NULL);
} }
int int
...@@ -519,7 +561,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -519,7 +561,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
return smb2_compound_op(xid, tcon, cifs_sb, full_path, return smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_DATA, FILE_OPEN, 0, &eof, FILE_WRITE_DATA, FILE_OPEN, 0, &eof,
SMB2_OP_SET_EOF); SMB2_OP_SET_EOF, NULL);
} }
int int
...@@ -541,7 +583,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, ...@@ -541,7 +583,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO); SMB2_OP_SET_INFO, NULL);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment