diff options
Diffstat (limited to 'fs/inode.c')
| -rw-r--r-- | fs/inode.c | 92 | 
1 files changed, 88 insertions, 4 deletions
diff --git a/fs/inode.c b/fs/inode.c index ee4e66b998f..87535753ab0 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -26,6 +26,7 @@  #include <linux/ima.h>  #include <linux/cred.h>  #include <linux/buffer_head.h> /* for inode_has_buffers */ +#include <linux/ratelimit.h>  #include "internal.h"  /* @@ -191,6 +192,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)  	}  	inode->i_private = NULL;  	inode->i_mapping = mapping; +	INIT_LIST_HEAD(&inode->i_dentry);	/* buggered by rcu freeing */  #ifdef CONFIG_FS_POSIX_ACL  	inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;  #endif @@ -241,6 +243,11 @@ void __destroy_inode(struct inode *inode)  	BUG_ON(inode_has_buffers(inode));  	security_inode_free(inode);  	fsnotify_inode_delete(inode); +	if (!inode->i_nlink) { +		WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0); +		atomic_long_dec(&inode->i_sb->s_remove_count); +	} +  #ifdef CONFIG_FS_POSIX_ACL  	if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)  		posix_acl_release(inode->i_acl); @@ -254,7 +261,6 @@ EXPORT_SYMBOL(__destroy_inode);  static void i_callback(struct rcu_head *head)  {  	struct inode *inode = container_of(head, struct inode, i_rcu); -	INIT_LIST_HEAD(&inode->i_dentry);  	kmem_cache_free(inode_cachep, inode);  } @@ -268,6 +274,85 @@ static void destroy_inode(struct inode *inode)  		call_rcu(&inode->i_rcu, i_callback);  } +/** + * drop_nlink - directly drop an inode's link count + * @inode: inode + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink.  In cases + * where we are attempting to track writes to the + * filesystem, a decrement to zero means an imminent + * write when the file is truncated and actually unlinked + * on the filesystem. + */ +void drop_nlink(struct inode *inode) +{ +	WARN_ON(inode->i_nlink == 0); +	inode->__i_nlink--; +	if (!inode->i_nlink) +		atomic_long_inc(&inode->i_sb->s_remove_count); +} +EXPORT_SYMBOL(drop_nlink); + +/** + * clear_nlink - directly zero an inode's link count + * @inode: inode + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink.  See + * drop_nlink() for why we care about i_nlink hitting zero. + */ +void clear_nlink(struct inode *inode) +{ +	if (inode->i_nlink) { +		inode->__i_nlink = 0; +		atomic_long_inc(&inode->i_sb->s_remove_count); +	} +} +EXPORT_SYMBOL(clear_nlink); + +/** + * set_nlink - directly set an inode's link count + * @inode: inode + * @nlink: new nlink (should be non-zero) + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink. + */ +void set_nlink(struct inode *inode, unsigned int nlink) +{ +	if (!nlink) { +		printk_ratelimited(KERN_INFO +			"set_nlink() clearing i_nlink on %s inode %li\n", +			inode->i_sb->s_type->name, inode->i_ino); +		clear_nlink(inode); +	} else { +		/* Yes, some filesystems do change nlink from zero to one */ +		if (inode->i_nlink == 0) +			atomic_long_dec(&inode->i_sb->s_remove_count); + +		inode->__i_nlink = nlink; +	} +} +EXPORT_SYMBOL(set_nlink); + +/** + * inc_nlink - directly increment an inode's link count + * @inode: inode + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink.  Currently, + * it is only here for parity with dec_nlink(). + */ +void inc_nlink(struct inode *inode) +{ +	if (WARN_ON(inode->i_nlink == 0)) +		atomic_long_dec(&inode->i_sb->s_remove_count); + +	inode->__i_nlink++; +} +EXPORT_SYMBOL(inc_nlink); +  void address_space_init_once(struct address_space *mapping)  {  	memset(mapping, 0, sizeof(*mapping)); @@ -290,7 +375,6 @@ void inode_init_once(struct inode *inode)  {  	memset(inode, 0, sizeof(*inode));  	INIT_HLIST_NODE(&inode->i_hash); -	INIT_LIST_HEAD(&inode->i_dentry);  	INIT_LIST_HEAD(&inode->i_devices);  	INIT_LIST_HEAD(&inode->i_wb_list);  	INIT_LIST_HEAD(&inode->i_lru); @@ -1508,7 +1592,7 @@ void file_update_time(struct file *file)  	if (sync_it & S_MTIME)  		inode->i_mtime = now;  	mark_inode_dirty_sync(inode); -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  }  EXPORT_SYMBOL(file_update_time); @@ -1647,7 +1731,7 @@ EXPORT_SYMBOL(init_special_inode);   * @mode: mode of the new inode   */  void inode_init_owner(struct inode *inode, const struct inode *dir, -			mode_t mode) +			umode_t mode)  {  	inode->i_uid = current_fsuid();  	if (dir && dir->i_mode & S_ISGID) {  |