Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc137bf5 authored by Al Viro's avatar Al Viro
Browse files

cifs: build_path_from_dentry() race fix



deal with d_move() races properly; rename_lock read-retry loop,
rcu_read_lock() held while walking to root, d_lock held over
subtraction from namelen and copying the component to stabilize
->d_name.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 3110df80
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
	char dirsep;
	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
	unsigned seq;

	if (direntry == NULL)
		return NULL;  /* not much we can do if dentry is freed and
@@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry)
		dfsplen = 0;
cifs_bp_rename_retry:
	namelen = dfsplen;
	seq = read_seqbegin(&rename_lock);
	rcu_read_lock();
	for (temp = direntry; !IS_ROOT(temp);) {
		namelen += (1 + temp->d_name.len);
		temp = temp->d_parent;
		if (temp == NULL) {
			cERROR(1, "corrupt dentry");
			rcu_read_unlock();
			return NULL;
		}
	}
	rcu_read_unlock();

	full_path = kmalloc(namelen+1, GFP_KERNEL);
	if (full_path == NULL)
		return full_path;
	full_path[namelen] = 0;	/* trailing null */
	rcu_read_lock();
	for (temp = direntry; !IS_ROOT(temp);) {
		spin_lock(&temp->d_lock);
		namelen -= 1 + temp->d_name.len;
		if (namelen < 0) {
			spin_unlock(&temp->d_lock);
			break;
		} else {
			full_path[namelen] = dirsep;
@@ -91,14 +99,17 @@ build_path_from_dentry(struct dentry *direntry)
				temp->d_name.len);
			cFYI(0, "name: %s", full_path + namelen);
		}
		spin_unlock(&temp->d_lock);
		temp = temp->d_parent;
		if (temp == NULL) {
			cERROR(1, "corrupt dentry");
			rcu_read_unlock();
			kfree(full_path);
			return NULL;
		}
	}
	if (namelen != dfsplen) {
	rcu_read_unlock();
	if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
		cERROR(1, "did not end path lookup where expected namelen is %d",
			namelen);
		/* presumably this is only possible if racing with a rename