diff options
Diffstat (limited to 'fs/inode.c')
| -rw-r--r-- | fs/inode.c | 108 | 
1 files changed, 87 insertions, 21 deletions
diff --git a/fs/inode.c b/fs/inode.c index b33ba8e021c..6eecb7ff0b9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -105,7 +105,7 @@ long get_nr_dirty_inodes(void)   * Handle nr_inode sysctl   */  #ifdef CONFIG_SYSCTL -int proc_nr_inodes(ctl_table *table, int write, +int proc_nr_inodes(struct ctl_table *table, int write,  		   void __user *buffer, size_t *lenp, loff_t *ppos)  {  	inodes_stat.nr_inodes = get_nr_inodes(); @@ -503,6 +503,7 @@ void clear_inode(struct inode *inode)  	 */  	spin_lock_irq(&inode->i_data.tree_lock);  	BUG_ON(inode->i_data.nrpages); +	BUG_ON(inode->i_data.nrshadows);  	spin_unlock_irq(&inode->i_data.tree_lock);  	BUG_ON(!list_empty(&inode->i_data.private_list));  	BUG_ON(!(inode->i_state & I_FREEING)); @@ -548,8 +549,7 @@ static void evict(struct inode *inode)  	if (op->evict_inode) {  		op->evict_inode(inode);  	} else { -		if (inode->i_data.nrpages) -			truncate_inode_pages(&inode->i_data, 0); +		truncate_inode_pages_final(&inode->i_data);  		clear_inode(inode);  	}  	if (S_ISBLK(inode->i_mode) && inode->i_bdev) @@ -773,15 +773,11 @@ static struct inode *find_inode(struct super_block *sb,  repeat:  	hlist_for_each_entry(inode, head, i_hash) { -		spin_lock(&inode->i_lock); -		if (inode->i_sb != sb) { -			spin_unlock(&inode->i_lock); +		if (inode->i_sb != sb)  			continue; -		} -		if (!test(inode, data)) { -			spin_unlock(&inode->i_lock); +		if (!test(inode, data))  			continue; -		} +		spin_lock(&inode->i_lock);  		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {  			__wait_on_freeing_inode(inode);  			goto repeat; @@ -804,15 +800,11 @@ static struct inode *find_inode_fast(struct super_block *sb,  repeat:  	hlist_for_each_entry(inode, head, i_hash) { -		spin_lock(&inode->i_lock); -		if (inode->i_ino != ino) { -			spin_unlock(&inode->i_lock); +		if (inode->i_ino != ino)  			continue; -		} -		if (inode->i_sb != sb) { -			spin_unlock(&inode->i_lock); +		if (inode->i_sb != sb)  			continue; -		} +		spin_lock(&inode->i_lock);  		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {  			__wait_on_freeing_inode(inode);  			goto repeat; @@ -951,6 +943,41 @@ void unlock_new_inode(struct inode *inode)  EXPORT_SYMBOL(unlock_new_inode);  /** + * lock_two_nondirectories - take two i_mutexes on non-directory objects + * + * Lock any non-NULL argument that is not a directory. + * Zero, one or two objects may be locked by this function. + * + * @inode1: first inode to lock + * @inode2: second inode to lock + */ +void lock_two_nondirectories(struct inode *inode1, struct inode *inode2) +{ +	if (inode1 > inode2) +		swap(inode1, inode2); + +	if (inode1 && !S_ISDIR(inode1->i_mode)) +		mutex_lock(&inode1->i_mutex); +	if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1) +		mutex_lock_nested(&inode2->i_mutex, I_MUTEX_NONDIR2); +} +EXPORT_SYMBOL(lock_two_nondirectories); + +/** + * unlock_two_nondirectories - release locks from lock_two_nondirectories() + * @inode1: first inode to unlock + * @inode2: second inode to unlock + */ +void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2) +{ +	if (inode1 && !S_ISDIR(inode1->i_mode)) +		mutex_unlock(&inode1->i_mutex); +	if (inode2 && !S_ISDIR(inode2->i_mode) && inode2 != inode1) +		mutex_unlock(&inode2->i_mutex); +} +EXPORT_SYMBOL(unlock_two_nondirectories); + +/**   * iget5_locked - obtain an inode from a mounted file system   * @sb:		super block of file system   * @hashval:	hash value (usually inode number) to get @@ -1575,7 +1602,11 @@ static int __remove_suid(struct dentry *dentry, int kill)  	struct iattr newattrs;  	newattrs.ia_valid = ATTR_FORCE | kill; -	return notify_change(dentry, &newattrs); +	/* +	 * Note we call this on write, so notify_change will not +	 * encounter any conflicting delegations: +	 */ +	return notify_change(dentry, &newattrs, NULL);  }  int file_remove_suid(struct file *file) @@ -1808,14 +1839,18 @@ EXPORT_SYMBOL(inode_init_owner);   * inode_owner_or_capable - check current task permissions to inode   * @inode: inode being checked   * - * Return true if current either has CAP_FOWNER to the inode, or - * owns the file. + * Return true if current either has CAP_FOWNER in a namespace with the + * inode owner uid mapped, or owns the file.   */  bool inode_owner_or_capable(const struct inode *inode)  { +	struct user_namespace *ns; +  	if (uid_eq(current_fsuid(), inode->i_uid))  		return true; -	if (inode_capable(inode, CAP_FOWNER)) + +	ns = current_user_ns(); +	if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))  		return true;  	return false;  } @@ -1867,3 +1902,34 @@ void inode_dio_done(struct inode *inode)  		wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);  }  EXPORT_SYMBOL(inode_dio_done); + +/* + * inode_set_flags - atomically set some inode flags + * + * Note: the caller should be holding i_mutex, or else be sure that + * they have exclusive access to the inode structure (i.e., while the + * inode is being instantiated).  The reason for the cmpxchg() loop + * --- which wouldn't be necessary if all code paths which modify + * i_flags actually followed this rule, is that there is at least one + * code path which doesn't today --- for example, + * __generic_file_aio_write() calls file_remove_suid() without holding + * i_mutex --- so we use cmpxchg() out of an abundance of caution. + * + * In the long run, i_mutex is overkill, and we should probably look + * at using the i_lock spinlock to protect i_flags, and then make sure + * it is so documented in include/linux/fs.h and that all code follows + * the locking convention!! + */ +void inode_set_flags(struct inode *inode, unsigned int flags, +		     unsigned int mask) +{ +	unsigned int old_flags, new_flags; + +	WARN_ON_ONCE(flags & ~mask); +	do { +		old_flags = ACCESS_ONCE(inode->i_flags); +		new_flags = (old_flags & ~mask) | flags; +	} while (unlikely(cmpxchg(&inode->i_flags, old_flags, +				  new_flags) != old_flags)); +} +EXPORT_SYMBOL(inode_set_flags);  | 
