forked from mirrors/linux
		
	io_uring: add support for IORING_OP_LINKAT
IORING_OP_LINKAT behaves like linkat(2) and takes the same flags and arguments. In some internal places 'hardlink' is used instead of 'link' to avoid confusion with the SQE links. Name 'link' conflicts with the existing 'link' member of io_kiocb. Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Suggested-by: Christian Brauner <christian.brauner@ubuntu.com> Link: https://lore.kernel.org/io-uring/20210514145259.wtl4xcsp52woi6ab@wittgenstein/ Signed-off-by: Dmitry Kadashev <dkadashev@gmail.com> Acked-by: Christian Brauner <christian.brauner@ubuntu.com> Link: https://lore.kernel.org/r/20210708063447.3556403-12-dkadashev@gmail.com [axboe: add splice_fd_in check] Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
		
							parent
							
								
									7a8721f84f
								
							
						
					
					
						commit
						cf30da90bc
					
				
					 4 changed files with 76 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -78,6 +78,8 @@ int do_renameat2(int olddfd, struct filename *oldname, int newdfd,
 | 
			
		|||
		 struct filename *newname, unsigned int flags);
 | 
			
		||||
int do_mkdirat(int dfd, struct filename *name, umode_t mode);
 | 
			
		||||
int do_symlinkat(struct filename *from, int newdfd, struct filename *to);
 | 
			
		||||
int do_linkat(int olddfd, struct filename *old, int newdfd,
 | 
			
		||||
			struct filename *new, int flags);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * namespace.c
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -676,6 +676,15 @@ struct io_symlink {
 | 
			
		|||
	struct filename			*newpath;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct io_hardlink {
 | 
			
		||||
	struct file			*file;
 | 
			
		||||
	int				old_dfd;
 | 
			
		||||
	int				new_dfd;
 | 
			
		||||
	struct filename			*oldpath;
 | 
			
		||||
	struct filename			*newpath;
 | 
			
		||||
	int				flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct io_completion {
 | 
			
		||||
	struct file			*file;
 | 
			
		||||
	u32				cflags;
 | 
			
		||||
| 
						 | 
				
			
			@ -837,6 +846,7 @@ struct io_kiocb {
 | 
			
		|||
		struct io_unlink	unlink;
 | 
			
		||||
		struct io_mkdir		mkdir;
 | 
			
		||||
		struct io_symlink	symlink;
 | 
			
		||||
		struct io_hardlink	hardlink;
 | 
			
		||||
		/* use only after cleaning per-op data, see io_clean_op() */
 | 
			
		||||
		struct io_completion	compl;
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -1050,6 +1060,7 @@ static const struct io_op_def io_op_defs[] = {
 | 
			
		|||
	[IORING_OP_UNLINKAT] = {},
 | 
			
		||||
	[IORING_OP_MKDIRAT] = {},
 | 
			
		||||
	[IORING_OP_SYMLINKAT] = {},
 | 
			
		||||
	[IORING_OP_LINKAT] = {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* requests with any of those set should undergo io_disarm_next() */
 | 
			
		||||
| 
						 | 
				
			
			@ -3713,6 +3724,57 @@ static int io_symlinkat(struct io_kiocb *req, int issue_flags)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int io_linkat_prep(struct io_kiocb *req,
 | 
			
		||||
			    const struct io_uring_sqe *sqe)
 | 
			
		||||
{
 | 
			
		||||
	struct io_hardlink *lnk = &req->hardlink;
 | 
			
		||||
	const char __user *oldf, *newf;
 | 
			
		||||
 | 
			
		||||
	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (sqe->ioprio || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (unlikely(req->flags & REQ_F_FIXED_FILE))
 | 
			
		||||
		return -EBADF;
 | 
			
		||||
 | 
			
		||||
	lnk->old_dfd = READ_ONCE(sqe->fd);
 | 
			
		||||
	lnk->new_dfd = READ_ONCE(sqe->len);
 | 
			
		||||
	oldf = u64_to_user_ptr(READ_ONCE(sqe->addr));
 | 
			
		||||
	newf = u64_to_user_ptr(READ_ONCE(sqe->addr2));
 | 
			
		||||
	lnk->flags = READ_ONCE(sqe->hardlink_flags);
 | 
			
		||||
 | 
			
		||||
	lnk->oldpath = getname(oldf);
 | 
			
		||||
	if (IS_ERR(lnk->oldpath))
 | 
			
		||||
		return PTR_ERR(lnk->oldpath);
 | 
			
		||||
 | 
			
		||||
	lnk->newpath = getname(newf);
 | 
			
		||||
	if (IS_ERR(lnk->newpath)) {
 | 
			
		||||
		putname(lnk->oldpath);
 | 
			
		||||
		return PTR_ERR(lnk->newpath);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req->flags |= REQ_F_NEED_CLEANUP;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int io_linkat(struct io_kiocb *req, int issue_flags)
 | 
			
		||||
{
 | 
			
		||||
	struct io_hardlink *lnk = &req->hardlink;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (issue_flags & IO_URING_F_NONBLOCK)
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
 | 
			
		||||
	ret = do_linkat(lnk->old_dfd, lnk->oldpath, lnk->new_dfd,
 | 
			
		||||
				lnk->newpath, lnk->flags);
 | 
			
		||||
 | 
			
		||||
	req->flags &= ~REQ_F_NEED_CLEANUP;
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		req_set_fail(req);
 | 
			
		||||
	io_req_complete(req, ret);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int io_shutdown_prep(struct io_kiocb *req,
 | 
			
		||||
			    const struct io_uring_sqe *sqe)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -6114,6 +6176,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 | 
			
		|||
		return io_mkdirat_prep(req, sqe);
 | 
			
		||||
	case IORING_OP_SYMLINKAT:
 | 
			
		||||
		return io_symlinkat_prep(req, sqe);
 | 
			
		||||
	case IORING_OP_LINKAT:
 | 
			
		||||
		return io_linkat_prep(req, sqe);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n",
 | 
			
		||||
| 
						 | 
				
			
			@ -6284,6 +6348,10 @@ static void io_clean_op(struct io_kiocb *req)
 | 
			
		|||
			putname(req->symlink.oldpath);
 | 
			
		||||
			putname(req->symlink.newpath);
 | 
			
		||||
			break;
 | 
			
		||||
		case IORING_OP_LINKAT:
 | 
			
		||||
			putname(req->hardlink.oldpath);
 | 
			
		||||
			putname(req->hardlink.newpath);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ((req->flags & REQ_F_POLLED) && req->apoll) {
 | 
			
		||||
| 
						 | 
				
			
			@ -6418,6 +6486,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
 | 
			
		|||
	case IORING_OP_SYMLINKAT:
 | 
			
		||||
		ret = io_symlinkat(req, issue_flags);
 | 
			
		||||
		break;
 | 
			
		||||
	case IORING_OP_LINKAT:
 | 
			
		||||
		ret = io_linkat(req, issue_flags);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4368,7 +4368,7 @@ EXPORT_SYMBOL(vfs_link);
 | 
			
		|||
 * with linux 2.0, and to avoid hard-linking to directories
 | 
			
		||||
 * and other special files.  --ADM
 | 
			
		||||
 */
 | 
			
		||||
static int do_linkat(int olddfd, struct filename *old, int newdfd,
 | 
			
		||||
int do_linkat(int olddfd, struct filename *old, int newdfd,
 | 
			
		||||
	      struct filename *new, int flags)
 | 
			
		||||
{
 | 
			
		||||
	struct user_namespace *mnt_userns;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ struct io_uring_sqe {
 | 
			
		|||
		__u32		splice_flags;
 | 
			
		||||
		__u32		rename_flags;
 | 
			
		||||
		__u32		unlink_flags;
 | 
			
		||||
		__u32		hardlink_flags;
 | 
			
		||||
	};
 | 
			
		||||
	__u64	user_data;	/* data to be passed back at completion time */
 | 
			
		||||
	/* pack this to avoid bogus arm OABI complaints */
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +136,7 @@ enum {
 | 
			
		|||
	IORING_OP_UNLINKAT,
 | 
			
		||||
	IORING_OP_MKDIRAT,
 | 
			
		||||
	IORING_OP_SYMLINKAT,
 | 
			
		||||
	IORING_OP_LINKAT,
 | 
			
		||||
 | 
			
		||||
	/* this goes last, obviously */
 | 
			
		||||
	IORING_OP_LAST,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue