Fix casesensitivity=insensitive deadlock
When casesensitivity=insensitive is set for the file system, we can deadlock in a rename if the user uses different case for each path. For example rename("A/some-file.txt", "a/some-file.txt"). The simple test for this is: 1. mkdir some-dir in a ZFS file system 2. touch some-dir/some-file.txt 3. mv Some-dir/some-file.txt some-dir/some-other-file.txt This last request deadlocks trying to relock the i_mutex on the inode for the parent directory. The solution is to use d_add_ci in zpl_lookup if we are on a file system that has the casesensitivity=insensitive attribute set. This patch checks if we are working on a case insensitive file system and if so, allocates storage for the case insensitive name and passes it to zfs_lookup and then calls d_add_ci instead of d_splice_alias. The performance impact seems to be minimal even though we have introduced a kmalloc and kfree in the lookup path. The problem was found when running Microsoft's FSCT against Samba on top of ZFS On Linux. Signed-off-by: Richard Sharpe <realrichardsharpe@gmail.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #4136
This commit is contained in:
parent
d41e763c72
commit
a99c845fdc
|
@ -44,13 +44,24 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||||
struct inode *ip;
|
struct inode *ip;
|
||||||
int error;
|
int error;
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
|
pathname_t *ppn = NULL;
|
||||||
|
pathname_t pn;
|
||||||
|
zfs_sb_t *zsb = dentry->d_sb->s_fs_info;
|
||||||
|
|
||||||
if (dlen(dentry) > ZFS_MAXNAMELEN)
|
if (dlen(dentry) > ZFS_MAXNAMELEN)
|
||||||
return (ERR_PTR(-ENAMETOOLONG));
|
return (ERR_PTR(-ENAMETOOLONG));
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
error = -zfs_lookup(dir, dname(dentry), &ip, 0, cr, NULL, NULL);
|
|
||||||
|
/* If we are a case insensitive fs, we need the real name */
|
||||||
|
if (zsb->z_case == ZFS_CASE_INSENSITIVE) {
|
||||||
|
pn.pn_bufsize = ZFS_MAXNAMELEN;
|
||||||
|
pn.pn_buf = kmem_zalloc(ZFS_MAXNAMELEN, KM_SLEEP);
|
||||||
|
ppn = &pn;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = -zfs_lookup(dir, dname(dentry), &ip, 0, cr, NULL, ppn);
|
||||||
spl_fstrans_unmark(cookie);
|
spl_fstrans_unmark(cookie);
|
||||||
ASSERT3S(error, <=, 0);
|
ASSERT3S(error, <=, 0);
|
||||||
crfree(cr);
|
crfree(cr);
|
||||||
|
@ -63,13 +74,30 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
if (ppn)
|
||||||
|
kmem_free(pn.pn_buf, ZFS_MAXNAMELEN);
|
||||||
if (error == -ENOENT)
|
if (error == -ENOENT)
|
||||||
return (d_splice_alias(NULL, dentry));
|
return (d_splice_alias(NULL, dentry));
|
||||||
else
|
else
|
||||||
return (ERR_PTR(error));
|
return (ERR_PTR(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are case insensitive, call the correct function
|
||||||
|
* to install the name.
|
||||||
|
*/
|
||||||
|
if (ppn) {
|
||||||
|
struct dentry *new_dentry;
|
||||||
|
struct qstr ci_name;
|
||||||
|
|
||||||
|
ci_name.name = pn.pn_buf;
|
||||||
|
ci_name.len = strlen(pn.pn_buf);
|
||||||
|
new_dentry = d_add_ci(dentry, ip, &ci_name);
|
||||||
|
kmem_free(pn.pn_buf, ZFS_MAXNAMELEN);
|
||||||
|
return (new_dentry);
|
||||||
|
} else {
|
||||||
return (d_splice_alias(ip, dentry));
|
return (d_splice_alias(ip, dentry));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in New Issue