forked from mirrors/linux
		
	libfs: add path_from_stashed()
Add a helper for both nsfs and pidfs to reuse an already stashed dentry or to add and stash a new dentry. Link: https://lore.kernel.org/r/20240218-neufahrzeuge-brauhaus-fb0eb6459771@brauner Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
		
							parent
							
								
									cb12fd8e0d
								
							
						
					
					
						commit
						07fd7c3298
					
				
					 2 changed files with 97 additions and 0 deletions
				
			
		|  | @ -310,3 +310,6 @@ ssize_t __kernel_write_iter(struct file *file, struct iov_iter *from, loff_t *po | ||||||
| struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns); | struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns); | ||||||
| struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap); | struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap); | ||||||
| void mnt_idmap_put(struct mnt_idmap *idmap); | void mnt_idmap_put(struct mnt_idmap *idmap); | ||||||
|  | int path_from_stashed(struct dentry **stashed, unsigned long ino, | ||||||
|  | 		      struct vfsmount *mnt, const struct file_operations *fops, | ||||||
|  | 		      void *data, struct path *path); | ||||||
|  |  | ||||||
							
								
								
									
										94
									
								
								fs/libfs.c
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								fs/libfs.c
									
									
									
									
									
								
							|  | @ -1973,3 +1973,97 @@ struct timespec64 simple_inode_init_ts(struct inode *inode) | ||||||
| 	return ts; | 	return ts; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(simple_inode_init_ts); | EXPORT_SYMBOL(simple_inode_init_ts); | ||||||
|  | 
 | ||||||
|  | static inline struct dentry *get_stashed_dentry(struct dentry *stashed) | ||||||
|  | { | ||||||
|  | 	struct dentry *dentry; | ||||||
|  | 
 | ||||||
|  | 	guard(rcu)(); | ||||||
|  | 	dentry = READ_ONCE(stashed); | ||||||
|  | 	if (!dentry) | ||||||
|  | 		return NULL; | ||||||
|  | 	if (!lockref_get_not_dead(&dentry->d_lockref)) | ||||||
|  | 		return NULL; | ||||||
|  | 	return dentry; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct dentry *stash_dentry(struct dentry **stashed, unsigned long ino, | ||||||
|  | 				   struct super_block *sb, | ||||||
|  | 				   const struct file_operations *fops, | ||||||
|  | 				   void *data) | ||||||
|  | { | ||||||
|  | 	struct dentry *dentry; | ||||||
|  | 	struct inode *inode; | ||||||
|  | 
 | ||||||
|  | 	dentry = d_alloc_anon(sb); | ||||||
|  | 	if (!dentry) | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 
 | ||||||
|  | 	inode = new_inode_pseudo(sb); | ||||||
|  | 	if (!inode) { | ||||||
|  | 		dput(dentry); | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	inode->i_ino = ino; | ||||||
|  | 	inode->i_flags |= S_IMMUTABLE; | ||||||
|  | 	inode->i_mode = S_IFREG | S_IRUGO; | ||||||
|  | 	inode->i_fop = fops; | ||||||
|  | 	inode->i_private = data; | ||||||
|  | 	simple_inode_init_ts(inode); | ||||||
|  | 
 | ||||||
|  | 	/* @data is now owned by the fs */ | ||||||
|  | 	d_instantiate(dentry, inode); | ||||||
|  | 
 | ||||||
|  | 	if (cmpxchg(stashed, NULL, dentry)) { | ||||||
|  | 		d_delete(dentry); /* make sure ->d_prune() does nothing */ | ||||||
|  | 		dput(dentry); | ||||||
|  | 		cpu_relax(); | ||||||
|  | 		return ERR_PTR(-EAGAIN); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dentry; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * path_from_stashed - create path from stashed or new dentry | ||||||
|  |  * @stashed:    where to retrieve or stash dentry | ||||||
|  |  * @ino:        inode number to use | ||||||
|  |  * @mnt:        mnt of the filesystems to use | ||||||
|  |  * @fops:       file operations to use | ||||||
|  |  * @data:       data to store in inode->i_private | ||||||
|  |  * @path:       path to create | ||||||
|  |  * | ||||||
|  |  * The function tries to retrieve a stashed dentry from @stashed. If the dentry | ||||||
|  |  * is still valid then it will be reused. If the dentry isn't able the function | ||||||
|  |  * will allocate a new dentry and inode. It will then try to update @stashed | ||||||
|  |  * with the newly added dentry. If it fails -EAGAIN is returned and the caller | ||||||
|  |  * my retry. | ||||||
|  |  * | ||||||
|  |  * Special-purpose helper for nsfs and pidfs. | ||||||
|  |  * | ||||||
|  |  * Return: If 0 or an error is returned the caller can be sure that @data must | ||||||
|  |  *         be cleaned up. If 1 or -EAGAIN is returned @data is owned by the | ||||||
|  |  *         filesystem. | ||||||
|  |  */ | ||||||
|  | int path_from_stashed(struct dentry **stashed, unsigned long ino, | ||||||
|  | 		      struct vfsmount *mnt, const struct file_operations *fops, | ||||||
|  | 		      void *data, struct path *path) | ||||||
|  | { | ||||||
|  | 	struct dentry *dentry; | ||||||
|  | 	int ret = 0; | ||||||
|  | 
 | ||||||
|  | 	dentry = get_stashed_dentry(*stashed); | ||||||
|  | 	if (dentry) | ||||||
|  | 		goto out_path; | ||||||
|  | 
 | ||||||
|  | 	dentry = stash_dentry(stashed, ino, mnt->mnt_sb, fops, data); | ||||||
|  | 	if (IS_ERR(dentry)) | ||||||
|  | 		return PTR_ERR(dentry); | ||||||
|  | 	ret = 1; | ||||||
|  | 
 | ||||||
|  | out_path: | ||||||
|  | 	path->dentry = dentry; | ||||||
|  | 	path->mnt = mntget(mnt); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Christian Brauner
						Christian Brauner