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:
Linus Torvalds 2011-07-18 09:03:15 -07:00
commit d36c30181c
6 changed files with 54 additions and 45 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
} }

View file

@ -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));
} }

View file

@ -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;

View file

@ -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);
} }
/* /*