mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	fs: use backing_file container for internal files with "fake" f_path
Overlayfs uses open_with_fake_path() to allocate internal kernel files, with a "fake" path - whose f_path is not on the same fs as f_inode. Allocate a container struct backing_file for those internal files, that is used to hold the "fake" ovl path along with the real path. backing_file_real_path() can be used to access the stored real path. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Message-Id: <20230615112229.2143178-5-amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
		
							parent
							
								
									8a05a8c31d
								
							
						
					
					
						commit
						62d53c4a1d
					
				
					 5 changed files with 114 additions and 23 deletions
				
			
		|  | @ -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); | ||||
| } | ||||
|  | @ -226,6 +248,30 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) | |||
| 	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' | ||||
|  * | ||||
|  |  | |||
|  | @ -97,8 +97,9 @@ extern void chroot_fs_refs(const struct path *, const struct path *); | |||
| /*
 | ||||
|  * file_table.c | ||||
|  */ | ||||
| extern struct file *alloc_empty_file(int, const struct cred *); | ||||
| extern struct file *alloc_empty_file_noaccount(int, const struct cred *); | ||||
| struct file *alloc_empty_file(int flags, const struct cred *cred); | ||||
| struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); | ||||
| struct file *alloc_empty_backing_file(int flags, const struct cred *cred); | ||||
| 
 | ||||
| static inline void put_file_access(struct file *file) | ||||
| { | ||||
|  |  | |||
							
								
								
									
										45
									
								
								fs/open.c
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								fs/open.c
									
									
									
									
									
								
							|  | @ -1149,23 +1149,44 @@ struct file *kernel_file_open(const struct path *path, int flags, | |||
| } | ||||
| EXPORT_SYMBOL_GPL(kernel_file_open); | ||||
| 
 | ||||
| struct file *open_with_fake_path(const struct path *path, int flags, | ||||
| 				struct inode *inode, const struct cred *cred) | ||||
| /**
 | ||||
|  * backing_file_open - open a backing file for kernel internal use | ||||
|  * @path:	path of the file to open | ||||
|  * @flags:	open flags | ||||
|  * @path:	path of the backing file | ||||
|  * @cred:	credentials for open | ||||
|  * | ||||
|  * Open a backing file for a stackable filesystem (e.g., overlayfs). | ||||
|  * @path may be on the stackable filesystem and backing inode on the | ||||
|  * underlying filesystem. In this case, we want to be able to return | ||||
|  * the @real_path of the backing inode. This is done by embedding the | ||||
|  * returned file into a container structure that also stores the path of | ||||
|  * the backing inode on the underlying filesystem, which can be | ||||
|  * retrieved using backing_file_real_path(). | ||||
|  */ | ||||
| struct file *backing_file_open(const struct path *path, int flags, | ||||
| 			       const struct path *real_path, | ||||
| 			       const struct cred *cred) | ||||
| { | ||||
| 	struct file *f = alloc_empty_file_noaccount(flags, cred); | ||||
| 	if (!IS_ERR(f)) { | ||||
| 		int error; | ||||
| 	struct file *f; | ||||
| 	int error; | ||||
| 
 | ||||
| 		f->f_path = *path; | ||||
| 		error = do_dentry_open(f, inode, NULL); | ||||
| 		if (error) { | ||||
| 			fput(f); | ||||
| 			f = ERR_PTR(error); | ||||
| 		} | ||||
| 	f = alloc_empty_backing_file(flags, cred); | ||||
| 	if (IS_ERR(f)) | ||||
| 		return f; | ||||
| 
 | ||||
| 	f->f_path = *path; | ||||
| 	path_get(real_path); | ||||
| 	*backing_file_real_path(f) = *real_path; | ||||
| 	error = do_dentry_open(f, d_inode(real_path->dentry), NULL); | ||||
| 	if (error) { | ||||
| 		fput(f); | ||||
| 		f = ERR_PTR(error); | ||||
| 	} | ||||
| 
 | ||||
| 	return f; | ||||
| } | ||||
| EXPORT_SYMBOL(open_with_fake_path); | ||||
| EXPORT_SYMBOL_GPL(backing_file_open); | ||||
| 
 | ||||
| #define WILL_CREATE(flags)	(flags & (O_CREAT | __O_TMPFILE)) | ||||
| #define O_PATH_FLAGS		(O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC) | ||||
|  |  | |||
|  | @ -61,8 +61,8 @@ static struct file *ovl_open_realfile(const struct file *file, | |||
| 		if (!inode_owner_or_capable(real_idmap, realinode)) | ||||
| 			flags &= ~O_NOATIME; | ||||
| 
 | ||||
| 		realfile = open_with_fake_path(&file->f_path, flags, realinode, | ||||
| 					       current_cred()); | ||||
| 		realfile = backing_file_open(&file->f_path, flags, realpath, | ||||
| 					     current_cred()); | ||||
| 	} | ||||
| 	revert_creds(old_cred); | ||||
| 
 | ||||
|  |  | |||
|  | @ -171,6 +171,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, | |||
| /* File supports non-exclusive O_DIRECT writes from multiple threads */ | ||||
| #define FMODE_DIO_PARALLEL_WRITE	((__force fmode_t)0x1000000) | ||||
| 
 | ||||
| /* File is embedded in backing_file object */ | ||||
| #define FMODE_BACKING		((__force fmode_t)0x2000000) | ||||
| 
 | ||||
| /* File was opened by fanotify and shouldn't generate fanotify events */ | ||||
| #define FMODE_NONOTIFY		((__force fmode_t)0x4000000) | ||||
| 
 | ||||
|  | @ -2352,11 +2355,31 @@ static inline struct file *file_open_root_mnt(struct vfsmount *mnt, | |||
| 	return file_open_root(&(struct path){.mnt = mnt, .dentry = mnt->mnt_root}, | ||||
| 			      name, flags, mode); | ||||
| } | ||||
| extern struct file * dentry_open(const struct path *, int, const struct cred *); | ||||
| extern struct file *dentry_create(const struct path *path, int flags, | ||||
| 				  umode_t mode, const struct cred *cred); | ||||
| extern struct file * open_with_fake_path(const struct path *, int, | ||||
| 					 struct inode*, const struct cred *); | ||||
| struct file *dentry_open(const struct path *path, int flags, | ||||
| 			 const struct cred *creds); | ||||
| struct file *dentry_create(const struct path *path, int flags, umode_t mode, | ||||
| 			   const struct cred *cred); | ||||
| struct file *backing_file_open(const struct path *path, int flags, | ||||
| 			       const struct path *real_path, | ||||
| 			       const struct cred *cred); | ||||
| struct path *backing_file_real_path(struct file *f); | ||||
| 
 | ||||
| /*
 | ||||
|  * file_real_path - get the path corresponding to f_inode | ||||
|  * | ||||
|  * When opening a backing file for a stackable filesystem (e.g., | ||||
|  * overlayfs) f_path may be on the stackable filesystem and f_inode on | ||||
|  * the underlying filesystem.  When the path associated with f_inode is | ||||
|  * needed, this helper should be used instead of accessing f_path | ||||
|  * directly. | ||||
| */ | ||||
| static inline const struct path *file_real_path(struct file *f) | ||||
| { | ||||
| 	if (unlikely(f->f_mode & FMODE_BACKING)) | ||||
| 		return backing_file_real_path(f); | ||||
| 	return &f->f_path; | ||||
| } | ||||
| 
 | ||||
| static inline struct file *file_clone_open(struct file *file) | ||||
| { | ||||
| 	return dentry_open(&file->f_path, file->f_flags, file->f_cred); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Amir Goldstein
						Amir Goldstein