18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 38c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 48c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 58c2ecf20Sopenharmony_ci#include <linux/namei.h> 68c2ecf20Sopenharmony_ci#include <linux/file.h> 78c2ecf20Sopenharmony_ci#include <linux/utsname.h> 88c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 98c2ecf20Sopenharmony_ci#include <linux/ipc_namespace.h> 108c2ecf20Sopenharmony_ci#include <linux/pid_namespace.h> 118c2ecf20Sopenharmony_ci#include <linux/user_namespace.h> 128c2ecf20Sopenharmony_ci#include "internal.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic const struct proc_ns_operations *ns_entries[] = { 168c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_NS 178c2ecf20Sopenharmony_ci &netns_operations, 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci#ifdef CONFIG_UTS_NS 208c2ecf20Sopenharmony_ci &utsns_operations, 218c2ecf20Sopenharmony_ci#endif 228c2ecf20Sopenharmony_ci#ifdef CONFIG_IPC_NS 238c2ecf20Sopenharmony_ci &ipcns_operations, 248c2ecf20Sopenharmony_ci#endif 258c2ecf20Sopenharmony_ci#ifdef CONFIG_PID_NS 268c2ecf20Sopenharmony_ci &pidns_operations, 278c2ecf20Sopenharmony_ci &pidns_for_children_operations, 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci#ifdef CONFIG_USER_NS 308c2ecf20Sopenharmony_ci &userns_operations, 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci &mntns_operations, 338c2ecf20Sopenharmony_ci#ifdef CONFIG_CGROUPS 348c2ecf20Sopenharmony_ci &cgroupns_operations, 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci#ifdef CONFIG_TIME_NS 378c2ecf20Sopenharmony_ci &timens_operations, 388c2ecf20Sopenharmony_ci &timens_for_children_operations, 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const char *proc_ns_get_link(struct dentry *dentry, 438c2ecf20Sopenharmony_ci struct inode *inode, 448c2ecf20Sopenharmony_ci struct delayed_call *done) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; 478c2ecf20Sopenharmony_ci struct task_struct *task; 488c2ecf20Sopenharmony_ci struct path ns_path; 498c2ecf20Sopenharmony_ci int error = -EACCES; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!dentry) 528c2ecf20Sopenharmony_ci return ERR_PTR(-ECHILD); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci task = get_proc_task(inode); 558c2ecf20Sopenharmony_ci if (!task) 568c2ecf20Sopenharmony_ci return ERR_PTR(-EACCES); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) 598c2ecf20Sopenharmony_ci goto out; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci error = ns_get_path(&ns_path, task, ns_ops); 628c2ecf20Sopenharmony_ci if (error) 638c2ecf20Sopenharmony_ci goto out; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci error = nd_jump_link(&ns_path); 668c2ecf20Sopenharmony_ciout: 678c2ecf20Sopenharmony_ci put_task_struct(task); 688c2ecf20Sopenharmony_ci return ERR_PTR(error); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 748c2ecf20Sopenharmony_ci const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; 758c2ecf20Sopenharmony_ci struct task_struct *task; 768c2ecf20Sopenharmony_ci char name[50]; 778c2ecf20Sopenharmony_ci int res = -EACCES; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci task = get_proc_task(inode); 808c2ecf20Sopenharmony_ci if (!task) 818c2ecf20Sopenharmony_ci return res; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { 848c2ecf20Sopenharmony_ci res = ns_get_name(name, sizeof(name), task, ns_ops); 858c2ecf20Sopenharmony_ci if (res >= 0) 868c2ecf20Sopenharmony_ci res = readlink_copy(buffer, buflen, name); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci put_task_struct(task); 898c2ecf20Sopenharmony_ci return res; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct inode_operations proc_ns_link_inode_operations = { 938c2ecf20Sopenharmony_ci .readlink = proc_ns_readlink, 948c2ecf20Sopenharmony_ci .get_link = proc_ns_get_link, 958c2ecf20Sopenharmony_ci .setattr = proc_setattr, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct dentry *proc_ns_instantiate(struct dentry *dentry, 998c2ecf20Sopenharmony_ci struct task_struct *task, const void *ptr) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci const struct proc_ns_operations *ns_ops = ptr; 1028c2ecf20Sopenharmony_ci struct inode *inode; 1038c2ecf20Sopenharmony_ci struct proc_inode *ei; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRWXUGO); 1068c2ecf20Sopenharmony_ci if (!inode) 1078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ei = PROC_I(inode); 1108c2ecf20Sopenharmony_ci inode->i_op = &proc_ns_link_inode_operations; 1118c2ecf20Sopenharmony_ci ei->ns_ops = ns_ops; 1128c2ecf20Sopenharmony_ci pid_update_inode(task, inode); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci d_set_d_op(dentry, &pid_dentry_operations); 1158c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct task_struct *task = get_proc_task(file_inode(file)); 1218c2ecf20Sopenharmony_ci const struct proc_ns_operations **entry, **last; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!task) 1248c2ecf20Sopenharmony_ci return -ENOENT; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries)) 1298c2ecf20Sopenharmony_ci goto out; 1308c2ecf20Sopenharmony_ci entry = ns_entries + (ctx->pos - 2); 1318c2ecf20Sopenharmony_ci last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; 1328c2ecf20Sopenharmony_ci while (entry <= last) { 1338c2ecf20Sopenharmony_ci const struct proc_ns_operations *ops = *entry; 1348c2ecf20Sopenharmony_ci if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name), 1358c2ecf20Sopenharmony_ci proc_ns_instantiate, task, ops)) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci ctx->pos++; 1388c2ecf20Sopenharmony_ci entry++; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ciout: 1418c2ecf20Sopenharmony_ci put_task_struct(task); 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciconst struct file_operations proc_ns_dir_operations = { 1468c2ecf20Sopenharmony_ci .read = generic_read_dir, 1478c2ecf20Sopenharmony_ci .iterate_shared = proc_ns_dir_readdir, 1488c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic struct dentry *proc_ns_dir_lookup(struct inode *dir, 1528c2ecf20Sopenharmony_ci struct dentry *dentry, unsigned int flags) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct task_struct *task = get_proc_task(dir); 1558c2ecf20Sopenharmony_ci const struct proc_ns_operations **entry, **last; 1568c2ecf20Sopenharmony_ci unsigned int len = dentry->d_name.len; 1578c2ecf20Sopenharmony_ci struct dentry *res = ERR_PTR(-ENOENT); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!task) 1608c2ecf20Sopenharmony_ci goto out_no_task; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci last = &ns_entries[ARRAY_SIZE(ns_entries)]; 1638c2ecf20Sopenharmony_ci for (entry = ns_entries; entry < last; entry++) { 1648c2ecf20Sopenharmony_ci if (strlen((*entry)->name) != len) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci if (!memcmp(dentry->d_name.name, (*entry)->name, len)) 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (entry == last) 1708c2ecf20Sopenharmony_ci goto out; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci res = proc_ns_instantiate(dentry, task, *entry); 1738c2ecf20Sopenharmony_ciout: 1748c2ecf20Sopenharmony_ci put_task_struct(task); 1758c2ecf20Sopenharmony_ciout_no_task: 1768c2ecf20Sopenharmony_ci return res; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciconst struct inode_operations proc_ns_dir_inode_operations = { 1808c2ecf20Sopenharmony_ci .lookup = proc_ns_dir_lookup, 1818c2ecf20Sopenharmony_ci .getattr = pid_getattr, 1828c2ecf20Sopenharmony_ci .setattr = proc_setattr, 1838c2ecf20Sopenharmony_ci}; 184