diff options
Diffstat (limited to 'fs/file_table.c')
| -rw-r--r-- | fs/file_table.c | 91 | 
1 files changed, 74 insertions, 17 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 372653b92617..e06c68e2d757 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -44,18 +44,40 @@ static struct kmem_cache *filp_cachep __read_mostly;  static struct percpu_counter nr_files __cacheline_aligned_in_smp; +/* Container for backing file with optional real path */ +struct backing_file { +	struct file file; +	struct path real_path; +}; + +static inline struct backing_file *backing_file(struct file *f) +{ +	return container_of(f, struct backing_file, file); +} + +struct path *backing_file_real_path(struct file *f) +{ +	return &backing_file(f)->real_path; +} +EXPORT_SYMBOL_GPL(backing_file_real_path); +  static void file_free_rcu(struct rcu_head *head)  {  	struct file *f = container_of(head, struct file, f_rcuhead);  	put_cred(f->f_cred); -	kmem_cache_free(filp_cachep, f); +	if (unlikely(f->f_mode & FMODE_BACKING)) +		kfree(backing_file(f)); +	else +		kmem_cache_free(filp_cachep, f);  }  static inline void file_free(struct file *f)  {  	security_file_free(f); -	if (!(f->f_mode & FMODE_NOACCOUNT)) +	if (unlikely(f->f_mode & FMODE_BACKING)) +		path_put(backing_file_real_path(f)); +	if (likely(!(f->f_mode & FMODE_NOACCOUNT)))  		percpu_counter_dec(&nr_files);  	call_rcu(&f->f_rcuhead, file_free_rcu);  } @@ -131,20 +153,15 @@ static int __init init_fs_stat_sysctls(void)  fs_initcall(init_fs_stat_sysctls);  #endif -static struct file *__alloc_file(int flags, const struct cred *cred) +static int init_file(struct file *f, int flags, const struct cred *cred)  { -	struct file *f;  	int error; -	f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL); -	if (unlikely(!f)) -		return ERR_PTR(-ENOMEM); -  	f->f_cred = get_cred(cred);  	error = security_file_alloc(f);  	if (unlikely(error)) {  		file_free_rcu(&f->f_rcuhead); -		return ERR_PTR(error); +		return error;  	}  	atomic_long_set(&f->f_count, 1); @@ -155,7 +172,7 @@ static struct file *__alloc_file(int flags, const struct cred *cred)  	f->f_mode = OPEN_FMODE(flags);  	/* f->f_version: 0 */ -	return f; +	return 0;  }  /* Find an unused file structure and return a pointer to it. @@ -172,6 +189,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)  {  	static long old_max;  	struct file *f; +	int error;  	/*  	 * Privileged users can go above max_files @@ -185,9 +203,15 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)  			goto over;  	} -	f = __alloc_file(flags, cred); -	if (!IS_ERR(f)) -		percpu_counter_inc(&nr_files); +	f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL); +	if (unlikely(!f)) +		return ERR_PTR(-ENOMEM); + +	error = init_file(f, flags, cred); +	if (unlikely(error)) +		return ERR_PTR(error); + +	percpu_counter_inc(&nr_files);  	return f; @@ -203,18 +227,51 @@ over:  /*   * Variant of alloc_empty_file() that doesn't check and modify nr_files.   * - * Should not be used unless there's a very good reason to do so. + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such.   */  struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)  { -	struct file *f = __alloc_file(flags, cred); +	struct file *f; +	int error; -	if (!IS_ERR(f)) -		f->f_mode |= FMODE_NOACCOUNT; +	f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL); +	if (unlikely(!f)) +		return ERR_PTR(-ENOMEM); + +	error = init_file(f, flags, cred); +	if (unlikely(error)) +		return ERR_PTR(error); + +	f->f_mode |= FMODE_NOACCOUNT;  	return f;  } +/* + * Variant of alloc_empty_file() that allocates a backing_file container + * and doesn't check and modify nr_files. + * + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such. + */ +struct file *alloc_empty_backing_file(int flags, const struct cred *cred) +{ +	struct backing_file *ff; +	int error; + +	ff = kzalloc(sizeof(struct backing_file), GFP_KERNEL); +	if (unlikely(!ff)) +		return ERR_PTR(-ENOMEM); + +	error = init_file(&ff->file, flags, cred); +	if (unlikely(error)) +		return ERR_PTR(error); + +	ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; +	return &ff->file; +} +  /**   * alloc_file - allocate and initialize a 'struct file'   *  |