linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Christoph Hellwig <hch@lst.de>
To: Anton Altaparmakov <aia21@cam.ac.uk>
Cc: Christoph Hellwig <hch@lst.de>,
	ntfs-dev <linux-ntfs-dev@lists.sourceforge.net>,
	fsdevel <linux-fsdevel@vger.kernel.org>
Subject: Re: fishy ->put_inode usage in ntfs
Date: Sun, 13 Feb 2005 17:35:12 +0100	[thread overview]
Message-ID: <20050213163512.GA3686@lst.de> (raw)
In-Reply-To: <1108047572.12000.24.camel@imp.csi.cam.ac.uk>

On Thu, Feb 10, 2005 at 02:59:32PM +0000, Anton Altaparmakov wrote:
> On Thu, 2005-02-10 at 15:50 +0100, Christoph Hellwig wrote:
> > On Thu, Feb 10, 2005 at 02:48:26PM +0000, Anton Altaparmakov wrote:
> > > If the igrab() were not done, it would be possible for clear_inode to be
> > > called on the 'parent' inode whilst at the same time one or more attr
> > > inodes (belonging to this 'parent') are in use and Bad Things(TM) would
> > > happen...
> > 
> > What bad thing specificly?  If there's shared information we should
> > probably refcount them separately.
> 
> Each attr inode stores a pointer to its parent inode in NTFS_I(struct
> inode *vi)->ext.base_ntfs_ino.  This pointer would point to random
> memory if clear_inode is called on the parent whilst the attr inode is
> still in use.

Ok.  Al Viro suggested to put a spinlock around the bmp_ino pointer and
then igrab the master inode when accessing it, iput it when done.

Below is a rather half-backed patch to implement the suggestion.  It
compiles, but I'm pretty sure I introduced various bugs.

Also the new spinlock isn't nice, it'd probably be better to reuse some
other lock for this single field.


--- 1.85/fs/ntfs/dir.c	2004-11-05 13:48:35 +01:00
+++ edited/fs/ntfs/dir.c	2005-02-13 17:34:51 +01:00
@@ -1102,7 +1102,7 @@ static int ntfs_readdir(struct file *fil
 {
 	s64 ia_pos, ia_start, prev_ia_pos, bmp_pos;
 	loff_t fpos;
-	struct inode *bmp_vi, *vdir = filp->f_dentry->d_inode;
+	struct inode *bmp_vi = NULL, *vdir = filp->f_dentry->d_inode;
 	struct super_block *sb = vdir->i_sb;
 	ntfs_inode *ndir = NTFS_I(vdir);
 	ntfs_volume *vol = NTFS_SB(sb);
@@ -1250,17 +1250,14 @@ skip_index_root:
 	/* Get the offset into the index allocation attribute. */
 	ia_pos = (s64)fpos - vol->mft_record_size;
 	ia_mapping = vdir->i_mapping;
-	bmp_vi = ndir->itype.index.bmp_ino;
+
+	bmp_vi = ntfs_grab_bmp_inode(vdir);
 	if (unlikely(!bmp_vi)) {
-		ntfs_debug("Inode 0x%lx, regetting index bitmap.", vdir->i_ino);
-		bmp_vi = ntfs_attr_iget(vdir, AT_BITMAP, I30, 4);
-		if (IS_ERR(bmp_vi)) {
-			ntfs_error(sb, "Failed to get bitmap attribute.");
-			err = PTR_ERR(bmp_vi);
-			goto err_out;
-		}
-		ndir->itype.index.bmp_ino = bmp_vi;
+		ntfs_error(sb, "Failed to get bitmap attribute.");
+		err = -EINVAL;
+		goto err_out;
 	}
+
 	bmp_mapping = bmp_vi->i_mapping;
 	/* Get the starting bitmap bit position and sanity check it. */
 	bmp_pos = ia_pos >> ndir->itype.index.block_size_bits;
@@ -1445,6 +1442,8 @@ EOD:
 abort:
 	kfree(name);
 done:
+	if (bmp_vi)
+		ntfs_ungrab_bmp_inode(vdir);
 #ifdef DEBUG
 	if (!rc)
 		ntfs_debug("EOD, fpos 0x%llx, returning 0.", fpos);
@@ -1455,6 +1454,8 @@ done:
 	filp->f_pos = fpos;
 	return 0;
 err_out:
+	if (bmp_vi)
+		ntfs_ungrab_bmp_inode(vdir);
 	if (bmp_page)
 		ntfs_unmap_page(bmp_page);
 	if (ia_page) {
@@ -1537,8 +1538,16 @@ static int ntfs_dir_fsync(struct file *f
 
 	ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
 	BUG_ON(!S_ISDIR(vi->i_mode));
-	if (NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino)
-		write_inode_now(ni->itype.index.bmp_ino, !datasync);
+
+	if (NInoIndexAllocPresent(ni)) {
+		struct inode *bmp_inode = ntfs_grab_bmp_inode(vi);
+
+		if (bmp_inode) {
+			write_inode_now(bmp_inode, !datasync);
+			ntfs_ungrab_bmp_inode(vi);
+		}
+	}
+
 	ret = ntfs_write_inode(vi, 1);
 	write_inode_now(vi, !datasync);
 	err = sync_blockdev(vi->i_sb->s_bdev);
--- 1.154/fs/ntfs/inode.c	2004-11-09 11:42:05 +01:00
+++ edited/fs/ntfs/inode.c	2005-02-13 17:40:55 +01:00
@@ -388,6 +388,7 @@ void __ntfs_init_inode(struct super_bloc
 	ni->attr_list = NULL;
 	ntfs_init_runlist(&ni->attr_list_rl);
 	ni->itype.index.bmp_ino = NULL;
+	spin_lock_init(&ni->itype.index.bmp_ino_lock);
 	ni->itype.index.block_size = 0;
 	ni->itype.index.vcn_size = 0;
 	ni->itype.index.collation_rule = 0;
@@ -414,6 +415,29 @@ inline ntfs_inode *ntfs_new_extent_inode
 	return ni;
 }
 
+/*
+ * Grab primary inode when we're using the attr inode because they 
+ * share state.
+ */
+struct inode *ntfs_grab_bmp_inode(struct inode *inode)
+{
+	struct inode *bmp_inode;
+	ntfs_inode *ni = NTFS_I(inode);
+
+	spin_lock(&ni->itype.index.bmp_ino_lock);
+	bmp_inode = ni->itype.index.bmp_ino;
+	if (bmp_inode && !igrab(inode))
+		bmp_inode = NULL;
+	spin_unlock(&ni->itype.index.bmp_ino_lock);
+
+	return inode;
+}
+
+void ntfs_ungrab_bmp_inode(struct inode *inode)
+{
+	iput(inode);
+}
+
 /**
  * ntfs_is_extended_system_file - check if a file is in the $Extend directory
  * @ctx:	initialized attribute search context
@@ -1365,7 +1389,6 @@ static int ntfs_read_locked_attr_inode(s
 	 * Make sure the base inode doesn't go away and attach it to the
 	 * attribute inode.
 	 */
-	igrab(base_vi);
 	ni->ext.base_ntfs_ino = base_ni;
 	ni->nr_extents = -1;
 
@@ -1651,7 +1674,6 @@ skip_large_index_stuff:
 	 * Make sure the base inode doesn't go away and attach it to the
 	 * index inode.
 	 */
-	igrab(base_vi);
 	ni->ext.base_ntfs_ino = base_ni;
 	ni->nr_extents = -1;
 
@@ -2102,37 +2124,6 @@ err_out:
 	return -1;
 }
 
-/**
- * ntfs_put_inode - handler for when the inode reference count is decremented
- * @vi:		vfs inode
- *
- * The VFS calls ntfs_put_inode() every time the inode reference count (i_count)
- * is about to be decremented (but before the decrement itself.
- *
- * If the inode @vi is a directory with two references, one of which is being
- * dropped, we need to put the attribute inode for the directory index bitmap,
- * if it is present, otherwise the directory inode would remain pinned for
- * ever.
- */
-void ntfs_put_inode(struct inode *vi)
-{
-	if (S_ISDIR(vi->i_mode) && atomic_read(&vi->i_count) == 2) {
-		ntfs_inode *ni = NTFS_I(vi);
-		if (NInoIndexAllocPresent(ni)) {
-			struct inode *bvi = NULL;
-			down(&vi->i_sem);
-			if (atomic_read(&vi->i_count) == 2) {
-				bvi = ni->itype.index.bmp_ino;
-				if (bvi)
-					ni->itype.index.bmp_ino = NULL;
-			}
-			up(&vi->i_sem);
-			if (bvi)
-				iput(bvi);
-		}
-	}
-}
-
 static void __ntfs_clear_inode(ntfs_inode *ni)
 {
 	/* Free all alocated memory. */
@@ -2198,18 +2189,6 @@ void ntfs_clear_big_inode(struct inode *
 {
 	ntfs_inode *ni = NTFS_I(vi);
 
-	/*
-	 * If the inode @vi is an index inode we need to put the attribute
-	 * inode for the index bitmap, if it is present, otherwise the index
-	 * inode would disappear and the attribute inode for the index bitmap
-	 * would no longer be referenced from anywhere and thus it would remain
-	 * pinned for ever.
-	 */
-	if (NInoAttr(ni) && (ni->type == AT_INDEX_ALLOCATION) &&
-			NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) {
-		iput(ni->itype.index.bmp_ino);
-		ni->itype.index.bmp_ino = NULL;
-	}
 #ifdef NTFS_RW
 	if (NInoDirty(ni)) {
 		BOOL was_bad = (is_bad_inode(vi));
@@ -2236,15 +2215,22 @@ void ntfs_clear_big_inode(struct inode *
 
 	__ntfs_clear_inode(ni);
 
+	/* Release the base inode if we are holding it. */
 	if (NInoAttr(ni)) {
-		/* Release the base inode if we are holding it. */
-		if (ni->nr_extents == -1) {
-			iput(VFS_I(ni->ext.base_ntfs_ino));
+		ntfs_inode *base_inode = ni->ext.base_ntfs_ino;
+
+		if (S_ISDIR(VFS_I(base_inode)->i_mode)) {
+			spin_lock(&base_inode->itype.index.bmp_ino_lock);
+			base_inode->itype.index.bmp_ino = NULL;
+			iput(VFS_I(base_inode));
+			spin_unlock(&base_inode->itype.index.bmp_ino_lock);
+			ni->ext.base_ntfs_ino = NULL;
+		} else if (ni->nr_extents == -1) {
 			ni->nr_extents = 0;
 			ni->ext.base_ntfs_ino = NULL;
+			iput(VFS_I(base_inode));
 		}
 	}
-	return;
 }
 
 /**
--- 1.45/fs/ntfs/inode.h	2004-10-25 11:46:30 +02:00
+++ edited/fs/ntfs/inode.h	2005-02-13 17:32:58 +01:00
@@ -101,6 +101,7 @@ struct _ntfs_inode {
 		struct { /* It is a directory, $MFT, or an index inode. */
 			struct inode *bmp_ino;	/* Attribute inode for the
 						   index $BITMAP. */
+			spinlock_t bmp_ino_lock;
 			u32 block_size;		/* Size of an index block. */
 			u32 vcn_size;		/* Size of a vcn in this
 						   index. */
@@ -275,6 +276,9 @@ extern struct inode *ntfs_attr_iget(stru
 extern struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
 		u32 name_len);
 
+extern struct inode *ntfs_grab_bmp_inode(struct inode *inode);
+extern void ntfs_ungrab_bmp_inode(struct inode *inode);
+
 extern struct inode *ntfs_alloc_big_inode(struct super_block *sb);
 extern void ntfs_destroy_big_inode(struct inode *inode);
 extern void ntfs_clear_big_inode(struct inode *vi);
@@ -295,8 +299,6 @@ extern ntfs_inode *ntfs_new_extent_inode
 extern void ntfs_clear_extent_inode(ntfs_inode *ni);
 
 extern int ntfs_read_inode_mount(struct inode *vi);
-
-extern void ntfs_put_inode(struct inode *vi);
 
 extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt);
 
--- 1.184/fs/ntfs/super.c	2005-01-05 03:48:14 +01:00
+++ edited/fs/ntfs/super.c	2005-02-13 17:15:47 +01:00
@@ -2186,9 +2186,6 @@ static int ntfs_statfs(struct super_bloc
 static struct super_operations ntfs_sops = {
 	.alloc_inode	= ntfs_alloc_big_inode,	  /* VFS: Allocate new inode. */
 	.destroy_inode	= ntfs_destroy_big_inode, /* VFS: Deallocate inode. */
-	.put_inode	= ntfs_put_inode,	  /* VFS: Called just before
-						     the inode reference count
-						     is decreased. */
 #ifdef NTFS_RW
 	//.dirty_inode	= NULL,			/* VFS: Called from
 	//					   __mark_inode_dirty(). */

  reply	other threads:[~2005-02-13 16:35 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-10-14 11:26 fishy ->put_inode usage in ntfs Christoph Hellwig
2004-10-14 12:39 ` Anton Altaparmakov
2004-10-14 12:44   ` Matthew Wilcox
2004-10-14 13:27     ` Anton Altaparmakov
2004-10-14 14:59     ` Anton Altaparmakov
2004-10-14 12:59   ` Christoph Hellwig
2004-10-14 13:26     ` Anton Altaparmakov
2005-02-10 10:47       ` Christoph Hellwig
2005-02-10 14:40         ` Anton Altaparmakov
2005-02-10 14:42           ` Christoph Hellwig
2005-02-10 14:48             ` Anton Altaparmakov
2005-02-10 14:50               ` Anton Altaparmakov
2005-02-10 14:51                 ` Christoph Hellwig
2005-02-10 14:50               ` Christoph Hellwig
2005-02-10 14:59                 ` Anton Altaparmakov
2005-02-13 16:35                   ` Christoph Hellwig [this message]
2005-02-14 20:44                     ` Anton Altaparmakov
2005-03-01 23:17                       ` David Woodhouse
2005-03-02  8:43                         ` Anton Altaparmakov
2005-03-02  8:53                           ` David Woodhouse

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20050213163512.GA3686@lst.de \
    --to=hch@lst.de \
    --cc=aia21@cam.ac.uk \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-ntfs-dev@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).