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
7cf2ffa020
commit
c5d0287011
@ -44,13 +44,24 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
struct inode *ip;
|
||||
int error;
|
||||
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)
|
||||
return (ERR_PTR(-ENAMETOOLONG));
|
||||
|
||||
crhold(cr);
|
||||
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);
|
||||
ASSERT3S(error, <=, 0);
|
||||
crfree(cr);
|
||||
@ -63,13 +74,30 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
if (error) {
|
||||
if (ppn)
|
||||
kmem_free(pn.pn_buf, ZFS_MAXNAMELEN);
|
||||
if (error == -ENOENT)
|
||||
return (d_splice_alias(NULL, dentry));
|
||||
else
|
||||
return (ERR_PTR(error));
|
||||
}
|
||||
|
||||
return (d_splice_alias(ip, dentry));
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user