mirror of
https://github.com/clearlinux/graphene.git
synced 2026-10-03 23:48:19 +00:00
[LibOS] Refactor /proc/[tid|pid] logic
Graphene emulates `/proc/[tid|pid]` subdirectory using the pseudo-FS logic under fs/proc/thread.c and fs/proc/ipc-thread.c. This commit refactors these files: name changes, better structure, using string.h functions, small bug fixes. The IPC-thread logic is significantly simplified and partly commented out (it was never used and probably will never be used, but still keeping it just in case). The LibOS `proc_common` test is augmented to test more functionality.
This commit is contained in:
@@ -483,8 +483,8 @@ extern struct shim_fs_ops proc_fs_ops;
|
||||
extern struct shim_d_ops proc_d_ops;
|
||||
|
||||
struct pseudo_name_ops {
|
||||
int (*match_name)(const char* name);
|
||||
int (*list_name)(const char* name, struct shim_dirent** buf, int count);
|
||||
int (*match_path)(const char* relpath);
|
||||
int (*list_dirents)(const char* relpath, struct shim_dirent** buf, size_t size);
|
||||
};
|
||||
|
||||
static inline dev_t makedev(unsigned int major, unsigned int minor) {
|
||||
|
||||
+18
-20
@@ -2,42 +2,40 @@
|
||||
/* Copyright (C) 2014 Stony Brook University */
|
||||
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* This file contains the implementation of `/proc` pseudo-filesystem.
|
||||
*/
|
||||
|
||||
#include "shim_fs.h"
|
||||
|
||||
extern const struct pseudo_name_ops nm_thread;
|
||||
extern const struct pseudo_fs_ops fs_thread;
|
||||
extern const struct pseudo_dir dir_thread;
|
||||
extern const struct pseudo_name_ops proc_thread_name_ops;
|
||||
extern const struct pseudo_fs_ops proc_thread_fs_ops;
|
||||
extern const struct pseudo_dir proc_thread_dir;
|
||||
|
||||
extern const struct pseudo_name_ops nm_ipc_thread;
|
||||
extern const struct pseudo_fs_ops fs_ipc_thread;
|
||||
extern const struct pseudo_dir dir_ipc_thread;
|
||||
extern const struct pseudo_name_ops proc_ipc_thread_name_ops;
|
||||
extern const struct pseudo_fs_ops proc_ipc_thread_fs_ops;
|
||||
extern const struct pseudo_dir proc_ipc_thread_dir;
|
||||
|
||||
extern const struct pseudo_fs_ops fs_meminfo;
|
||||
extern const struct pseudo_fs_ops proc_meminfo_fs_ops;
|
||||
|
||||
extern const struct pseudo_fs_ops fs_cpuinfo;
|
||||
extern const struct pseudo_fs_ops proc_cpuinfo_fs_ops;
|
||||
|
||||
static const struct pseudo_dir proc_root_dir = {
|
||||
.size = 5,
|
||||
.ent = {
|
||||
{.name = "self",
|
||||
.fs_ops = &fs_thread,
|
||||
.dir = &dir_thread},
|
||||
{.name_ops = &nm_thread,
|
||||
.fs_ops = &fs_thread,
|
||||
.dir = &dir_thread},
|
||||
{.name_ops = &nm_ipc_thread,
|
||||
.fs_ops = &fs_ipc_thread,
|
||||
.dir = &dir_ipc_thread},
|
||||
.fs_ops = &proc_thread_fs_ops,
|
||||
.dir = &proc_thread_dir},
|
||||
{.name_ops = &proc_thread_name_ops,
|
||||
.fs_ops = &proc_thread_fs_ops,
|
||||
.dir = &proc_thread_dir},
|
||||
{.name_ops = &proc_ipc_thread_name_ops,
|
||||
.fs_ops = &proc_ipc_thread_fs_ops,
|
||||
.dir = &proc_ipc_thread_dir},
|
||||
{.name = "meminfo",
|
||||
.fs_ops = &fs_meminfo,
|
||||
.fs_ops = &proc_meminfo_fs_ops,
|
||||
.type = LINUX_DT_REG},
|
||||
{.name = "cpuinfo",
|
||||
.fs_ops = &fs_cpuinfo,
|
||||
.fs_ops = &proc_cpuinfo_fs_ops,
|
||||
.type = LINUX_DT_REG},
|
||||
}};
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
/* Copyright (C) 2014 Stony Brook University */
|
||||
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* This file contains the implementation of `/proc/meminfo` and `/proc/cpuinfo`.
|
||||
*/
|
||||
|
||||
@@ -179,13 +177,13 @@ static int proc_cpuinfo_open(struct shim_handle* hdl, const char* name, int flag
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pseudo_fs_ops fs_meminfo = {
|
||||
struct pseudo_fs_ops proc_meminfo_fs_ops = {
|
||||
.mode = &proc_info_mode,
|
||||
.stat = &proc_info_stat,
|
||||
.open = &proc_meminfo_open,
|
||||
};
|
||||
|
||||
struct pseudo_fs_ops fs_cpuinfo = {
|
||||
struct pseudo_fs_ops proc_cpuinfo_fs_ops = {
|
||||
.mode = &proc_info_mode,
|
||||
.stat = &proc_info_stat,
|
||||
.open = &proc_cpuinfo_open,
|
||||
|
||||
+219
-269
@@ -1,8 +1,14 @@
|
||||
#include <asm/fcntl.h>
|
||||
#include <asm/mman.h>
|
||||
/* SPDX-License-Identifier: LGPL-3.0-or-later */
|
||||
/* Copyright (C) 2014 Stony Brook University */
|
||||
/* Copyright (C) 2020 Intel Corporation */
|
||||
|
||||
/*!
|
||||
* This file contains the implementation of `/proc/[remote-pid]` dir. Currently only adding remote
|
||||
* PIDs to the list of `/proc/[pids]` and checking PID existence ("match path") are implemented.
|
||||
*/
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/fcntl.h>
|
||||
|
||||
#include "pal.h"
|
||||
#include "pal_error.h"
|
||||
@@ -17,366 +23,310 @@
|
||||
#include "shim_utils.h"
|
||||
#include "stat.h"
|
||||
|
||||
static int parse_ipc_thread_name(const char* name, IDTYPE* pidptr, const char** next,
|
||||
size_t* next_len, const char** nextnext) {
|
||||
const char* p = name;
|
||||
IDTYPE pid = 0;
|
||||
static struct pid_status_cache {
|
||||
size_t status_num;
|
||||
struct pid_status* status;
|
||||
} g_pid_status_cache;
|
||||
|
||||
if (*p == '/')
|
||||
p++;
|
||||
static struct shim_lock g_pid_status_lock;
|
||||
|
||||
for (; *p && *p != '/'; p++) {
|
||||
if (*p < '0' || *p > '9')
|
||||
return -ENOENT;
|
||||
/* returns PID of the remote process found in relpath and pointer to the rest of relpath string
|
||||
* (e.g. "42/cwd" returns 42 in `*pid_ptr` and pointer to "cwd" in `rest` if process 42 exists) */
|
||||
static int get_pid_from_relpath(const char* relpath, IDTYPE* pid_ptr, char** rest) {
|
||||
if (*relpath == '\0' || *relpath == '/')
|
||||
return -ENOENT;
|
||||
|
||||
pid = pid * 10 + *p - '0';
|
||||
char* pid_end = NULL;
|
||||
IDTYPE pid = (IDTYPE)strtol(relpath, &pid_end, /*base=*/10);
|
||||
|
||||
if (!pid_end || (*pid_end != '\0' && *pid_end != '/'))
|
||||
return -ENOENT;
|
||||
|
||||
if (!create_lock_runtime(&g_pid_status_lock))
|
||||
return -ENOMEM;
|
||||
|
||||
lock(&g_pid_status_lock);
|
||||
|
||||
if (!g_pid_status_cache.status_num || !g_pid_status_cache.status) {
|
||||
unlock(&g_pid_status_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
if (*(p++) == '/' && *p) {
|
||||
*next = p;
|
||||
|
||||
if (next_len || nextnext)
|
||||
for (; *p && *p != '/'; p++)
|
||||
;
|
||||
|
||||
if (next_len)
|
||||
*next_len = p - *next;
|
||||
|
||||
if (nextnext)
|
||||
*nextnext = (*(p++) == '/' && *p) ? p : NULL;
|
||||
} else {
|
||||
*next = NULL;
|
||||
bool found_pid = false;
|
||||
for (size_t i = 0; i < g_pid_status_cache.status_num; i++) {
|
||||
if (g_pid_status_cache.status[i].pid == pid) {
|
||||
found_pid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pidptr)
|
||||
*pidptr = pid;
|
||||
unlock(&g_pid_status_lock);
|
||||
|
||||
if (!found_pid)
|
||||
return -ENOENT;
|
||||
|
||||
*pid_ptr = pid;
|
||||
if (rest)
|
||||
*rest = *pid_end == '\0' ? NULL : pid_end + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_ipc_thread_link(const char* name, struct shim_qstr* link,
|
||||
struct shim_dentry** dentptr) {
|
||||
const char* next;
|
||||
const char* nextnext;
|
||||
size_t next_len;
|
||||
IDTYPE pid;
|
||||
/* returns dentry corresponding to PID's "root"/"cwd"/"exe" in relpath (e.g. "[pid]/root")
|
||||
* FIXME: currently only a stub because there is no shared multi-process file system in Graphene */
|
||||
static int get_ipc_genericlink_dentry(const char* relpath, struct shim_dentry** dent_ptr) {
|
||||
int ret;
|
||||
assert(dent_ptr);
|
||||
|
||||
int ret = parse_ipc_thread_name(name, &pid, &next, &next_len, &nextnext);
|
||||
IDTYPE pid = 0;
|
||||
char* rest = NULL;
|
||||
ret = get_pid_from_relpath(relpath, &pid, &rest);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!rest)
|
||||
return -ENOENT;
|
||||
|
||||
struct shim_dentry* dent = NULL;
|
||||
|
||||
enum pid_meta_code ipc_code;
|
||||
void* ipc_data = NULL;
|
||||
|
||||
if (!memcmp(next, "root", next_len)) {
|
||||
if (strstartswith(rest, "root")) {
|
||||
ipc_code = PID_META_ROOT;
|
||||
goto do_ipc;
|
||||
}
|
||||
|
||||
if (!memcmp(next, "cwd", next_len)) {
|
||||
} else if (strstartswith(rest, "cwd")) {
|
||||
ipc_code = PID_META_CWD;
|
||||
goto do_ipc;
|
||||
}
|
||||
|
||||
if (!memcmp(next, "exe", next_len)) {
|
||||
} else if (strstartswith(rest, "exe")) {
|
||||
ipc_code = PID_META_EXEC;
|
||||
goto do_ipc;
|
||||
} else {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
do_ipc:
|
||||
#if 0
|
||||
/* this doesn't work because there is no shared multi-process file system in Graphene,
|
||||
* thus path_lookupat() on a returned `ipc_data` path doesn't make much sense */
|
||||
void* ipc_data = NULL;
|
||||
ret = ipc_pid_getmeta_send(pid, ipc_code, &ipc_data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (link)
|
||||
qstrsetstr(link, (char*)ipc_data, strlen((char*)ipc_data));
|
||||
|
||||
if (dentptr) {
|
||||
/* XXX: Not sure how to handle this case yet */
|
||||
__abort();
|
||||
ret = path_lookupat(NULL, (char*)ipc_data, 0, &dent, NULL);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
get_dentry(dent);
|
||||
*dentptr = dent;
|
||||
ret = path_lookupat(NULL, (char*)ipc_data, 0, &dent, NULL);
|
||||
if (ret < 0) {
|
||||
free(ipc_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
out:
|
||||
if (dent)
|
||||
put_dentry(dent);
|
||||
return ret;
|
||||
free(ipc_data);
|
||||
get_dentry(dent);
|
||||
#else
|
||||
__UNUSED(pid);
|
||||
__UNUSED(ipc_code);
|
||||
__UNUSED(dent);
|
||||
return -ENOENT;
|
||||
#endif
|
||||
|
||||
*dent_ptr = dent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_link_open(struct shim_handle* hdl, const char* name, int flags) {
|
||||
struct shim_dentry* dent;
|
||||
static int proc_ipc_thread_genericlink_open(struct shim_handle* hdl, const char* relpath,
|
||||
int flags) {
|
||||
int ret;
|
||||
|
||||
int ret = find_ipc_thread_link(name, NULL, &dent);
|
||||
struct shim_dentry* dent = NULL;
|
||||
ret = get_ipc_genericlink_dentry(relpath, &dent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->open) {
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
put_dentry(dent);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = dent->fs->d_ops->open(hdl, dent, flags);
|
||||
out:
|
||||
put_dentry(dent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_link_mode(const char* name, mode_t* mode) {
|
||||
struct shim_dentry* dent;
|
||||
static int proc_ipc_thread_genericlink_mode(const char* relpath, mode_t* mode) {
|
||||
int ret;
|
||||
|
||||
int ret = find_ipc_thread_link(name, NULL, &dent);
|
||||
struct shim_dentry* dent = NULL;
|
||||
ret = get_ipc_genericlink_dentry(relpath, &dent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->mode) {
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
put_dentry(dent);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = dent->fs->d_ops->mode(dent, mode);
|
||||
out:
|
||||
put_dentry(dent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_link_stat(const char* name, struct stat* buf) {
|
||||
struct shim_dentry* dent;
|
||||
static int proc_ipc_thread_genericlink_stat(const char* relpath, struct stat* buf) {
|
||||
int ret;
|
||||
|
||||
int ret = find_ipc_thread_link(name, NULL, &dent);
|
||||
struct shim_dentry* dent = NULL;
|
||||
ret = get_ipc_genericlink_dentry(relpath, &dent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->stat) {
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
put_dentry(dent);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = dent->fs->d_ops->stat(dent, buf);
|
||||
out:
|
||||
put_dentry(dent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_link_follow_link(const char* name, struct shim_qstr* link) {
|
||||
return find_ipc_thread_link(name, link, NULL);
|
||||
}
|
||||
static int proc_ipc_thread_genericlink_follow(const char* relpath, struct shim_qstr* link) {
|
||||
int ret;
|
||||
|
||||
static const struct pseudo_fs_ops fs_ipc_thread_link = {
|
||||
.open = &proc_ipc_thread_link_open,
|
||||
.mode = &proc_ipc_thread_link_mode,
|
||||
.stat = &proc_ipc_thread_link_stat,
|
||||
.follow_link = &proc_ipc_thread_link_follow_link,
|
||||
};
|
||||
struct shim_dentry* dent = NULL;
|
||||
ret = get_ipc_genericlink_dentry(relpath, &dent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
static struct pid_status_cache {
|
||||
uint32_t ref_count;
|
||||
bool dirty;
|
||||
size_t nstatus;
|
||||
struct pid_status* status;
|
||||
} * pid_status_cache;
|
||||
|
||||
static struct shim_lock status_lock;
|
||||
|
||||
static int proc_match_ipc_thread(const char* name) {
|
||||
IDTYPE pid;
|
||||
if (parse_ipc_thread_name(name, &pid, NULL, NULL, NULL) < 0)
|
||||
return 0;
|
||||
|
||||
if (!create_lock_runtime(&status_lock)) {
|
||||
if (!dentry_get_path_into_qstr(dent, link)) {
|
||||
put_dentry(dent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
lock(&status_lock);
|
||||
|
||||
if (pid_status_cache)
|
||||
for (size_t i = 0; i < pid_status_cache->nstatus; i++)
|
||||
if (pid_status_cache->status[i].pid == pid) {
|
||||
unlock(&status_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unlock(&status_lock);
|
||||
put_dentry(dent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_dir_mode(const char* name, mode_t* mode) {
|
||||
const char* next;
|
||||
size_t next_len;
|
||||
IDTYPE pid;
|
||||
int ret = parse_ipc_thread_name(name, &pid, &next, &next_len, NULL);
|
||||
static const struct pseudo_fs_ops proc_ipc_thread_genericlink_fs_ops = {
|
||||
.open = &proc_ipc_thread_genericlink_open,
|
||||
.mode = &proc_ipc_thread_genericlink_mode,
|
||||
.stat = &proc_ipc_thread_genericlink_stat,
|
||||
.follow_link = &proc_ipc_thread_genericlink_follow,
|
||||
};
|
||||
|
||||
/* return 0 if prefix of relpath (in format "[pid]") is a valid PID, or negative error code
|
||||
* otherwise */
|
||||
static int proc_match_ipc_thread(const char* relpath) {
|
||||
IDTYPE dummy_pid;
|
||||
return get_pid_from_relpath(relpath, &dummy_pid, /*rest=*/NULL);
|
||||
}
|
||||
|
||||
/* return an array of dirents with all other-processes PIDs; also save it in g_pid_status_cache */
|
||||
static int proc_list_ipc_threads(const char* relpath, struct shim_dirent** buf, size_t size) {
|
||||
__UNUSED(relpath);
|
||||
int ret;
|
||||
|
||||
if (!create_lock_runtime(&g_pid_status_lock))
|
||||
return -ENOMEM;
|
||||
|
||||
/* free previously cached list of PIDs; we always query all PIDs anew in this function */
|
||||
lock(&g_pid_status_lock);
|
||||
free(g_pid_status_cache.status);
|
||||
g_pid_status_cache.status = NULL;
|
||||
g_pid_status_cache.status_num = 0;
|
||||
unlock(&g_pid_status_lock);
|
||||
|
||||
struct pid_status* status = NULL;
|
||||
ret = get_all_pid_status(&status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!create_lock_runtime(&status_lock)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
lock(&status_lock);
|
||||
size_t status_num = ret;
|
||||
size_t bytes = 0;
|
||||
struct shim_dirent* dirent = *buf;
|
||||
|
||||
if (pid_status_cache)
|
||||
for (size_t i = 0; i < pid_status_cache->nstatus; i++)
|
||||
if (pid_status_cache->status[i].pid == pid) {
|
||||
unlock(&status_lock);
|
||||
*mode = PERM_r_x______;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unlock(&status_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_dir_stat(const char* name, struct stat* buf) {
|
||||
const char* next;
|
||||
size_t next_len;
|
||||
IDTYPE pid;
|
||||
int ret = parse_ipc_thread_name(name, &pid, &next, &next_len, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!create_lock_runtime(&status_lock)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
lock(&status_lock);
|
||||
|
||||
if (pid_status_cache)
|
||||
for (size_t i = 0; i < pid_status_cache->nstatus; i++)
|
||||
if (pid_status_cache->status[i].pid == pid) {
|
||||
memset(buf, 0, sizeof(struct stat));
|
||||
buf->st_dev = buf->st_ino = 1;
|
||||
buf->st_mode = PERM_r_x______ | S_IFDIR;
|
||||
buf->st_uid = 0; /* XXX */
|
||||
buf->st_gid = 0; /* XXX */
|
||||
buf->st_size = 4096;
|
||||
unlock(&status_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unlock(&status_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int proc_list_ipc_thread(const char* name, struct shim_dirent** buf, int len) {
|
||||
// Only one valid name
|
||||
__UNUSED(name);
|
||||
struct pid_status_cache* status = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!create_lock_runtime(&status_lock)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lock(&status_lock);
|
||||
if (pid_status_cache && !pid_status_cache->dirty) {
|
||||
status = pid_status_cache;
|
||||
status->ref_count++;
|
||||
}
|
||||
unlock(&status_lock);
|
||||
|
||||
if (!status) {
|
||||
status = malloc(sizeof(struct pid_status_cache));
|
||||
if (!status)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = get_all_pid_status(&status->status);
|
||||
if (ret < 0) {
|
||||
free(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
status->nstatus = ret;
|
||||
status->ref_count = 1;
|
||||
status->dirty = false;
|
||||
|
||||
lock(&status_lock);
|
||||
if (pid_status_cache) {
|
||||
if (pid_status_cache->dirty) {
|
||||
if (!pid_status_cache->ref_count)
|
||||
free(pid_status_cache);
|
||||
pid_status_cache = status;
|
||||
} else {
|
||||
if (status->nstatus)
|
||||
free(status->status);
|
||||
free(status);
|
||||
status = pid_status_cache;
|
||||
status->ref_count++;
|
||||
}
|
||||
} else {
|
||||
pid_status_cache = status;
|
||||
}
|
||||
unlock(&status_lock);
|
||||
}
|
||||
|
||||
if (!status->nstatus)
|
||||
goto success;
|
||||
|
||||
struct shim_dirent* ptr = (*buf);
|
||||
void* buf_end = (void*)ptr + len;
|
||||
|
||||
for (size_t i = 0; i < status->nstatus; i++) {
|
||||
if (status->status[i].pid != status->status[i].tgid)
|
||||
for (size_t i = 0; i < status_num; i++) {
|
||||
if (status[i].pid != status[i].tgid)
|
||||
continue;
|
||||
|
||||
IDTYPE pid = status->status[i].pid;
|
||||
int p = pid, l = 0;
|
||||
for (; p; p /= 10, l++)
|
||||
;
|
||||
char pid_str[16];
|
||||
ssize_t pid_str_size = snprintf(pid_str, sizeof(pid_str), "%u", status[i].pid) + 1;
|
||||
|
||||
if ((void*)(ptr + 1) + l + 1 > buf_end) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
size_t total_dirent_size = sizeof(struct shim_dirent) + pid_str_size;
|
||||
bytes += total_dirent_size;
|
||||
if (bytes > size) {
|
||||
free(status);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ptr->next = (void*)(ptr + 1) + l + 1;
|
||||
ptr->ino = 1;
|
||||
ptr->type = LINUX_DT_DIR;
|
||||
ptr->name[l--] = 0;
|
||||
for (p = pid; p; p /= 10) {
|
||||
ptr->name[l--] = p % 10 + '0';
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
dirent->next = (struct shim_dirent*)((char*)(dirent) + total_dirent_size);
|
||||
dirent->ino = 1;
|
||||
dirent->type = LINUX_DT_DIR;
|
||||
memcpy(dirent->name, pid_str, pid_str_size);
|
||||
dirent = dirent->next;
|
||||
}
|
||||
|
||||
*buf = ptr;
|
||||
success:
|
||||
lock(&status_lock);
|
||||
status->dirty = true;
|
||||
status->ref_count--;
|
||||
if (!status->ref_count && status != pid_status_cache)
|
||||
free(status);
|
||||
unlock(&status_lock);
|
||||
lock(&g_pid_status_lock);
|
||||
g_pid_status_cache.status = status;
|
||||
g_pid_status_cache.status_num = status_num;
|
||||
unlock(&g_pid_status_lock);
|
||||
|
||||
*buf = dirent; /* upon return, buf must point past all added entries */
|
||||
return 0;
|
||||
err:
|
||||
lock(&status_lock);
|
||||
status->ref_count--;
|
||||
if (!status->ref_count && status != pid_status_cache)
|
||||
free(status);
|
||||
unlock(&status_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct pseudo_name_ops nm_ipc_thread = {
|
||||
.match_name = &proc_match_ipc_thread,
|
||||
.list_name = &proc_list_ipc_thread,
|
||||
const struct pseudo_name_ops proc_ipc_thread_name_ops = {
|
||||
.match_path = &proc_match_ipc_thread,
|
||||
.list_dirents = &proc_list_ipc_threads,
|
||||
};
|
||||
|
||||
const struct pseudo_fs_ops fs_ipc_thread = {
|
||||
static int proc_ipc_thread_dir_open(struct shim_handle* hdl, const char* relpath, int flags) {
|
||||
__UNUSED(hdl);
|
||||
int ret;
|
||||
|
||||
IDTYPE pid = 0;
|
||||
ret = get_pid_from_relpath(relpath, &pid, /*rest=*/NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (flags & (O_WRONLY | O_RDWR))
|
||||
return -EISDIR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_dir_mode(const char* relpath, mode_t* mode) {
|
||||
int ret;
|
||||
|
||||
IDTYPE pid = 0;
|
||||
ret = get_pid_from_relpath(relpath, &pid, /*rest=*/NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*mode = PERM_r_x______;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_ipc_thread_dir_stat(const char* relpath, struct stat* buf) {
|
||||
int ret;
|
||||
|
||||
IDTYPE pid = 0;
|
||||
ret = get_pid_from_relpath(relpath, &pid, /*rest=*/NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memset(buf, 0, sizeof(struct stat));
|
||||
buf->st_dev = 1;
|
||||
buf->st_ino = 1;
|
||||
buf->st_mode = PERM_r_x______ | S_IFDIR;
|
||||
buf->st_uid = 0;
|
||||
buf->st_gid = 0;
|
||||
buf->st_size = 4096;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct pseudo_fs_ops proc_ipc_thread_fs_ops = {
|
||||
.open = &proc_ipc_thread_dir_open,
|
||||
.mode = &proc_ipc_thread_dir_mode,
|
||||
.stat = &proc_ipc_thread_dir_stat,
|
||||
};
|
||||
|
||||
const struct pseudo_dir dir_ipc_thread = {
|
||||
const struct pseudo_dir proc_ipc_thread_dir = {
|
||||
.size = 3,
|
||||
.ent = {
|
||||
{.name = "cwd", .fs_ops = &fs_ipc_thread_link, .type = LINUX_DT_LNK},
|
||||
{.name = "exe", .fs_ops = &fs_ipc_thread_link, .type = LINUX_DT_LNK},
|
||||
{.name = "root", .fs_ops = &fs_ipc_thread_link, .type = LINUX_DT_LNK},
|
||||
{.name = "cwd", .fs_ops = &proc_ipc_thread_genericlink_fs_ops, .type = LINUX_DT_LNK},
|
||||
{.name = "exe", .fs_ops = &proc_ipc_thread_genericlink_fs_ops, .type = LINUX_DT_LNK},
|
||||
{.name = "root", .fs_ops = &proc_ipc_thread_genericlink_fs_ops, .type = LINUX_DT_LNK},
|
||||
}
|
||||
};
|
||||
|
||||
+442
-499
File diff suppressed because it is too large
Load Diff
@@ -53,10 +53,16 @@ static int pseudo_findent(const char* path, const struct pseudo_ent* root_ent,
|
||||
break;
|
||||
}
|
||||
|
||||
if (ent->name_ops && ent->name_ops->match_name && ent->name_ops->match_name(token)) {
|
||||
/* directory entry has a calculated at runtime name (via match_name) that matches
|
||||
* current token: found ent */
|
||||
break;
|
||||
if (ent->name_ops && ent->name_ops->match_path) {
|
||||
int ret = ent->name_ops->match_path(path);
|
||||
if (ret == 0) {
|
||||
/* directory entry has a calculated at runtime name (via match_path) that
|
||||
* matches current path prefix (not just token!): found ent */
|
||||
break;
|
||||
} else if (ret != -ENOENT) {
|
||||
/* actual failure in match_path() */
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,10 +110,10 @@ static int populate_dirent(const char* path, const struct pseudo_dir* dir, struc
|
||||
dirent_in_buf->type = ent->dir ? LINUX_DT_DIR : ent->type;
|
||||
|
||||
dirent_in_buf = dirent_in_buf->next;
|
||||
} else if (ent->name_ops && ent->name_ops->list_name) {
|
||||
/* directory entry has a list of entries calculated at runtime (via list_name) */
|
||||
} else if (ent->name_ops && ent->name_ops->list_dirents) {
|
||||
/* directory entry has a list of entries calculated at runtime (via list_dirents) */
|
||||
struct shim_dirent* old_dirent_in_buf = dirent_in_buf;
|
||||
int ret = ent->name_ops->list_name(path, &dirent_in_buf, buf_size - total_size);
|
||||
int ret = ent->name_ops->list_dirents(path, &dirent_in_buf, buf_size - total_size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MSG "Hello from /proc/1/fd/1\n"
|
||||
#define MSG2 "Hello from /dev/stdout\n"
|
||||
|
||||
static void* fn(void* arg) {
|
||||
/* not to consume CPU, each thread simply sleeps */
|
||||
sleep(10000);
|
||||
@@ -89,6 +92,72 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("===== Contents of /proc/1/fd\n");
|
||||
dir = opendir("/proc/1/fd");
|
||||
if (!dir) {
|
||||
perror("opendir /proc/1/fd");
|
||||
return 1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
while ((dirent = readdir(dir))) {
|
||||
printf("/proc/1/fd/%s\n", dirent->d_name);
|
||||
}
|
||||
if (errno) {
|
||||
perror("readdir /proc/1/fd");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = closedir(dir);
|
||||
if (ret < 0) {
|
||||
perror("closedir /proc/1/fd");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("===== Writing to /proc/1/fd/1 (stdout)\n");
|
||||
f = fopen("/proc/1/fd/1", "w");
|
||||
if (!f) {
|
||||
perror("fopen /proc/1/fd/1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = fwrite(MSG, sizeof(MSG), 1, f);
|
||||
if (ferror(f)) {
|
||||
perror("fwrite /proc/1/fd/1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* above fwrite will print "Hello ..." to stdout *without* bufferization */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
ret = fclose(f);
|
||||
if (ret) {
|
||||
perror("fclose /proc/1/fd/1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("===== Writing to /dev/stdout (stdout)\n");
|
||||
f = fopen("/dev/stdout", "w");
|
||||
if (!f) {
|
||||
perror("fopen /dev/stdout");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = fwrite(MSG2, sizeof(MSG2), 1, f);
|
||||
if (ferror(f)) {
|
||||
perror("fwrite /dev/stdout");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* above fwrite will print "Hello ..." to stdout *without* bufferization */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
ret = fclose(f);
|
||||
if (ret) {
|
||||
perror("fclose /dev/stdout");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("===== Contents of /proc/self\n");
|
||||
dir = opendir("/proc/self");
|
||||
if (!dir) {
|
||||
|
||||
@@ -549,6 +549,10 @@ class TC_40_FileSystem(RegressionTestCase):
|
||||
self.assertIn('/proc/1/root', stdout)
|
||||
self.assertIn('/proc/1/fd', stdout)
|
||||
self.assertIn('/proc/1/maps', stdout)
|
||||
self.assertIn('/proc/1/fd/0', stdout)
|
||||
self.assertIn('/proc/1/fd/1', stdout)
|
||||
self.assertIn('/proc/1/fd/2', stdout)
|
||||
self.assertIn('Hello from /proc/1/fd/1', stdout)
|
||||
self.assertIn('/proc/self/..', stdout)
|
||||
self.assertIn('/proc/self/cwd', stdout)
|
||||
self.assertIn('/proc/self/exe', stdout)
|
||||
|
||||
Reference in New Issue
Block a user