ext3: make sure inode is deleted from orphan list after truncate
As Ted pointed out, it can happen that ext3_truncate() returns without removing inode from orphan list. This way we could in some rare cases (like when we get ENOMEM from an allocation in ext3_truncate called because of failed ext3_write_begin) leave the inode on orphan list and that triggers assertion failure on umount. So make ext3_truncate() always remove inode from in-memory orphan list. Cc: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
					parent
					
						
							
								6f3f1cb21f
							
						
					
				
			
			
				commit
				
					
						ef43618a47
					
				
			
		
					 1 changed files with 11 additions and 9 deletions
				
			
		| 
						 | 
					@ -2374,7 +2374,7 @@ void ext3_truncate(struct inode *inode)
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ext3_can_truncate(inode))
 | 
						if (!ext3_can_truncate(inode))
 | 
				
			||||||
		return;
 | 
							goto out_notrans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (inode->i_size == 0 && ext3_should_writeback_data(inode))
 | 
						if (inode->i_size == 0 && ext3_should_writeback_data(inode))
 | 
				
			||||||
		ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE;
 | 
							ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE;
 | 
				
			||||||
| 
						 | 
					@ -2390,7 +2390,7 @@ void ext3_truncate(struct inode *inode)
 | 
				
			||||||
		page = grab_cache_page(mapping,
 | 
							page = grab_cache_page(mapping,
 | 
				
			||||||
				inode->i_size >> PAGE_CACHE_SHIFT);
 | 
									inode->i_size >> PAGE_CACHE_SHIFT);
 | 
				
			||||||
		if (!page)
 | 
							if (!page)
 | 
				
			||||||
			return;
 | 
								goto out_notrans;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handle = start_transaction(inode);
 | 
						handle = start_transaction(inode);
 | 
				
			||||||
| 
						 | 
					@ -2401,7 +2401,7 @@ void ext3_truncate(struct inode *inode)
 | 
				
			||||||
			unlock_page(page);
 | 
								unlock_page(page);
 | 
				
			||||||
			page_cache_release(page);
 | 
								page_cache_release(page);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return;		/* AKPM: return what? */
 | 
							goto out_notrans;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	last_block = (inode->i_size + blocksize-1)
 | 
						last_block = (inode->i_size + blocksize-1)
 | 
				
			||||||
| 
						 | 
					@ -2525,6 +2525,14 @@ out_stop:
 | 
				
			||||||
		ext3_orphan_del(handle, inode);
 | 
							ext3_orphan_del(handle, inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ext3_journal_stop(handle);
 | 
						ext3_journal_stop(handle);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					out_notrans:
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Delete the inode from orphan list so that it doesn't stay there
 | 
				
			||||||
 | 
						 * forever and trigger assertion on umount.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (inode->i_nlink)
 | 
				
			||||||
 | 
							ext3_orphan_del(NULL, inode);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb,
 | 
					static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb,
 | 
				
			||||||
| 
						 | 
					@ -3122,12 +3130,6 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = inode_setattr(inode, attr);
 | 
						rc = inode_setattr(inode, attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If inode_setattr's call to ext3_truncate failed to get a
 | 
					 | 
				
			||||||
	 * transaction handle at all, we need to clean up the in-core
 | 
					 | 
				
			||||||
	 * orphan list manually. */
 | 
					 | 
				
			||||||
	if (inode->i_nlink)
 | 
					 | 
				
			||||||
		ext3_orphan_del(NULL, inode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!rc && (ia_valid & ATTR_MODE))
 | 
						if (!rc && (ia_valid & ATTR_MODE))
 | 
				
			||||||
		rc = ext3_acl_chmod(inode);
 | 
							rc = ext3_acl_chmod(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue