/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
/* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
/* Copyright (C) 2014 OSCAR lab, Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/*
* fs.c
*
* This file contains codes for implementation of 'dev' filesystem.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define EMPTY_DEV_OPS \
{ \
.open = NULL, \
.close = NULL, \
.read = NULL, \
.write = NULL, \
.flush = NULL, \
.seek = NULL, \
.truncate = NULL, \
.mode = NULL, \
.stat = NULL, \
.hstat = NULL, \
}
#define DEV_INO_BASE 1025
static int dev_null_read (struct shim_handle * hdl, void * buf,
size_t count)
{
return 0;
}
static int dev_zero_read (struct shim_handle * hdl, void * buf,
size_t count)
{
memset(buf, 0, count);
return count;
}
static int dev_null_write (struct shim_handle * hdl, const void * buf,
size_t count)
{
return count;
}
static int dev_null_mode (const char * name, mode_t * mode)
{
*mode = 0666|S_IFCHR;
return 0;
}
static int dev_null_stat (const char * name, struct stat * stat)
{
stat->st_mode = 0666|S_IFCHR;
stat->st_uid = 0;
stat->st_gid = 0;
stat->st_size = 0;
stat->st_blksize = 0;
return 0;
}
static int dev_null_hstat (struct shim_handle * hdl, struct stat * stat)
{
stat->st_mode = 0666|S_IFCHR;
stat->st_uid = 0;
stat->st_gid = 0;
stat->st_size = 0;
stat->st_blksize = 0;
return 0;
}
static int dev_null_truncate (struct shim_handle * hdl, int size)
{
return 0;
}
static int dev_random_mode (const char * name, mode_t * mode)
{
*mode = 0444|S_IFCHR;
return 0;
}
static int dev_random_read (struct shim_handle * hdl, void * buf,
size_t count)
{
int rv;
rv = DkRandomBitsRead(buf, count);
return rv;
}
static int dev_urandom_read (struct shim_handle * hdl, void * buf,
size_t count)
{
int rv;
rv = getrand(buf, count);
return rv;
}
static int dev_random_stat (const char * name, struct stat * stat)
{
stat->st_mode = 0444|S_IFCHR;
stat->st_uid = 0;
stat->st_gid = 0;
stat->st_size = 0;
stat->st_blksize = 0;
return 0;
}
static int dev_random_hstat (struct shim_handle * hdl, struct stat * stat)
{
stat->st_mode = 0444|S_IFCHR;
stat->st_uid = 0;
stat->st_gid = 0;
stat->st_size = 0;
stat->st_blksize = 0;
return 0;
}
static int search_dev_driver (const char * name, struct shim_dev_ops * ops)
{
if (!memcmp(name, "null", 5) || !memcmp(name, "tty", 4)) {
if (ops)
ops->read = &dev_null_read;
null_dev:
if (ops) {
ops->write = &dev_null_write;
ops->truncate = &dev_null_truncate;
ops->mode = &dev_null_mode;
ops->stat = &dev_null_stat;
ops->hstat = &dev_null_hstat;
}
return 0;
}
if (!memcmp(name, "zero", 5)) {
if (ops)
ops->read = &dev_zero_read;
goto null_dev;
}
if (!memcmp(name, "random", 7)) {
if (ops)
ops->read = &dev_random_read;
random_dev:
if (ops) {
ops->mode = &dev_random_mode;
ops->stat = &dev_random_stat;
ops->hstat = &dev_random_hstat;
}
return 0;
}
if (!memcmp(name, "urandom", 8)) {
if (ops)
ops->read = &dev_urandom_read;
goto random_dev;
}
if (!memcmp(name, "stdin", 6) || !memcmp(name, "stdout", 7) ||
!memcmp(name, "stderr", 7))
return -EISLINK;
return -ENOENT;
}
static int dev_mount (const char * uri, const char * root, void ** mount_data)
{
/* do nothing */
return 0;
}
static int dev_unmount (void * mount_data)
{
/* do nothing */
return 0;
}
static int dev_open (struct shim_handle * hdl, struct shim_dentry * dent,
int flags)
{
struct shim_dev_ops ops_buf = EMPTY_DEV_OPS;
int ret = search_dev_driver(qstrgetstr(&dent->rel_path), &ops_buf);
if (ret < 0)
return ret;
hdl->type = TYPE_DEV;
hdl->flags = flags & ~O_ACCMODE;
hdl->acc_mode = ACC_MODE(flags & O_ACCMODE);
memcpy(&hdl->info.dev.dev_ops, &ops_buf,
sizeof(struct shim_dev_ops));
if (!ops_buf.read && (hdl->acc_mode & MAY_READ))
return -EACCES;
if (!ops_buf.write && (hdl->acc_mode & MAY_WRITE))
return -EACCES;
if (ops_buf.open)
return ops_buf.open(hdl, qstrgetstr(&dent->rel_path), flags);
return 0;
}
static int dev_lookup (struct shim_dentry * dent, bool force)
{
if (qstrempty(&dent->rel_path)) {
dent->ino = DEV_INO_BASE;
return 0;
}
/* we don't care about forced or not */
return search_dev_driver(qstrgetstr(&dent->rel_path), NULL);
}
static int dev_mode (struct shim_dentry * dent, mode_t * mode, bool force)
{
if (qstrempty(&dent->rel_path)) {
dent->ino = DEV_INO_BASE;
*mode = 0555|S_IFDIR;
return 0;
}
/* we don't care about forced or not */
struct shim_dev_ops ops_buf = EMPTY_DEV_OPS;
int ret = search_dev_driver(qstrgetstr(&dent->rel_path), &ops_buf);
if (ret < 0)
return ret;
return ops_buf.mode(qstrgetstr(&dent->rel_path), mode);
}
static int dev_flush (struct shim_handle * hdl)
{
if (!hdl->info.dev.dev_ops.flush)
return 0;
return hdl->info.dev.dev_ops.flush(hdl);
}
static int dev_close (struct shim_handle * hdl)
{
if (!hdl->info.dev.dev_ops.close)
return 0;
return hdl->info.dev.dev_ops.close(hdl);
}
static int dev_read (struct shim_handle * hdl, void * buf,
size_t count)
{
if (!hdl->info.dev.dev_ops.read)
return -EACCES;
return hdl->info.dev.dev_ops.read(hdl, buf, count);
}
static int dev_write (struct shim_handle * hdl, const void * buf,
size_t count)
{
if (!hdl->info.dev.dev_ops.write)
return -EACCES;
return hdl->info.dev.dev_ops.write(hdl, buf, count);
}
static int dev_seek (struct shim_handle * hdl, off_t offset, int wence)
{
if (!hdl->info.dev.dev_ops.seek)
return -EACCES;
return hdl->info.dev.dev_ops.seek(hdl, offset, wence);
}
static int dev_truncate (struct shim_handle * hdl, int len)
{
if (!hdl->info.dev.dev_ops.truncate)
return -EACCES;
return hdl->info.dev.dev_ops.truncate(hdl, len);
}
static int dev_readdir (struct shim_dentry * dent, struct shim_dirent ** dirent)
{
if (!qstrempty(&dent->rel_path)) {
struct shim_dev_ops ops_buf = EMPTY_DEV_OPS;
int ret = search_dev_driver(qstrgetstr(&dent->rel_path), &ops_buf);
if (ret < 0 && ret != -EISLINK)
return ret;
return -ENOTDIR;
}
struct shim_dirent * buf, * ptr;
int buf_size = MAX_PATH;
retry:
buf = malloc(buf_size);
*dirent = ptr = buf;
struct shim_dirent ** last = dirent;
#define copy_entry(devname, devtype) \
do { \
int name_len = strlen(devname); \
\
if ((void *) (ptr + 1) + name_len + 1 > \
(void *) buf + buf_size) \
goto nomem; \
\
ptr->next = (void *) (ptr + 1) + name_len + 1; \
ptr->ino = 1; \
ptr->type = (devtype); \
memcpy(ptr->name, (devname), name_len + 1); \
last = &ptr->next; \
ptr = ptr->next; \
} while (0)
copy_entry("null", LINUX_DT_CHR);
copy_entry("zero", LINUX_DT_CHR);
copy_entry("stdin", LINUX_DT_LNK);
copy_entry("stdout", LINUX_DT_LNK);
copy_entry("stderr", LINUX_DT_LNK);
*last = NULL;
return 0;
nomem:
buf_size *= 2;
free(buf);
goto retry;
}
static int dev_stat (struct shim_dentry * dent, struct stat * buf)
{
if (qstrempty(&dent->rel_path)) {
buf->st_dev = DEV_INO_BASE;
buf->st_ino = DEV_INO_BASE;
buf->st_mode = 0777|S_IFDIR;
buf->st_size = 4096;
buf->st_blksize = 4096;
return 0;
}
struct shim_dev_ops ops_buf = EMPTY_DEV_OPS;
int ret = search_dev_driver(qstrgetstr(&dent->rel_path), &ops_buf);
if (ret < 0 && ret != -EISLINK)
return ret;
if (ret == -EISLINK) {
buf->st_dev = DEV_INO_BASE;
buf->st_ino = DEV_INO_BASE;
buf->st_mode = 0777|S_IFLNK;
buf->st_size = 0;
buf->st_blksize = 0;
return 0;
}
buf->st_dev = DEV_INO_BASE;
buf->st_ino = DEV_INO_BASE;
return ops_buf.stat ? ops_buf.stat(qstrgetstr(&dent->rel_path), buf) :
-EACCES;
}
static int dev_hstat (struct shim_handle * hdl, struct stat * buf)
{
if (!hdl->info.dev.dev_ops.hstat)
return -EACCES;
return hdl->info.dev.dev_ops.hstat(hdl, buf);
}
static int dev_follow_link (struct shim_dentry * dent, struct shim_qstr * link)
{
const char * name = qstrgetstr(&dent->rel_path);
if (!memcmp(name, "stdin", 6))
qstrsetstr(link, "/proc/self/0", 13);
else if (!memcmp(name, "stdout", 7))
qstrsetstr(link, "/proc/self/1", 13);
else if (!memcmp(name, "stderr", 7))
qstrsetstr(link, "/proc/self/2", 13);
else if (!memcmp(name, "null", 5) || !memcmp(name, "zero", 5))
return -ENOTLINK;
return -ENOENT;
}
struct shim_fs_ops dev_fs_ops = {
.mount = &dev_mount,
.unmount = &dev_unmount,
.flush = &dev_flush,
.close = &dev_close,
.read = &dev_read,
.write = &dev_write,
.seek = &dev_seek,
.hstat = &dev_hstat,
.truncate = &dev_truncate,
};
struct shim_d_ops dev_d_ops = {
.open = &dev_open,
.lookup = &dev_lookup,
.mode = &dev_mode,
.readdir = &dev_readdir,
.stat = &dev_stat,
.follow_link = &dev_follow_link,
};