From e293854fa29d942320229898da9fbb6cb03f1916 Mon Sep 17 00:00:00 2001
From: Pekka Enberg <penberg@cloudius-systems.com>
Date: Fri, 10 Jan 2014 14:45:35 +0200
Subject: [PATCH] proc filesystem

This patch adds a simple Linux compatible procfs filesystem.  It
currently implements a "/proc/self/maps" file which is looked up by
OpenJDK during startup.

Signed-off-by: Pekka Enberg <penberg@cloudius-systems.com>
---
 fs/build.mk               |   2 +
 fs/procfs/procfs_vnops.cc | 271 ++++++++++++++++++++++++++++++++++++++
 fs/vfs/vfs_conf.c         |   3 +
 3 files changed, 276 insertions(+)
 create mode 100644 fs/procfs/procfs_vnops.cc

diff --git a/fs/build.mk b/fs/build.mk
index 74e6181da..7208aacb9 100644
--- a/fs/build.mk
+++ b/fs/build.mk
@@ -23,3 +23,5 @@ fs +=	ramfs/ramfs_vfsops.o \
 
 fs +=	devfs/devfs_vnops.o \
 	devfs/device.o
+
+fs +=	procfs/procfs_vnops.o
diff --git a/fs/procfs/procfs_vnops.cc b/fs/procfs/procfs_vnops.cc
new file mode 100644
index 000000000..13a504ae8
--- /dev/null
+++ b/fs/procfs/procfs_vnops.cc
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <osv/dentry.h>
+#include <osv/vnode.h>
+#include <osv/mount.h>
+#include <osv/prex.h>
+#include "sched.hh"
+#include "mmu.hh"
+
+#include <functional>
+#include <memory>
+#include <map>
+
+namespace procfs {
+
+using namespace std;
+
+class proc_node {
+public:
+    proc_node(uint64_t ino) : _ino(ino) { }
+    virtual ~proc_node() { }
+
+    uint64_t ino() const { return _ino; };
+
+    virtual off_t    size() const = 0;
+    virtual int      type() const = 0;
+    virtual mode_t   mode() const = 0;
+private:
+    uint64_t _ino;
+};
+
+class proc_file_node : public proc_node {
+public:
+    proc_file_node(uint64_t ino, function<string ()> gen)
+        : proc_node(ino)
+        , _gen(gen)
+    { }
+
+    virtual off_t size() const override {
+        return 0;
+    }
+    virtual int type() const override {
+        return VREG;
+    }
+    virtual mode_t mode() const override {
+        return S_IRUSR|S_IRGRP|S_IROTH;
+    }
+    string* data() const {
+        return new string(_gen());
+    }
+private:
+    function<string ()> _gen;
+};
+
+class proc_dir_node : public proc_node {
+public:
+    proc_dir_node(uint64_t ino) : proc_node(ino) { }
+
+    shared_ptr<proc_node> lookup(string name) {
+        auto it = _children.find(name);
+        if (it == _children.end()) {
+            return nullptr;
+        }
+        return it->second;
+    }
+    void add(string name, uint64_t ino, function<string ()> gen) {
+        _children.insert({name, make_shared<proc_file_node>(ino, gen)});
+    }
+    void add(string name, shared_ptr<proc_node> np) {
+        _children.insert({name, np});
+    }
+    virtual off_t size() const override {
+        return 0;
+    }
+    virtual int type() const override {
+        return VDIR;
+    }
+    virtual mode_t mode() const override {
+        return S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+    }
+private:
+    map<string, shared_ptr<proc_node>> _children;
+};
+
+static uint64_t inode_count = 1; /* inode 0 is reserved to root */
+
+static proc_node* to_node(vnode* vp)
+{
+    return static_cast<proc_node*>(vp->v_data);
+}
+
+static proc_dir_node* to_dir_node(vnode* vp)
+{
+    auto *np = to_node(vp);
+
+    return dynamic_cast<proc_dir_node*>(np);
+}
+
+static proc_file_node* to_file_node(vnode* vp)
+{
+    auto *np = to_node(vp);
+
+    return dynamic_cast<proc_file_node*>(np);
+}
+
+static int
+procfs_open(file* fp)
+{
+    auto* np = to_file_node(fp->f_dentry->d_vnode);
+    if (np) {
+        fp->f_data = np->data();
+    }
+    return 0;
+}
+
+static int
+procfs_close(vnode* vp, file* fp)
+{
+    auto* data = static_cast<string*>(fp->f_data);
+
+    delete data;
+
+    return 0;
+}
+
+static int
+procfs_read(vnode* vp, file *fp, uio* uio, int ioflags)
+{
+    auto* data = static_cast<string*>(fp->f_data);
+
+    if (vp->v_type == VDIR)
+        return EISDIR;
+    if (vp->v_type != VREG)
+        return EINVAL;
+    if (uio->uio_offset < 0)
+        return EINVAL;
+    if (uio->uio_offset >= (off_t)data->size())
+        return 0;
+
+    size_t len;
+
+    if ((off_t)data->size() - uio->uio_offset < uio->uio_resid)
+        len = data->size() - uio->uio_offset;
+    else
+        len = uio->uio_resid;
+
+    return uiomove(const_cast<char*>(data->data()) + uio->uio_offset, len, uio);
+}
+
+static int
+procfs_write(vnode* vp, uio* uio, int ioflags)
+{
+    return EINVAL;
+}
+
+static int
+procfs_ioctl(vnode* vp, file* fp, u_long cmd, void *arg)
+{
+    return EINVAL;
+}
+
+static int
+procfs_lookup(vnode* dvp, char* name, vnode** vpp)
+{
+    auto* parent = to_dir_node(dvp);
+
+    *vpp = nullptr;
+
+    if (!*name || !parent) {
+        return ENOENT;
+    }
+    auto node = parent->lookup(name);
+    if (!node) {
+        return ENOENT;
+    }
+    vnode* vp;
+    if (vget(dvp->v_mount, node->ino(), &vp)) {
+        /* found in cache */
+        *vpp = vp;
+        return 0;
+    }
+    if (!vp) {
+        return ENOMEM;
+    }
+    vp->v_data = node.get();
+    vp->v_type = node->type();
+    vp->v_mode = node->mode();
+    vp->v_size = node->size();
+
+    *vpp = vp;
+
+    return 0;
+}
+
+static int
+procfs_readdir(vnode *vp, file *fp, dirent *dir)
+{
+    return ENOENT;
+}
+
+static int
+procfs_mount(mount* mp, char *dev, int flags, void* data)
+{
+    auto* vp = mp->m_root->d_vnode;
+
+    auto self = make_shared<proc_dir_node>(inode_count++);
+    self->add("maps", inode_count++, mmu::procfs_maps);
+
+    auto* root = new proc_dir_node(vp->v_ino);
+    root->add("self", self);
+
+    vp->v_data = static_cast<void*>(root);
+
+    return 0;
+}
+
+static int
+procfs_unmount(mount* mp, int flags)
+{
+    release_mp_dentries(mp);
+
+    auto* vp = to_node(mp->m_root->d_vnode);
+
+    delete vp;
+
+    return 0;
+}
+
+} // namespace procfs
+
+extern "C"
+int procfs_init(void)
+{
+    return 0;
+}
+
+vnops procfs_vnops = {
+    procfs::procfs_open,          // vop_open
+    procfs::procfs_close,         // vop_close
+    procfs::procfs_read,          // vop_read
+    procfs::procfs_write,         // vop_write
+    (vnop_seek_t)     vop_nullop, // vop_seek
+    procfs::procfs_ioctl,         // vop_ioctl
+    (vnop_fsync_t)    vop_nullop, // vop_fsync
+    procfs::procfs_readdir,       // vop_readdir
+    procfs::procfs_lookup,        // vop_lookup
+    (vnop_create_t)   vop_einval, // vop_create
+    (vnop_remove_t)   vop_einval, // vop_remove
+    (vnop_rename_t)   vop_einval, // vop_remame
+    (vnop_mkdir_t)    vop_einval, // vop_mkdir
+    (vnop_rmdir_t)    vop_einval, // vop_rmdir
+    (vnop_getattr_t)  vop_nullop, // vop_getattr
+    (vnop_setattr_t)  vop_eperm,  // vop_setattr
+    (vnop_inactive_t) vop_nullop, // vop_inactive
+    (vnop_truncate_t) vop_nullop, // vop_truncate
+    (vnop_link_t)     vop_eperm,  // vop_link
+};
+
+vfsops procfs_vfsops = {
+    procfs::procfs_mount,         // vfs_mount
+    procfs::procfs_unmount,       // vfs_unmount
+    (vfsop_sync_t)   vfs_nullop,  // vfs_sync
+    (vfsop_vget_t)   vfs_nullop,  // vfs_vget
+    (vfsop_statfs_t) vfs_nullop,  // vfs_statfs
+    &procfs_vnops,                // vfs_vnops
+};
diff --git a/fs/vfs/vfs_conf.c b/fs/vfs/vfs_conf.c
index 40b34ec7a..1a4960b25 100644
--- a/fs/vfs/vfs_conf.c
+++ b/fs/vfs/vfs_conf.c
@@ -47,10 +47,12 @@
 
 extern struct vfsops ramfs_vfsops;
 extern struct vfsops devfs_vfsops;
+extern struct vfsops procfs_vfsops;
 extern struct vfsops zfs_vfsops;
 
 extern int ramfs_init(void);
 extern int devfs_init(void);
+extern int procfs_init(void);
 extern int zfs_init(void);
 
 /*
@@ -59,6 +61,7 @@ extern int zfs_init(void);
 const struct vfssw vfssw[] = {
 	{"ramfs",	ramfs_init,	&ramfs_vfsops},
 	{"devfs",	devfs_init,	&devfs_vfsops},
+	{"procfs",	procfs_init,	&procfs_vfsops},
 	{"zfs",		NULL,		&zfs_vfsops},
 	{NULL,		fs_noop,	NULL},
 };
-- 
GitLab