Commit 27b2eaa1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.14-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "Four CIFS/SMB3 Fixes, all for stable, two relating to deferred close,
  and one for the 'modefromsid' mount option (when 'idsfromsid' not
  specified)"

* tag '5.14-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Call close synchronously during unlink/rename/lease break.
  cifs: Handle race conditions during rename
  cifs: use the correct max-length for dentry_path_raw()
  cifs: create sd context must be a multiple of 8
parents a83ed225 9e992755
...@@ -1611,6 +1611,11 @@ struct dfs_info3_param { ...@@ -1611,6 +1611,11 @@ struct dfs_info3_param {
int ttl; int ttl;
}; };
struct file_list {
struct list_head list;
struct cifsFileInfo *cfile;
};
/* /*
* common struct for holding inode info when searching for or updating an * common struct for holding inode info when searching for or updating an
* inode with new info * inode with new info
......
...@@ -100,7 +100,7 @@ build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, ...@@ -100,7 +100,7 @@ build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0; pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;
s = dentry_path_raw(direntry, page, PAGE_SIZE); s = dentry_path_raw(direntry, page, PATH_MAX);
if (IS_ERR(s)) if (IS_ERR(s))
return s; return s;
if (!s[1]) // for root we want "", not "/" if (!s[1]) // for root we want "", not "/"
......
...@@ -4847,17 +4847,6 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4847,17 +4847,6 @@ void cifs_oplock_break(struct work_struct *work)
cifs_dbg(VFS, "Push locks rc = %d\n", rc); cifs_dbg(VFS, "Push locks rc = %d\n", rc);
oplock_break_ack: oplock_break_ack:
/*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
* not bother sending an oplock release if session to server still is
* disconnected since oplock already released by the server
*/
if (!cfile->oplock_break_cancelled) {
rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
}
/* /*
* When oplock break is received and there are no active * When oplock break is received and there are no active
* file handles but cached, then schedule deferred close immediately. * file handles but cached, then schedule deferred close immediately.
...@@ -4865,17 +4854,27 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4865,17 +4854,27 @@ void cifs_oplock_break(struct work_struct *work)
*/ */
spin_lock(&CIFS_I(inode)->deferred_lock); spin_lock(&CIFS_I(inode)->deferred_lock);
is_deferred = cifs_is_deferred_close(cfile, &dclose); is_deferred = cifs_is_deferred_close(cfile, &dclose);
spin_unlock(&CIFS_I(inode)->deferred_lock);
if (is_deferred && if (is_deferred &&
cfile->deferred_close_scheduled && cfile->deferred_close_scheduled &&
delayed_work_pending(&cfile->deferred)) { delayed_work_pending(&cfile->deferred)) {
/* if (cancel_delayed_work(&cfile->deferred)) {
* If there is no pending work, mod_delayed_work queues new work. _cifsFileInfo_put(cfile, false, false);
* So, Increase the ref count to avoid use-after-free. goto oplock_break_done;
*/ }
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
cifsFileInfo_get(cfile);
} }
spin_unlock(&CIFS_I(inode)->deferred_lock); /*
* releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do
* not bother sending an oplock release if session to server still is
* disconnected since oplock already released by the server
*/
if (!cfile->oplock_break_cancelled) {
rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
}
oplock_break_done:
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
cifs_done_oplock_break(cinode); cifs_done_oplock_break(cinode);
} }
......
...@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
goto unlink_out; goto unlink_out;
} }
cifs_close_all_deferred_files(tcon); cifs_close_deferred_file(CIFS_I(inode));
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = CIFSPOSIXDelFile(xid, tcon, full_path, rc = CIFSPOSIXDelFile(xid, tcon, full_path,
...@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, ...@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
FILE_UNIX_BASIC_INFO *info_buf_target; FILE_UNIX_BASIC_INFO *info_buf_target;
unsigned int xid; unsigned int xid;
int rc, tmprc; int rc, tmprc;
int retry_count = 0;
if (flags & ~RENAME_NOREPLACE) if (flags & ~RENAME_NOREPLACE)
return -EINVAL; return -EINVAL;
...@@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, ...@@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
goto cifs_rename_exit; goto cifs_rename_exit;
} }
cifs_close_all_deferred_files(tcon); cifs_close_deferred_file(CIFS_I(d_inode(source_dentry)));
if (d_inode(target_dentry) != NULL)
cifs_close_deferred_file(CIFS_I(d_inode(target_dentry)));
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name); to_name);
if (rc == -EACCES) {
while (retry_count < 3) {
cifs_close_all_deferred_files(tcon);
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name);
if (rc != -EACCES)
break;
retry_count++;
}
}
/* /*
* No-replace is the natural behavior for CIFS, so skip unlink hacks. * No-replace is the natural behavior for CIFS, so skip unlink hacks.
*/ */
......
...@@ -723,13 +723,31 @@ void ...@@ -723,13 +723,31 @@ void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{ {
struct cifsFileInfo *cfile = NULL; struct cifsFileInfo *cfile = NULL;
struct cifs_deferred_close *dclose; struct file_list *tmp_list, *tmp_next_list;
struct list_head file_head;
if (cifs_inode == NULL)
return;
INIT_LIST_HEAD(&file_head);
spin_lock(&cifs_inode->open_file_lock);
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
spin_lock(&cifs_inode->deferred_lock); if (delayed_work_pending(&cfile->deferred)) {
if (cifs_is_deferred_close(cfile, &dclose)) if (cancel_delayed_work(&cfile->deferred)) {
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
spin_unlock(&cifs_inode->deferred_lock); if (tmp_list == NULL)
continue;
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
}
}
spin_unlock(&cifs_inode->open_file_lock);
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
kfree(tmp_list);
} }
} }
...@@ -738,20 +756,30 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) ...@@ -738,20 +756,30 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{ {
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct list_head *tmp; struct list_head *tmp;
struct file_list *tmp_list, *tmp_next_list;
struct list_head file_head;
INIT_LIST_HEAD(&file_head);
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) { list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo, tlist); cfile = list_entry(tmp, struct cifsFileInfo, tlist);
if (delayed_work_pending(&cfile->deferred)) { if (delayed_work_pending(&cfile->deferred)) {
/* if (cancel_delayed_work(&cfile->deferred)) {
* If there is no pending work, mod_delayed_work queues new work. tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
* So, Increase the ref count to avoid use-after-free. if (tmp_list == NULL)
*/ continue;
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0)) tmp_list->cfile = cfile;
cifsFileInfo_get(cfile); list_add_tail(&tmp_list->list, &file_head);
}
} }
} }
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
kfree(tmp_list);
}
} }
/* parses DFS refferal V3 structure /* parses DFS refferal V3 structure
......
...@@ -2426,7 +2426,7 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) ...@@ -2426,7 +2426,7 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
memcpy(aclptr, &acl, sizeof(struct cifs_acl)); memcpy(aclptr, &acl, sizeof(struct cifs_acl));
buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd);
*len = ptr - (__u8 *)buf; *len = roundup(ptr - (__u8 *)buf, 8);
return buf; return buf;
} }
......
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