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;
|
struct dentry *temp;
|
||||||
char *path;
|
char *path;
|
||||||
int len, pos;
|
int len, pos;
|
||||||
|
unsigned seq;
|
||||||
|
|
||||||
if (dentry == NULL)
|
if (dentry == NULL)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
len = 0;
|
len = 0;
|
||||||
|
seq = read_seqbegin(&rename_lock);
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = dentry; !IS_ROOT(temp);) {
|
for (temp = dentry; !IS_ROOT(temp);) {
|
||||||
struct inode *inode = temp->d_inode;
|
struct inode *inode = temp->d_inode;
|
||||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR)
|
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;
|
len += 1 + temp->d_name.len;
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
|
rcu_read_unlock();
|
||||||
pr_err("build_path corrupt dentry %p\n", dentry);
|
pr_err("build_path corrupt dentry %p\n", dentry);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
if (len)
|
if (len)
|
||||||
len--; /* no leading '/' */
|
len--; /* no leading '/' */
|
||||||
|
|
||||||
|
@ -1467,9 +1472,12 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
pos = len;
|
pos = len;
|
||||||
path[pos] = 0; /* trailing null */
|
path[pos] = 0; /* trailing null */
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = dentry; !IS_ROOT(temp) && pos != 0; ) {
|
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) {
|
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
||||||
dout("build_path path+%d: %p SNAPDIR\n",
|
dout("build_path path+%d: %p SNAPDIR\n",
|
||||||
pos, temp);
|
pos, temp);
|
||||||
|
@ -1478,21 +1486,26 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
pos -= temp->d_name.len;
|
pos -= temp->d_name.len;
|
||||||
if (pos < 0)
|
if (pos < 0) {
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
strncpy(path + pos, temp->d_name.name,
|
strncpy(path + pos, temp->d_name.name,
|
||||||
temp->d_name.len);
|
temp->d_name.len);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
if (pos)
|
if (pos)
|
||||||
path[--pos] = '/';
|
path[--pos] = '/';
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
|
rcu_read_unlock();
|
||||||
pr_err("build_path corrupt dentry\n");
|
pr_err("build_path corrupt dentry\n");
|
||||||
kfree(path);
|
kfree(path);
|
||||||
return ERR_PTR(-EINVAL);
|
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 "
|
pr_err("build_path did not end path lookup where "
|
||||||
"expected, namelen is %d, pos is %d\n", len, pos);
|
"expected, namelen is %d, pos is %d\n", len, pos);
|
||||||
/* presumably this is only possible if racing with a
|
/* presumably this is only possible if racing with a
|
||||||
|
|
|
@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
|
||||||
char dirsep;
|
char dirsep;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
|
||||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||||
|
unsigned seq;
|
||||||
|
|
||||||
if (direntry == NULL)
|
if (direntry == NULL)
|
||||||
return NULL; /* not much we can do if dentry is freed and
|
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;
|
dfsplen = 0;
|
||||||
cifs_bp_rename_retry:
|
cifs_bp_rename_retry:
|
||||||
namelen = dfsplen;
|
namelen = dfsplen;
|
||||||
|
seq = read_seqbegin(&rename_lock);
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = direntry; !IS_ROOT(temp);) {
|
for (temp = direntry; !IS_ROOT(temp);) {
|
||||||
namelen += (1 + temp->d_name.len);
|
namelen += (1 + temp->d_name.len);
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
cERROR(1, "corrupt dentry");
|
cERROR(1, "corrupt dentry");
|
||||||
|
rcu_read_unlock();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
full_path = kmalloc(namelen+1, GFP_KERNEL);
|
full_path = kmalloc(namelen+1, GFP_KERNEL);
|
||||||
if (full_path == NULL)
|
if (full_path == NULL)
|
||||||
return full_path;
|
return full_path;
|
||||||
full_path[namelen] = 0; /* trailing null */
|
full_path[namelen] = 0; /* trailing null */
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = direntry; !IS_ROOT(temp);) {
|
for (temp = direntry; !IS_ROOT(temp);) {
|
||||||
|
spin_lock(&temp->d_lock);
|
||||||
namelen -= 1 + temp->d_name.len;
|
namelen -= 1 + temp->d_name.len;
|
||||||
if (namelen < 0) {
|
if (namelen < 0) {
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
full_path[namelen] = dirsep;
|
full_path[namelen] = dirsep;
|
||||||
|
@ -91,14 +99,17 @@ build_path_from_dentry(struct dentry *direntry)
|
||||||
temp->d_name.len);
|
temp->d_name.len);
|
||||||
cFYI(0, "name: %s", full_path + namelen);
|
cFYI(0, "name: %s", full_path + namelen);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
cERROR(1, "corrupt dentry");
|
cERROR(1, "corrupt dentry");
|
||||||
|
rcu_read_unlock();
|
||||||
kfree(full_path);
|
kfree(full_path);
|
||||||
return NULL;
|
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",
|
cERROR(1, "did not end path lookup where expected namelen is %d",
|
||||||
namelen);
|
namelen);
|
||||||
/* presumably this is only possible if racing with a rename
|
/* 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. */
|
/* These macros may change in future, to provide better st_ino semantics. */
|
||||||
#define OFFSET(x) ((x)->i_ino)
|
#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)
|
if (!cino->offset)
|
||||||
return offset + 1;
|
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,
|
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;
|
struct inode *inode;
|
||||||
static struct timespec zerotime;
|
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.. */
|
/* Set it all up.. */
|
||||||
sb->s_op = &cramfs_ops;
|
sb->s_op = &cramfs_ops;
|
||||||
root = get_cramfs_inode(sb, &super.root, 0);
|
root = get_cramfs_inode(sb, &super.root, 0);
|
||||||
if (!root)
|
if (IS_ERR(root))
|
||||||
goto out;
|
goto out;
|
||||||
sb->s_root = d_alloc_root(root);
|
sb->s_root = d_alloc_root(root);
|
||||||
if (!sb->s_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)
|
static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||||
{
|
{
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
|
struct inode *inode = NULL;
|
||||||
int sorted;
|
int sorted;
|
||||||
|
|
||||||
mutex_lock(&read_mutex);
|
mutex_lock(&read_mutex);
|
||||||
|
@ -449,8 +450,8 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!namelen) {
|
if (!namelen) {
|
||||||
mutex_unlock(&read_mutex);
|
inode = ERR_PTR(-EIO);
|
||||||
return ERR_PTR(-EIO);
|
goto out;
|
||||||
}
|
}
|
||||||
if (name[namelen-1])
|
if (name[namelen-1])
|
||||||
break;
|
break;
|
||||||
|
@ -462,17 +463,18 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s
|
||||||
if (retval > 0)
|
if (retval > 0)
|
||||||
continue;
|
continue;
|
||||||
if (!retval) {
|
if (!retval) {
|
||||||
struct cramfs_inode entry = *de;
|
inode = get_cramfs_inode(dir->i_sb, de, dir_off);
|
||||||
mutex_unlock(&read_mutex);
|
break;
|
||||||
d_add(dentry, get_cramfs_inode(dir->i_sb, &entry, dir_off));
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
/* else (retval < 0) */
|
/* else (retval < 0) */
|
||||||
if (sorted)
|
if (sorted)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
mutex_unlock(&read_mutex);
|
mutex_unlock(&read_mutex);
|
||||||
d_add(dentry, NULL);
|
if (IS_ERR(inode))
|
||||||
|
return ERR_CAST(inode);
|
||||||
|
d_add(dentry, inode);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -913,7 +913,7 @@ struct dentry *exofs_get_parent(struct dentry *child)
|
||||||
unsigned long ino = exofs_parent_ino(child);
|
unsigned long ino = exofs_parent_ino(child);
|
||||||
|
|
||||||
if (!ino)
|
if (!ino)
|
||||||
return NULL;
|
return ERR_PTR(-ESTALE);
|
||||||
|
|
||||||
return d_obtain_alias(exofs_iget(child->d_inode->i_sb, ino));
|
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,
|
static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||||
struct nameidata *nd)
|
struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct dentry *proc_dentry, *new, *parent;
|
struct dentry *proc_dentry, *parent;
|
||||||
|
struct qstr *name = &dentry->d_name;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int err, deleted;
|
int err, deleted;
|
||||||
|
|
||||||
|
@ -149,23 +150,9 @@ static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||||
else if (deleted)
|
else if (deleted)
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
parent = HPPFS_I(ino)->proc_dentry;
|
parent = HPPFS_I(ino)->proc_dentry;
|
||||||
mutex_lock(&parent->d_inode->i_mutex);
|
mutex_lock(&parent->d_inode->i_mutex);
|
||||||
proc_dentry = d_lookup(parent, &dentry->d_name);
|
proc_dentry = lookup_one_len(name->name, parent, name->len);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&parent->d_inode->i_mutex);
|
mutex_unlock(&parent->d_inode->i_mutex);
|
||||||
|
|
||||||
if (IS_ERR(proc_dentry))
|
if (IS_ERR(proc_dentry))
|
||||||
|
@ -174,13 +161,11 @@ static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
inode = get_inode(ino->i_sb, proc_dentry);
|
inode = get_inode(ino->i_sb, proc_dentry);
|
||||||
if (!inode)
|
if (!inode)
|
||||||
goto out_dput;
|
goto out;
|
||||||
|
|
||||||
d_add(dentry, inode);
|
d_add(dentry, inode);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
out_dput:
|
|
||||||
dput(proc_dentry);
|
|
||||||
out:
|
out:
|
||||||
return ERR_PTR(err);
|
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 *proc_ino = dentry->d_inode;
|
||||||
struct inode *inode = new_inode(sb);
|
struct inode *inode = new_inode(sb);
|
||||||
|
|
||||||
if (!inode)
|
if (!inode) {
|
||||||
|
dput(dentry);
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
if (S_ISDIR(dentry->d_inode->i_mode)) {
|
if (S_ISDIR(dentry->d_inode->i_mode)) {
|
||||||
inode->i_op = &hppfs_dir_iops;
|
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;
|
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_uid = proc_ino->i_uid;
|
||||||
inode->i_gid = proc_ino->i_gid;
|
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;
|
sb->s_fs_info = proc_mnt;
|
||||||
|
|
||||||
err = -ENOMEM;
|
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)
|
if (!root_inode)
|
||||||
goto out_mntput;
|
goto out_mntput;
|
||||||
|
|
||||||
|
|
|
@ -56,16 +56,12 @@ static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, stru
|
||||||
|
|
||||||
lock_ufs(dir->i_sb);
|
lock_ufs(dir->i_sb);
|
||||||
ino = ufs_inode_by_name(dir, &dentry->d_name);
|
ino = ufs_inode_by_name(dir, &dentry->d_name);
|
||||||
if (ino) {
|
if (ino)
|
||||||
inode = ufs_iget(dir->i_sb, 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);
|
unlock_ufs(dir->i_sb);
|
||||||
d_add(dentry, inode);
|
if (IS_ERR(inode))
|
||||||
return NULL;
|
return ERR_CAST(inode);
|
||||||
|
return d_splice_alias(inode, dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue