162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/file.h>
562306a36Sopenharmony_ci#include <linux/mm.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/nospec.h>
862306a36Sopenharmony_ci#include <linux/io_uring.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <uapi/linux/io_uring.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "io_uring.h"
1362306a36Sopenharmony_ci#include "rsrc.h"
1462306a36Sopenharmony_ci#include "filetable.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int io_file_bitmap_get(struct io_ring_ctx *ctx)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct io_file_table *table = &ctx->file_table;
1962306a36Sopenharmony_ci	unsigned long nr = ctx->file_alloc_end;
2062306a36Sopenharmony_ci	int ret;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (!table->bitmap)
2362306a36Sopenharmony_ci		return -ENFILE;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	do {
2662306a36Sopenharmony_ci		ret = find_next_zero_bit(table->bitmap, nr, table->alloc_hint);
2762306a36Sopenharmony_ci		if (ret != nr)
2862306a36Sopenharmony_ci			return ret;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci		if (table->alloc_hint == ctx->file_alloc_start)
3162306a36Sopenharmony_ci			break;
3262306a36Sopenharmony_ci		nr = table->alloc_hint;
3362306a36Sopenharmony_ci		table->alloc_hint = ctx->file_alloc_start;
3462306a36Sopenharmony_ci	} while (1);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return -ENFILE;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cibool io_alloc_file_tables(struct io_file_table *table, unsigned nr_files)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	table->files = kvcalloc(nr_files, sizeof(table->files[0]),
4262306a36Sopenharmony_ci				GFP_KERNEL_ACCOUNT);
4362306a36Sopenharmony_ci	if (unlikely(!table->files))
4462306a36Sopenharmony_ci		return false;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	table->bitmap = bitmap_zalloc(nr_files, GFP_KERNEL_ACCOUNT);
4762306a36Sopenharmony_ci	if (unlikely(!table->bitmap)) {
4862306a36Sopenharmony_ci		kvfree(table->files);
4962306a36Sopenharmony_ci		return false;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return true;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_civoid io_free_file_tables(struct io_file_table *table)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	kvfree(table->files);
5862306a36Sopenharmony_ci	bitmap_free(table->bitmap);
5962306a36Sopenharmony_ci	table->files = NULL;
6062306a36Sopenharmony_ci	table->bitmap = NULL;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int io_install_fixed_file(struct io_ring_ctx *ctx, struct file *file,
6462306a36Sopenharmony_ci				 u32 slot_index)
6562306a36Sopenharmony_ci	__must_hold(&req->ctx->uring_lock)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct io_fixed_file *file_slot;
6862306a36Sopenharmony_ci	int ret;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (io_is_uring_fops(file))
7162306a36Sopenharmony_ci		return -EBADF;
7262306a36Sopenharmony_ci	if (!ctx->file_data)
7362306a36Sopenharmony_ci		return -ENXIO;
7462306a36Sopenharmony_ci	if (slot_index >= ctx->nr_user_files)
7562306a36Sopenharmony_ci		return -EINVAL;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	slot_index = array_index_nospec(slot_index, ctx->nr_user_files);
7862306a36Sopenharmony_ci	file_slot = io_fixed_file_slot(&ctx->file_table, slot_index);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (file_slot->file_ptr) {
8162306a36Sopenharmony_ci		ret = io_queue_rsrc_removal(ctx->file_data, slot_index,
8262306a36Sopenharmony_ci					    io_slot_file(file_slot));
8362306a36Sopenharmony_ci		if (ret)
8462306a36Sopenharmony_ci			return ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		file_slot->file_ptr = 0;
8762306a36Sopenharmony_ci		io_file_bitmap_clear(&ctx->file_table, slot_index);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	*io_get_tag_slot(ctx->file_data, slot_index) = 0;
9162306a36Sopenharmony_ci	io_fixed_file_set(file_slot, file);
9262306a36Sopenharmony_ci	io_file_bitmap_set(&ctx->file_table, slot_index);
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciint __io_fixed_fd_install(struct io_ring_ctx *ctx, struct file *file,
9762306a36Sopenharmony_ci			  unsigned int file_slot)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	bool alloc_slot = file_slot == IORING_FILE_INDEX_ALLOC;
10062306a36Sopenharmony_ci	int ret;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (alloc_slot) {
10362306a36Sopenharmony_ci		ret = io_file_bitmap_get(ctx);
10462306a36Sopenharmony_ci		if (unlikely(ret < 0))
10562306a36Sopenharmony_ci			return ret;
10662306a36Sopenharmony_ci		file_slot = ret;
10762306a36Sopenharmony_ci	} else {
10862306a36Sopenharmony_ci		file_slot--;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	ret = io_install_fixed_file(ctx, file, file_slot);
11262306a36Sopenharmony_ci	if (!ret && alloc_slot)
11362306a36Sopenharmony_ci		ret = file_slot;
11462306a36Sopenharmony_ci	return ret;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Note when io_fixed_fd_install() returns error value, it will ensure
11862306a36Sopenharmony_ci * fput() is called correspondingly.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_ciint io_fixed_fd_install(struct io_kiocb *req, unsigned int issue_flags,
12162306a36Sopenharmony_ci			struct file *file, unsigned int file_slot)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct io_ring_ctx *ctx = req->ctx;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	io_ring_submit_lock(ctx, issue_flags);
12762306a36Sopenharmony_ci	ret = __io_fixed_fd_install(ctx, file, file_slot);
12862306a36Sopenharmony_ci	io_ring_submit_unlock(ctx, issue_flags);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (unlikely(ret < 0))
13162306a36Sopenharmony_ci		fput(file);
13262306a36Sopenharmony_ci	return ret;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint io_fixed_fd_remove(struct io_ring_ctx *ctx, unsigned int offset)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct io_fixed_file *file_slot;
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (unlikely(!ctx->file_data))
14162306a36Sopenharmony_ci		return -ENXIO;
14262306a36Sopenharmony_ci	if (offset >= ctx->nr_user_files)
14362306a36Sopenharmony_ci		return -EINVAL;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	offset = array_index_nospec(offset, ctx->nr_user_files);
14662306a36Sopenharmony_ci	file_slot = io_fixed_file_slot(&ctx->file_table, offset);
14762306a36Sopenharmony_ci	if (!file_slot->file_ptr)
14862306a36Sopenharmony_ci		return -EBADF;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = io_queue_rsrc_removal(ctx->file_data, offset,
15162306a36Sopenharmony_ci				    io_slot_file(file_slot));
15262306a36Sopenharmony_ci	if (ret)
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	file_slot->file_ptr = 0;
15662306a36Sopenharmony_ci	io_file_bitmap_clear(&ctx->file_table, offset);
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ciint io_register_file_alloc_range(struct io_ring_ctx *ctx,
16162306a36Sopenharmony_ci				 struct io_uring_file_index_range __user *arg)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct io_uring_file_index_range range;
16462306a36Sopenharmony_ci	u32 end;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (copy_from_user(&range, arg, sizeof(range)))
16762306a36Sopenharmony_ci		return -EFAULT;
16862306a36Sopenharmony_ci	if (check_add_overflow(range.off, range.len, &end))
16962306a36Sopenharmony_ci		return -EOVERFLOW;
17062306a36Sopenharmony_ci	if (range.resv || end > ctx->nr_user_files)
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	io_file_table_set_alloc_range(ctx, range.off, range.len);
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
176