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

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
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1611,6 +1611,11 @@ struct dfs_info3_param {
	int ttl;
};

struct file_list {
	struct list_head list;
	struct cifsFileInfo *cfile;
};

/*
 * common struct for holding inode info when searching for or updating an
 * inode with new info
+1 −1
Original line number Diff line number Diff line
@@ -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)
		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))
		return s;
	if (!s[1])	// for root we want "", not "/"
+17 −18
Original line number Diff line number Diff line
@@ -4847,17 +4847,6 @@ void cifs_oplock_break(struct work_struct *work)
		cifs_dbg(VFS, "Push locks rc = %d\n", rc);

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
	 * file handles but cached, then schedule deferred close immediately.
@@ -4865,17 +4854,27 @@ void cifs_oplock_break(struct work_struct *work)
	 */
	spin_lock(&CIFS_I(inode)->deferred_lock);
	is_deferred = cifs_is_deferred_close(cfile, &dclose);
	spin_unlock(&CIFS_I(inode)->deferred_lock);
	if (is_deferred &&
	    cfile->deferred_close_scheduled &&
	    delayed_work_pending(&cfile->deferred)) {
		if (cancel_delayed_work(&cfile->deferred)) {
			_cifsFileInfo_put(cfile, false, false);
			goto oplock_break_done;
		}
	}
	/*
		 * If there is no pending work, mod_delayed_work queues new work.
		 * So, Increase the ref count to avoid use-after-free.
	 * 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 (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
			cifsFileInfo_get(cfile);
	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);
	}
	spin_unlock(&CIFS_I(inode)->deferred_lock);
oplock_break_done:
	_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
	cifs_done_oplock_break(cinode);
}
+17 −2
Original line number Diff line number Diff line
@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
		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 &
				le64_to_cpu(tcon->fsUnixInfo.Capability))) {
		rc = CIFSPOSIXDelFile(xid, tcon, full_path,
@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
	FILE_UNIX_BASIC_INFO *info_buf_target;
	unsigned int xid;
	int rc, tmprc;
	int retry_count = 0;

	if (flags & ~RENAME_NOREPLACE)
		return -EINVAL;
@@ -2113,9 +2114,23 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
		goto cifs_rename_exit;
	}

	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,
			    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.
+39 −11
Original line number Diff line number Diff line
@@ -723,13 +723,31 @@ void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{
	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) {
		spin_lock(&cifs_inode->deferred_lock);
		if (cifs_is_deferred_close(cfile, &dclose))
			mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
		spin_unlock(&cifs_inode->deferred_lock);
		if (delayed_work_pending(&cfile->deferred)) {
			if (cancel_delayed_work(&cfile->deferred)) {
				tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
				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)
{
	struct cifsFileInfo *cfile;
	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);
	list_for_each(tmp, &tcon->openFileList) {
		cfile = list_entry(tmp, struct cifsFileInfo, tlist);
		if (delayed_work_pending(&cfile->deferred)) {
			/*
			 * If there is no pending work, mod_delayed_work queues new work.
			 * So, Increase the ref count to avoid use-after-free.
			 */
			if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
				cifsFileInfo_get(cfile);
			if (cancel_delayed_work(&cfile->deferred)) {
				tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
				if (tmp_list == NULL)
					continue;
				tmp_list->cfile = cfile;
				list_add_tail(&tmp_list->list, &file_head);
			}
		}
	}
	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
Loading