162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/sched/signal.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/dcache.h>
562306a36Sopenharmony_ci#include <linux/path.h>
662306a36Sopenharmony_ci#include <linux/fdtable.h>
762306a36Sopenharmony_ci#include <linux/namei.h>
862306a36Sopenharmony_ci#include <linux/pid.h>
962306a36Sopenharmony_ci#include <linux/ptrace.h>
1062306a36Sopenharmony_ci#include <linux/bitmap.h>
1162306a36Sopenharmony_ci#include <linux/security.h>
1262306a36Sopenharmony_ci#include <linux/file.h>
1362306a36Sopenharmony_ci#include <linux/seq_file.h>
1462306a36Sopenharmony_ci#include <linux/fs.h>
1562306a36Sopenharmony_ci#include <linux/filelock.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/proc_fs.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../mount.h"
2062306a36Sopenharmony_ci#include "internal.h"
2162306a36Sopenharmony_ci#include "fd.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int seq_show(struct seq_file *m, void *v)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct files_struct *files = NULL;
2662306a36Sopenharmony_ci	int f_flags = 0, ret = -ENOENT;
2762306a36Sopenharmony_ci	struct file *file = NULL;
2862306a36Sopenharmony_ci	struct task_struct *task;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	task = get_proc_task(m->private);
3162306a36Sopenharmony_ci	if (!task)
3262306a36Sopenharmony_ci		return -ENOENT;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	task_lock(task);
3562306a36Sopenharmony_ci	files = task->files;
3662306a36Sopenharmony_ci	if (files) {
3762306a36Sopenharmony_ci		unsigned int fd = proc_fd(m->private);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		spin_lock(&files->file_lock);
4062306a36Sopenharmony_ci		file = files_lookup_fd_locked(files, fd);
4162306a36Sopenharmony_ci		if (file) {
4262306a36Sopenharmony_ci			struct fdtable *fdt = files_fdtable(files);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci			f_flags = file->f_flags;
4562306a36Sopenharmony_ci			if (close_on_exec(fd, fdt))
4662306a36Sopenharmony_ci				f_flags |= O_CLOEXEC;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci			get_file(file);
4962306a36Sopenharmony_ci			ret = 0;
5062306a36Sopenharmony_ci		}
5162306a36Sopenharmony_ci		spin_unlock(&files->file_lock);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	task_unlock(task);
5462306a36Sopenharmony_ci	put_task_struct(task);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (ret)
5762306a36Sopenharmony_ci		return ret;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n",
6062306a36Sopenharmony_ci		   (long long)file->f_pos, f_flags,
6162306a36Sopenharmony_ci		   real_mount(file->f_path.mnt)->mnt_id,
6262306a36Sopenharmony_ci		   file_inode(file)->i_ino);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* show_fd_locks() never deferences files so a stale value is safe */
6562306a36Sopenharmony_ci	show_fd_locks(m, file, files);
6662306a36Sopenharmony_ci	if (seq_has_overflowed(m))
6762306a36Sopenharmony_ci		goto out;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (file->f_op->show_fdinfo)
7062306a36Sopenharmony_ci		file->f_op->show_fdinfo(m, file);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciout:
7362306a36Sopenharmony_ci	fput(file);
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int proc_fdinfo_access_allowed(struct inode *inode)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	bool allowed = false;
8062306a36Sopenharmony_ci	struct task_struct *task = get_proc_task(inode);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!task)
8362306a36Sopenharmony_ci		return -ESRCH;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
8662306a36Sopenharmony_ci	put_task_struct(task);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (!allowed)
8962306a36Sopenharmony_ci		return -EACCES;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int seq_fdinfo_open(struct inode *inode, struct file *file)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret = proc_fdinfo_access_allowed(inode);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (ret)
9962306a36Sopenharmony_ci		return ret;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return single_open(file, seq_show, inode);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const struct file_operations proc_fdinfo_file_operations = {
10562306a36Sopenharmony_ci	.open		= seq_fdinfo_open,
10662306a36Sopenharmony_ci	.read		= seq_read,
10762306a36Sopenharmony_ci	.llseek		= seq_lseek,
10862306a36Sopenharmony_ci	.release	= single_release,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic bool tid_fd_mode(struct task_struct *task, unsigned fd, fmode_t *mode)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct file *file;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	rcu_read_lock();
11662306a36Sopenharmony_ci	file = task_lookup_fd_rcu(task, fd);
11762306a36Sopenharmony_ci	if (file)
11862306a36Sopenharmony_ci		*mode = file->f_mode;
11962306a36Sopenharmony_ci	rcu_read_unlock();
12062306a36Sopenharmony_ci	return !!file;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void tid_fd_update_inode(struct task_struct *task, struct inode *inode,
12462306a36Sopenharmony_ci				fmode_t f_mode)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (S_ISLNK(inode->i_mode)) {
12962306a36Sopenharmony_ci		unsigned i_mode = S_IFLNK;
13062306a36Sopenharmony_ci		if (f_mode & FMODE_READ)
13162306a36Sopenharmony_ci			i_mode |= S_IRUSR | S_IXUSR;
13262306a36Sopenharmony_ci		if (f_mode & FMODE_WRITE)
13362306a36Sopenharmony_ci			i_mode |= S_IWUSR | S_IXUSR;
13462306a36Sopenharmony_ci		inode->i_mode = i_mode;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	security_task_to_inode(task, inode);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int tid_fd_revalidate(struct dentry *dentry, unsigned int flags)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct task_struct *task;
14262306a36Sopenharmony_ci	struct inode *inode;
14362306a36Sopenharmony_ci	unsigned int fd;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
14662306a36Sopenharmony_ci		return -ECHILD;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	inode = d_inode(dentry);
14962306a36Sopenharmony_ci	task = get_proc_task(inode);
15062306a36Sopenharmony_ci	fd = proc_fd(inode);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (task) {
15362306a36Sopenharmony_ci		fmode_t f_mode;
15462306a36Sopenharmony_ci		if (tid_fd_mode(task, fd, &f_mode)) {
15562306a36Sopenharmony_ci			tid_fd_update_inode(task, inode, f_mode);
15662306a36Sopenharmony_ci			put_task_struct(task);
15762306a36Sopenharmony_ci			return 1;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci		put_task_struct(task);
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const struct dentry_operations tid_fd_dentry_operations = {
16562306a36Sopenharmony_ci	.d_revalidate	= tid_fd_revalidate,
16662306a36Sopenharmony_ci	.d_delete	= pid_delete_dentry,
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int proc_fd_link(struct dentry *dentry, struct path *path)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct task_struct *task;
17262306a36Sopenharmony_ci	int ret = -ENOENT;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	task = get_proc_task(d_inode(dentry));
17562306a36Sopenharmony_ci	if (task) {
17662306a36Sopenharmony_ci		unsigned int fd = proc_fd(d_inode(dentry));
17762306a36Sopenharmony_ci		struct file *fd_file;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		fd_file = fget_task(task, fd);
18062306a36Sopenharmony_ci		if (fd_file) {
18162306a36Sopenharmony_ci			*path = fd_file->f_path;
18262306a36Sopenharmony_ci			path_get(&fd_file->f_path);
18362306a36Sopenharmony_ci			ret = 0;
18462306a36Sopenharmony_ci			fput(fd_file);
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		put_task_struct(task);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return ret;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistruct fd_data {
19362306a36Sopenharmony_ci	fmode_t mode;
19462306a36Sopenharmony_ci	unsigned fd;
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct dentry *proc_fd_instantiate(struct dentry *dentry,
19862306a36Sopenharmony_ci	struct task_struct *task, const void *ptr)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	const struct fd_data *data = ptr;
20162306a36Sopenharmony_ci	struct proc_inode *ei;
20262306a36Sopenharmony_ci	struct inode *inode;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK);
20562306a36Sopenharmony_ci	if (!inode)
20662306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ei = PROC_I(inode);
20962306a36Sopenharmony_ci	ei->fd = data->fd;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	inode->i_op = &proc_pid_link_inode_operations;
21262306a36Sopenharmony_ci	inode->i_size = 64;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ei->op.proc_get_link = proc_fd_link;
21562306a36Sopenharmony_ci	tid_fd_update_inode(task, inode, data->mode);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	d_set_d_op(dentry, &tid_fd_dentry_operations);
21862306a36Sopenharmony_ci	return d_splice_alias(inode, dentry);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct dentry *proc_lookupfd_common(struct inode *dir,
22262306a36Sopenharmony_ci					   struct dentry *dentry,
22362306a36Sopenharmony_ci					   instantiate_t instantiate)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct task_struct *task = get_proc_task(dir);
22662306a36Sopenharmony_ci	struct fd_data data = {.fd = name_to_int(&dentry->d_name)};
22762306a36Sopenharmony_ci	struct dentry *result = ERR_PTR(-ENOENT);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (!task)
23062306a36Sopenharmony_ci		goto out_no_task;
23162306a36Sopenharmony_ci	if (data.fd == ~0U)
23262306a36Sopenharmony_ci		goto out;
23362306a36Sopenharmony_ci	if (!tid_fd_mode(task, data.fd, &data.mode))
23462306a36Sopenharmony_ci		goto out;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	result = instantiate(dentry, task, &data);
23762306a36Sopenharmony_ciout:
23862306a36Sopenharmony_ci	put_task_struct(task);
23962306a36Sopenharmony_ciout_no_task:
24062306a36Sopenharmony_ci	return result;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int proc_readfd_common(struct file *file, struct dir_context *ctx,
24462306a36Sopenharmony_ci			      instantiate_t instantiate)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct task_struct *p = get_proc_task(file_inode(file));
24762306a36Sopenharmony_ci	unsigned int fd;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (!p)
25062306a36Sopenharmony_ci		return -ENOENT;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!dir_emit_dots(file, ctx))
25362306a36Sopenharmony_ci		goto out;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	rcu_read_lock();
25662306a36Sopenharmony_ci	for (fd = ctx->pos - 2;; fd++) {
25762306a36Sopenharmony_ci		struct file *f;
25862306a36Sopenharmony_ci		struct fd_data data;
25962306a36Sopenharmony_ci		char name[10 + 1];
26062306a36Sopenharmony_ci		unsigned int len;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		f = task_lookup_next_fd_rcu(p, &fd);
26362306a36Sopenharmony_ci		ctx->pos = fd + 2LL;
26462306a36Sopenharmony_ci		if (!f)
26562306a36Sopenharmony_ci			break;
26662306a36Sopenharmony_ci		data.mode = f->f_mode;
26762306a36Sopenharmony_ci		rcu_read_unlock();
26862306a36Sopenharmony_ci		data.fd = fd;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		len = snprintf(name, sizeof(name), "%u", fd);
27162306a36Sopenharmony_ci		if (!proc_fill_cache(file, ctx,
27262306a36Sopenharmony_ci				     name, len, instantiate, p,
27362306a36Sopenharmony_ci				     &data))
27462306a36Sopenharmony_ci			goto out;
27562306a36Sopenharmony_ci		cond_resched();
27662306a36Sopenharmony_ci		rcu_read_lock();
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	rcu_read_unlock();
27962306a36Sopenharmony_ciout:
28062306a36Sopenharmony_ci	put_task_struct(p);
28162306a36Sopenharmony_ci	return 0;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int proc_readfd_count(struct inode *inode, loff_t *count)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct task_struct *p = get_proc_task(inode);
28762306a36Sopenharmony_ci	struct fdtable *fdt;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!p)
29062306a36Sopenharmony_ci		return -ENOENT;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	task_lock(p);
29362306a36Sopenharmony_ci	if (p->files) {
29462306a36Sopenharmony_ci		rcu_read_lock();
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		fdt = files_fdtable(p->files);
29762306a36Sopenharmony_ci		*count = bitmap_weight(fdt->open_fds, fdt->max_fds);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		rcu_read_unlock();
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	task_unlock(p);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	put_task_struct(p);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int proc_readfd(struct file *file, struct dir_context *ctx)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	return proc_readfd_common(file, ctx, proc_fd_instantiate);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciconst struct file_operations proc_fd_operations = {
31462306a36Sopenharmony_ci	.read		= generic_read_dir,
31562306a36Sopenharmony_ci	.iterate_shared	= proc_readfd,
31662306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
31762306a36Sopenharmony_ci};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct dentry *proc_lookupfd(struct inode *dir, struct dentry *dentry,
32062306a36Sopenharmony_ci				    unsigned int flags)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return proc_lookupfd_common(dir, dentry, proc_fd_instantiate);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * /proc/pid/fd needs a special permission handler so that a process can still
32762306a36Sopenharmony_ci * access /proc/self/fd after it has executed a setuid().
32862306a36Sopenharmony_ci */
32962306a36Sopenharmony_ciint proc_fd_permission(struct mnt_idmap *idmap,
33062306a36Sopenharmony_ci		       struct inode *inode, int mask)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct task_struct *p;
33362306a36Sopenharmony_ci	int rv;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	rv = generic_permission(&nop_mnt_idmap, inode, mask);
33662306a36Sopenharmony_ci	if (rv == 0)
33762306a36Sopenharmony_ci		return rv;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	rcu_read_lock();
34062306a36Sopenharmony_ci	p = pid_task(proc_pid(inode), PIDTYPE_PID);
34162306a36Sopenharmony_ci	if (p && same_thread_group(p, current))
34262306a36Sopenharmony_ci		rv = 0;
34362306a36Sopenharmony_ci	rcu_read_unlock();
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return rv;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int proc_fd_getattr(struct mnt_idmap *idmap,
34962306a36Sopenharmony_ci			const struct path *path, struct kstat *stat,
35062306a36Sopenharmony_ci			u32 request_mask, unsigned int query_flags)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct inode *inode = d_inode(path->dentry);
35362306a36Sopenharmony_ci	int rv = 0;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* If it's a directory, put the number of open fds there */
35862306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode)) {
35962306a36Sopenharmony_ci		rv = proc_readfd_count(inode, &stat->size);
36062306a36Sopenharmony_ci		if (rv < 0)
36162306a36Sopenharmony_ci			return rv;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return rv;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ciconst struct inode_operations proc_fd_inode_operations = {
36862306a36Sopenharmony_ci	.lookup		= proc_lookupfd,
36962306a36Sopenharmony_ci	.permission	= proc_fd_permission,
37062306a36Sopenharmony_ci	.getattr	= proc_fd_getattr,
37162306a36Sopenharmony_ci	.setattr	= proc_setattr,
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
37562306a36Sopenharmony_ci	struct task_struct *task, const void *ptr)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	const struct fd_data *data = ptr;
37862306a36Sopenharmony_ci	struct proc_inode *ei;
37962306a36Sopenharmony_ci	struct inode *inode;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	inode = proc_pid_make_inode(dentry->d_sb, task, S_IFREG | S_IRUGO);
38262306a36Sopenharmony_ci	if (!inode)
38362306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	ei = PROC_I(inode);
38662306a36Sopenharmony_ci	ei->fd = data->fd;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	inode->i_fop = &proc_fdinfo_file_operations;
38962306a36Sopenharmony_ci	tid_fd_update_inode(task, inode, 0);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	d_set_d_op(dentry, &tid_fd_dentry_operations);
39262306a36Sopenharmony_ci	return d_splice_alias(inode, dentry);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic struct dentry *
39662306a36Sopenharmony_ciproc_lookupfdinfo(struct inode *dir, struct dentry *dentry, unsigned int flags)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	return proc_lookupfd_common(dir, dentry, proc_fdinfo_instantiate);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int proc_readfdinfo(struct file *file, struct dir_context *ctx)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	return proc_readfd_common(file, ctx,
40462306a36Sopenharmony_ci				  proc_fdinfo_instantiate);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int proc_open_fdinfo(struct inode *inode, struct file *file)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	int ret = proc_fdinfo_access_allowed(inode);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (ret)
41262306a36Sopenharmony_ci		return ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciconst struct inode_operations proc_fdinfo_inode_operations = {
41862306a36Sopenharmony_ci	.lookup		= proc_lookupfdinfo,
41962306a36Sopenharmony_ci	.setattr	= proc_setattr,
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ciconst struct file_operations proc_fdinfo_operations = {
42362306a36Sopenharmony_ci	.open		= proc_open_fdinfo,
42462306a36Sopenharmony_ci	.read		= generic_read_dir,
42562306a36Sopenharmony_ci	.iterate_shared	= proc_readfdinfo,
42662306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
42762306a36Sopenharmony_ci};
428