/* -*- 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 = 0666|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 = 0666|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_poll (struct shim_handle * hdl, int poll_type) { if (poll_type == FS_POLL_SZ) return 0; int ret = 0; if ((poll_type & FS_POLL_RD) && hdl->info.dev.dev_ops.read) ret |= FS_POLL_RD; if ((poll_type & FS_POLL_WR) && hdl->info.dev.dev_ops.write) ret |= FS_POLL_WR; return ret; } 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, .poll = &dev_poll, .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, };