Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: hppfs_lookup(): don't open-code lookup_one_len() hppfs: fix dentry leak cramfs: get_cramfs_inode() returns ERR_PTR() on failure ufs should use d_splice_alias() fix exofs ->get_parent() ceph analog of cifs build_path_from_dentry() race fix cifs: build_path_from_dentry() race fix
This commit is contained in:
commit
d36c30181c
6 changed files with 54 additions and 45 deletions
|
@ -1438,12 +1438,15 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
|||
struct dentry *temp;
|
||||
char *path;
|
||||
int len, pos;
|
||||
unsigned seq;
|
||||
|
||||
if (dentry == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
retry:
|
||||
len = 0;
|
||||
seq = read_seqbegin(&rename_lock);
|
||||
rcu_read_lock();
|
||||
for (temp = dentry; !IS_ROOT(temp);) {
|
||||
struct inode *inode = temp->d_inode;
|
||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR)
|
||||
|
@ -1455,10 +1458,12 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
|||
len += 1 + temp->d_name.len;
|
||||
temp = temp->d_parent;
|
||||
if (temp == NULL) {
|
||||
rcu_read_unlock();
|
||||
pr_err("build_path corrupt dentry %p\n", dentry);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (len)
|
||||
len--; /* no leading '/' */
|
||||
|
||||
|
@ -1467,9 +1472,12 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
pos = len;
|
||||
path[pos] = 0; /* trailing null */
|
||||
rcu_read_lock();
|
||||
for (temp = dentry; !IS_ROOT(temp) && pos != 0; ) {
|
||||
struct inode *inode = temp->d_inode;
|
||||
struct inode *inode;
|
||||
|
||||
spin_lock(&temp->d_lock);
|
||||
inode = temp->d_inode;
|
||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
||||
dout("build_path path+%d: %p SNAPDIR\n",
|
||||
pos, temp);
|
||||
|
@ -1478,21 +1486,26 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
|||
break;
|
||||
} else {
|
||||
pos -= temp->d_name.len;
|
||||
if (pos < 0)
|
||||
if (pos < 0) {
|
||||
spin_unlock(&temp->d_lock);
|
||||
break;
|
||||
}
|
||||
strncpy(path + pos, temp->d_name.name,
|
||||
temp->d_name.len);
|
||||
}
|
||||
spin_unlock(&temp->d_lock);
|
||||
if (pos)
|
||||
path[--pos] = '/';
|
||||
temp = temp->d_parent;
|
||||
if (temp == NULL) {
|
||||
rcu_read_unlock();
|
||||
pr_err("build_path corrupt dentry\n");
|
||||
kfree(path);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
if (pos != 0) {
|
||||
rcu_read_unlock();
|
||||
if (pos != 0 || read_seqretry(&rename_lock, seq)) {
|
||||
pr_err("build_path did not end path lookup where "
|
||||
"expected, namelen is %d, pos is %d\n", len, pos);
|
||||
/* presumably this is only possible if racing with a
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -37,7 +37,7 @@ static DEFINE_MUTEX(read_mutex);
|
|||
/* These macros may change in future, to provide better st_ino semantics. */
|
||||
#define OFFSET(x) ((x)->i_ino)
|
||||
|
||||
static unsigned long cramino(struct cramfs_inode *cino, unsigned int offset)
|
||||
static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset)
|
||||
{
|
||||
if (!cino->offset)
|
||||
return offset + 1;
|
||||
|
@ -61,7 +61,7 @@ static unsigned long cramino(struct cramfs_inode *cino, unsigned int offset)
|
|||
}
|
||||
|
||||
static struct inode *get_cramfs_inode(struct super_block *sb,
|
||||
struct cramfs_inode *cramfs_inode, unsigned int offset)
|
||||
const struct cramfs_inode *cramfs_inode, unsigned int offset)
|
||||
{
|
||||
struct inode *inode;
|
||||
static struct timespec zerotime;
|
||||
|
@ -317,7 +317,7 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
|
|||
/* Set it all up.. */
|
||||
sb->s_op = &cramfs_ops;
|
||||
root = get_cramfs_inode(sb, &super.root, 0);
|
||||
if (!root)
|
||||
if (IS_ERR(root))
|
||||
goto out;
|
||||
sb->s_root = d_alloc_root(root);
|
||||
if (!sb->s_root) {
|
||||
|
@ -423,6 +423,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|||
static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
struct inode *inode = NULL;
|
||||
int sorted;
|
||||
|
||||
mutex_lock(&read_mutex);
|
||||
|
@ -449,8 +450,8 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
|
|||
|
||||
for (;;) {
|
||||
if (!namelen) {
|
||||
mutex_unlock(&read_mutex);
|
||||
return ERR_PTR(-EIO);
|
||||
inode = ERR_PTR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
if (name[namelen-1])
|
||||
break;
|
||||
|
@ -462,17 +463,18 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
|
|||
if (retval > 0)
|
||||
continue;
|
||||
if (!retval) {
|
||||
struct cramfs_inode entry = *de;
|
||||
mutex_unlock(&read_mutex);
|
||||
d_add(dentry, get_cramfs_inode(dir->i_sb, &entry, dir_off));
|
||||
return NULL;
|
||||
inode = get_cramfs_inode(dir->i_sb, de, dir_off);
|
||||
break;
|
||||
}
|
||||
/* else (retval < 0) */
|
||||
if (sorted)
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&read_mutex);
|
||||
d_add(dentry, NULL);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -913,7 +913,7 @@ struct dentry *exofs_get_parent(struct dentry *child)
|
|||
unsigned long ino = exofs_parent_ino(child);
|
||||
|
||||
if (!ino)
|
||||
return NULL;
|
||||
return ERR_PTR(-ESTALE);
|
||||
|
||||
return d_obtain_alias(exofs_iget(child->d_inode->i_sb, ino));
|
||||
}
|
||||
|
|
|
@ -139,7 +139,8 @@ static int file_removed(struct dentry *dentry, const char *file)
|
|||
static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct dentry *proc_dentry, *new, *parent;
|
||||
struct dentry *proc_dentry, *parent;
|
||||
struct qstr *name = &dentry->d_name;
|
||||
struct inode *inode;
|
||||
int err, deleted;
|
||||
|
||||
|
@ -149,23 +150,9 @@ static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
|||
else if (deleted)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
err = -ENOMEM;
|
||||
parent = HPPFS_I(ino)->proc_dentry;
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
proc_dentry = d_lookup(parent, &dentry->d_name);
|
||||
if (proc_dentry == NULL) {
|
||||
proc_dentry = d_alloc(parent, &dentry->d_name);
|
||||
if (proc_dentry == NULL) {
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
goto out;
|
||||
}
|
||||
new = (*parent->d_inode->i_op->lookup)(parent->d_inode,
|
||||
proc_dentry, NULL);
|
||||
if (new) {
|
||||
dput(proc_dentry);
|
||||
proc_dentry = new;
|
||||
}
|
||||
}
|
||||
proc_dentry = lookup_one_len(name->name, parent, name->len);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
|
||||
if (IS_ERR(proc_dentry))
|
||||
|
@ -174,13 +161,11 @@ static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
|||
err = -ENOMEM;
|
||||
inode = get_inode(ino->i_sb, proc_dentry);
|
||||
if (!inode)
|
||||
goto out_dput;
|
||||
goto out;
|
||||
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
|
||||
out_dput:
|
||||
dput(proc_dentry);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
@ -690,8 +675,10 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
|
|||
struct inode *proc_ino = dentry->d_inode;
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (!inode)
|
||||
if (!inode) {
|
||||
dput(dentry);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (S_ISDIR(dentry->d_inode->i_mode)) {
|
||||
inode->i_op = &hppfs_dir_iops;
|
||||
|
@ -704,7 +691,7 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
|
|||
inode->i_fop = &hppfs_file_fops;
|
||||
}
|
||||
|
||||
HPPFS_I(inode)->proc_dentry = dget(dentry);
|
||||
HPPFS_I(inode)->proc_dentry = dentry;
|
||||
|
||||
inode->i_uid = proc_ino->i_uid;
|
||||
inode->i_gid = proc_ino->i_gid;
|
||||
|
@ -737,7 +724,7 @@ static int hppfs_fill_super(struct super_block *sb, void *d, int silent)
|
|||
sb->s_fs_info = proc_mnt;
|
||||
|
||||
err = -ENOMEM;
|
||||
root_inode = get_inode(sb, proc_mnt->mnt_sb->s_root);
|
||||
root_inode = get_inode(sb, dget(proc_mnt->mnt_sb->s_root));
|
||||
if (!root_inode)
|
||||
goto out_mntput;
|
||||
|
||||
|
|
|
@ -56,16 +56,12 @@ static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, stru
|
|||
|
||||
lock_ufs(dir->i_sb);
|
||||
ino = ufs_inode_by_name(dir, &dentry->d_name);
|
||||
if (ino) {
|
||||
if (ino)
|
||||
inode = ufs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode)) {
|
||||
unlock_ufs(dir->i_sb);
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
}
|
||||
unlock_ufs(dir->i_sb);
|
||||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
return d_splice_alias(inode, dentry);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue