Commit a97c9bf3 authored by Dave Johnson's avatar Dave Johnson Committed by Linus Torvalds

[PATCH] fix cramfs making duplicate entries in inode cache

Every time cramfs_lookup() is called to lookup and inode for a dentry,
get_cramfs_inode() will allocate a new inode without checking to see if that
inode already exists in the inode cache.

This is fine the first time, but if the dentry cache entry(ies) associated
with that inode are aged out, but the inode entry is not aged out (which can
be quite common if the inode has buffer cache linked to it), cramfs_lookup()
will be called again and another inode will be allocated and added to the
inode cache creating a duplicate in the inode cache.

The big issue here is that the buffers associated with each inode cache entry
are not shared between the duplicates!

The older inode entries are now orphaned as no dentry points to it and won't
be freed until the buffer cache assoicated with them are first freed.  The
newest entry will have to create all new buffer cache for each part of its
file as the old buffer cache is now orphaned as well.

Patch below fixes this by making get_cramfs_inode() use the inode cache before
blindly creating a new entry every time.  This eliminates the duplicate inodes
and duplicate buffer cache.

Cc: Phillip Lougher <phillip@lougher.demon.co.uk>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7f4bde9a
...@@ -39,12 +39,47 @@ static DECLARE_MUTEX(read_mutex); ...@@ -39,12 +39,47 @@ static DECLARE_MUTEX(read_mutex);
#define CRAMINO(x) ((x)->offset?(x)->offset<<2:1) #define CRAMINO(x) ((x)->offset?(x)->offset<<2:1)
#define OFFSET(x) ((x)->i_ino) #define OFFSET(x) ((x)->i_ino)
static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode)
static int cramfs_iget5_test(struct inode *inode, void *opaque)
{
struct cramfs_inode *cramfs_inode = opaque;
if (inode->i_ino != CRAMINO(cramfs_inode))
return 0; /* does not match */
if (inode->i_ino != 1)
return 1;
/* all empty directories, char, block, pipe, and sock, share inode #1 */
if ((inode->i_mode != cramfs_inode->mode) ||
(inode->i_gid != cramfs_inode->gid) ||
(inode->i_uid != cramfs_inode->uid))
return 0; /* does not match */
if ((S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) &&
(inode->i_rdev != old_decode_dev(cramfs_inode->size)))
return 0; /* does not match */
return 1; /* matches */
}
static int cramfs_iget5_set(struct inode *inode, void *opaque)
{
struct cramfs_inode *cramfs_inode = opaque;
inode->i_ino = CRAMINO(cramfs_inode);
return 0;
}
static struct inode *get_cramfs_inode(struct super_block *sb,
struct cramfs_inode * cramfs_inode)
{ {
struct inode * inode = new_inode(sb); struct inode *inode = iget5_locked(sb, CRAMINO(cramfs_inode),
cramfs_iget5_test, cramfs_iget5_set,
cramfs_inode);
static struct timespec zerotime; static struct timespec zerotime;
if (inode) { if (inode && (inode->i_state & I_NEW)) {
inode->i_mode = cramfs_inode->mode; inode->i_mode = cramfs_inode->mode;
inode->i_uid = cramfs_inode->uid; inode->i_uid = cramfs_inode->uid;
inode->i_size = cramfs_inode->size; inode->i_size = cramfs_inode->size;
...@@ -58,7 +93,6 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod ...@@ -58,7 +93,6 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod
but it's the best we can do without reading the directory but it's the best we can do without reading the directory
contents. 1 yields the right result in GNU find, even contents. 1 yields the right result in GNU find, even
without -noleaf option. */ without -noleaf option. */
insert_inode_hash(inode);
if (S_ISREG(inode->i_mode)) { if (S_ISREG(inode->i_mode)) {
inode->i_fop = &generic_ro_fops; inode->i_fop = &generic_ro_fops;
inode->i_data.a_ops = &cramfs_aops; inode->i_data.a_ops = &cramfs_aops;
...@@ -74,6 +108,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod ...@@ -74,6 +108,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod
init_special_inode(inode, inode->i_mode, init_special_inode(inode, inode->i_mode,
old_decode_dev(cramfs_inode->size)); old_decode_dev(cramfs_inode->size));
} }
unlock_new_inode(inode);
} }
return inode; return inode;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment