mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
smb: client: handle unlink(2) of files open by different clients
In order to identify whether a certain file is open by a different client, start the unlink process by sending a compound request of CREATE(DELETE_ON_CLOSE) + CLOSE with only FILE_SHARE_DELETE bit set in smb2_create_req::ShareAccess. If the file is currently open, then the server will fail the request with STATUS_SHARING_VIOLATION, in which case we'll map it to -EBUSY, so __cifs_unlink() will fall back to silly-rename the file. This fixes the following case where open(O_CREAT) fails with -ENOENT (STATUS_DELETE_PENDING) due to file still open by a different client. * Before patch $ mount.cifs //srv/share /mnt/1 -o ...,nosharesock $ mount.cifs //srv/share /mnt/2 -o ...,nosharesock $ cd /mnt/1 $ touch foo $ exec 3<>foo $ cd /mnt/2 $ rm foo $ touch foo touch: cannot touch 'foo': No such file or directory $ exec 3>&- * After patch $ mount.cifs //srv/share /mnt/1 -o ...,nosharesock $ mount.cifs //srv/share /mnt/2 -o ...,nosharesock $ cd /mnt/1 $ touch foo $ exec 3<>foo $ cd /mnt/2 $ rm foo $ touch foo $ exec 3>&- Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Reviewed-by: David Howells <dhowells@redhat.com> Cc: Frank Sorenson <sorenson@redhat.com> Cc: linux-cifs@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
07e27ad163
commit
1cf9f2a6a5
1 changed files with 89 additions and 12 deletions
|
|
@ -1175,23 +1175,92 @@ int
|
||||||
smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||||
struct cifs_sb_info *cifs_sb, struct dentry *dentry)
|
struct cifs_sb_info *cifs_sb, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
|
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||||
|
__le16 *utf16_path __free(kfree) = NULL;
|
||||||
|
int retries = 0, cur_sleep = 1;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct cifs_open_parms oparms;
|
struct cifs_open_parms oparms;
|
||||||
|
struct smb2_create_req *creq;
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
|
struct smb_rqst rqst[2];
|
||||||
|
struct kvec rsp_iov[2];
|
||||||
|
struct kvec close_iov;
|
||||||
|
int resp_buftype[2];
|
||||||
|
struct cifs_fid fid;
|
||||||
|
int flags = 0;
|
||||||
|
__u8 oplock;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (dentry)
|
utf16_path = cifs_convert_path_to_utf16(name, cifs_sb);
|
||||||
inode = d_inode(dentry);
|
if (!utf16_path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE,
|
if (smb3_encryption_required(tcon))
|
||||||
FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE);
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
|
again:
|
||||||
NULL, &(int){SMB2_OP_UNLINK},
|
oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
1, NULL, NULL, NULL, dentry);
|
server = cifs_pick_channel(tcon->ses);
|
||||||
if (rc == -EINVAL) {
|
|
||||||
cifs_dbg(FYI, "invalid lease key, resending request without lease");
|
memset(rqst, 0, sizeof(rqst));
|
||||||
rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
|
memset(resp_buftype, 0, sizeof(resp_buftype));
|
||||||
NULL, &(int){SMB2_OP_UNLINK},
|
memset(rsp_iov, 0, sizeof(rsp_iov));
|
||||||
1, NULL, NULL, NULL, NULL);
|
|
||||||
|
rqst[0].rq_iov = open_iov;
|
||||||
|
rqst[0].rq_nvec = ARRAY_SIZE(open_iov);
|
||||||
|
|
||||||
|
oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE | FILE_READ_ATTRIBUTES,
|
||||||
|
FILE_OPEN, CREATE_DELETE_ON_CLOSE |
|
||||||
|
OPEN_REPARSE_POINT, ACL_NO_MODE);
|
||||||
|
oparms.fid = &fid;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = SMB2_open_init(tcon, server,
|
||||||
|
&rqst[0], &oplock, &oparms, utf16_path);
|
||||||
|
if (rc)
|
||||||
|
goto err_free;
|
||||||
|
smb2_set_next_command(tcon, &rqst[0]);
|
||||||
|
creq = rqst[0].rq_iov[0].iov_base;
|
||||||
|
creq->ShareAccess = FILE_SHARE_DELETE_LE;
|
||||||
|
|
||||||
|
rqst[1].rq_iov = &close_iov;
|
||||||
|
rqst[1].rq_nvec = 1;
|
||||||
|
|
||||||
|
rc = SMB2_close_init(tcon, server, &rqst[1],
|
||||||
|
COMPOUND_FID, COMPOUND_FID, false);
|
||||||
|
smb2_set_related(&rqst[1]);
|
||||||
|
if (rc)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
if (retries) {
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(rqst); i++)
|
||||||
|
smb2_set_replay(server, &rqst[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = compound_send_recv(xid, tcon->ses, server, flags,
|
||||||
|
ARRAY_SIZE(rqst), rqst,
|
||||||
|
resp_buftype, rsp_iov);
|
||||||
|
SMB2_open_free(&rqst[0]);
|
||||||
|
SMB2_close_free(&rqst[1]);
|
||||||
|
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||||
|
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||||
|
|
||||||
|
if (is_replayable_error(rc) &&
|
||||||
|
smb2_should_replay(tcon, &retries, &cur_sleep))
|
||||||
|
goto again;
|
||||||
|
|
||||||
|
/* Retry compound request without lease */
|
||||||
|
if (rc == -EINVAL && dentry) {
|
||||||
|
dentry = NULL;
|
||||||
|
retries = 0;
|
||||||
|
cur_sleep = 1;
|
||||||
|
goto again;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If dentry (hence, inode) is NULL, lease break is going to
|
* If dentry (hence, inode) is NULL, lease break is going to
|
||||||
|
|
@ -1199,6 +1268,14 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||||
*/
|
*/
|
||||||
if (!rc && inode)
|
if (!rc && inode)
|
||||||
cifs_mark_open_handles_for_deleted_file(inode, name);
|
cifs_mark_open_handles_for_deleted_file(inode, name);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
SMB2_open_free(&rqst[0]);
|
||||||
|
SMB2_close_free(&rqst[1]);
|
||||||
|
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||||
|
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue