This one is identical to previous patch except it states v2 to clearly 
indicate it is 2nd version of the original patch I sent in December. It is 
essentially a minimal subset of ROFS implementation without cache layer. It 
also does not include unit tests.

I will be sending a followup patch to allow unit tests run against ROFS 
image with /tmp mounted as ramfs.

Waldek

On Friday, February 2, 2018 at 8:29:13 AM UTC-5, Waldek Kozaczuk wrote:
>
> The Read-Only File System (ROFS) is a simple implementation of 
> a file system where data from disk can only be read from and never 
> written to. It is simple enough for many stateless applications 
> deployed on OSv which only need to read code from local disk and never 
> write to local disk. The ROFS is inspired and shares some ideas 
> from the original MFS implementation by James Root from 2015. 
>
> This initial implementation of ROFS operates without cache. 
> This means that ROFS reads as much data from disk as requested 
> per uio passed in to the read function but it does not retain/cache it 
> for any subsequent read of the same data. 
>
> The image built with ROFS is automatically set up to mount /tmp 
> as ramfs filesystem to allow writing of any transient data like logs. 
>
> The layout of the data on disk is as follows: 
>
> - Super Block (512 bytes) that contains magic number and specifies meta 
>   information including block size and location and size of tables 
> containing 
>   i-nodes, dentries and symbolic links 
>
> - Files data where each file is padded to 512 bytes block 
>
> - Table of directory entries referenced by index in directory i-node 
>   (each entry holds string with direntry name and i-node number) 
>
> - Table of symlinks referenced by symlink i-node (each entry holds 
> symbolic link 
>   path string) 
>
> - Table of inodes where each specifies type (dir,file,symlink) and data 
> offset 
>   (for files it is a block on a disk, for symlinks and directories it is 
> an 
>   offset in one of the 2 tables above) 
>
> Besides ROFS implementation this patch also changes VFS main to 
> automatically 
> mount ROFS or ZFS. It also adds number of new metrics that are captured 
> and output 
> in verbose mode. 
>
> The images with RFS can be built by specified fs=rofs option like so: 
> ./scripts/build image=node-fs-example -j4 fs=rofs 
> ./scripts/build image=openjdk9-java-base,java-example -j4 fs=rofs 
>
> Potential future improvements to ROFS: 
> - add caching layer to read ahead more data from disk in anticipation 
>   of sequantial access 
> - add compression of file segments 
> - memory page sharing when file mmaping (implement vnop_cache) 
> - add LRU logic to ROFS cache 
>
> Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com> 
> --- 
>  Makefile                                           |   4 + 
>  fs/rofs/rofs.hh                                    | 117 +++++++ 
>  fs/rofs/rofs_common.cc                             |  85 +++++ 
>  fs/rofs/rofs_vfsops.cc                             | 216 ++++++++++++ 
>  fs/rofs/rofs_vnops.cc                              | 303 
> +++++++++++++++++ 
>  fs/vfs/main.cc                                     |  67 ++-- 
>  fs/vfs/vfs_conf.cc                                 |   3 + 
>  fs/vfs/vfs_vnode.cc                                |   9 +- 
>  include/osv/vnode.h                                |   1 + 
>  licenses/mfs.txt                                   |  46 +++ 
>  loader.cc                                          |  24 +- 
>  modules/httpserver-api/api/fs.cc                   |   4 +- 
>  scripts/build                                      |  22 +- 
>  scripts/gen-rofs-img.py                            | 373 
> +++++++++++++++++++++ 
>  static/etc/fstab_rofs                              |   4 + 
>  usr_nozfs.manifest.skel => usr_ramfs.manifest.skel |   0 
>  usr_nozfs.manifest.skel => usr_rofs.manifest.skel  |   2 +- 
>  17 files changed, 1245 insertions(+), 35 deletions(-) 
>  create mode 100644 fs/rofs/rofs.hh 
>  create mode 100644 fs/rofs/rofs_common.cc 
>  create mode 100644 fs/rofs/rofs_vfsops.cc 
>  create mode 100644 fs/rofs/rofs_vnops.cc 
>  create mode 100644 licenses/mfs.txt 
>  create mode 100755 scripts/gen-rofs-img.py 
>  create mode 100644 static/etc/fstab_rofs 
>  copy usr_nozfs.manifest.skel => usr_ramfs.manifest.skel (100%) 
>  rename usr_nozfs.manifest.skel => usr_rofs.manifest.skel (88%) 
>
> diff --git a/Makefile b/Makefile 
> index 8dd2371..2a02a19 100644 
> --- a/Makefile 
> +++ b/Makefile 
> @@ -1769,6 +1769,10 @@ fs_objs += ramfs/ramfs_vfsops.o \ 
>  fs_objs += devfs/devfs_vnops.o \ 
>          devfs/device.o 
>   
> +fs_objs += rofs/rofs_vfsops.o \ 
> +        rofs/rofs_vnops.o \ 
> +        rofs/rofs_common.o 
> + 
>  fs_objs += procfs/procfs_vnops.o 
>   
>  objects += $(addprefix fs/, $(fs_objs)) 
> diff --git a/fs/rofs/rofs.hh b/fs/rofs/rofs.hh 
> new file mode 100644 
> index 0000000..9fcd9a9 
> --- /dev/null 
> +++ b/fs/rofs/rofs.hh 
> @@ -0,0 +1,117 @@ 
> +/* 
> + * Copyright (c) 2015 Carnegie Mellon University. 
> + * All Rights Reserved. 
> + * 
> + * THIS SOFTWARE IS PROVIDED "AS IS," WITH NO WARRANTIES WHATSOEVER. 
> CARNEGIE 
> + * MELLON UNIVERSITY EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT 
> PERMITTEDBY LAW 
> + * ALL EXPRESS, IMPLIED, AND STATUTORY WARRANTIES, INCLUDING, WITHOUT 
> + * LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
> PARTICULAR 
> + * PURPOSE, AND NON-INFRINGEMENT OF PROPRIETARY RIGHTS. 
> + * 
> + * Released under a modified BSD license. For full terms, please see 
> mfs.txt in 
> + * the licenses folder or contact permi...@sei.cmu.edu. 
> + * 
> + * DM-0002621 
> + * 
> + * Based on https://github.com/jdroot/mfs 
> + * 
> + * Copyright (C) 2017 Waldemar Kozaczuk 
> + * Inspired by original MFS implementation by James Root from 2015 
> + * 
> + * 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. 
> + */ 
> + 
> +// 
> +// The Read-Only File System (ROFS) provides simple implementation of 
> +// a file system where data from disk can only be read from and never 
> +// written to. It is simple enough for many stateless applications 
> +// deployed on OSv which only need to read code from local disk and never 
> +// write to local disk. The ROFS is inspired and shares some ideas 
> +// from the original MFS implementation by James Root from 2015. 
> +// 
> +// This initial version of ROFS operates without cache. It reads as much 
> data 
> +// from disk as requested per uio passed in to the read function and it 
> does 
> +// not retain/cache it for any subsequent read of the same data. 
> +// 
> +// The structure of the data on disk is explained in 
> scripts/gen-rofs-img.py 
> + 
> +#ifndef __INCLUDE_ROFS_H__ 
> +#define __INCLUDE_ROFS_H__ 
> + 
> +#include <osv/vnode.h> 
> +#include <osv/mount.h> 
> +#include <osv/dentry.h> 
> +#include <osv/prex.h> 
> +#include <osv/buf.h> 
> + 
> +#define ROFS_VERSION            1 
> +#define ROFS_MAGIC              0xDEADBEAD 
> + 
> +#define ROFS_INODE_SIZE ((uint64_t)sizeof(struct rofs_inode)) 
> + 
> +#define ROFS_SUPERBLOCK_SIZE sizeof(struct rofs_super_block) 
> +#define ROFS_SUPERBLOCK_BLOCK 0 
> + 
> +//#define ROFS_DEBUG_ENABLED 1 
> + 
> +#if defined(ROFS_DEBUG_ENABLED) 
> +#define print(...) kprintf(__VA_ARGS__) 
> +#else 
> +#define print(...) 
> +#endif 
> + 
> +#define ROFS_DIAGNOSTICS_ENABLED 1 
> + 
> +#if defined(ROFS_DIAGNOSTICS_ENABLED) 
> +#define ROFS_STOPWATCH_START auto begin = 
> std::chrono::high_resolution_clock::now(); 
> +#define ROFS_STOPWATCH_END(total) auto end = 
> std::chrono::high_resolution_clock::now(); \ 
> +std::chrono::duration<double> sec = end - begin; \ 
> +total += ((long)(sec.count() * 1000000)); 
> +//TODO: Review - avoid conversions 
> +#else 
> +#define ROFS_STOPWATCH_START 
> +#define ROFS_STOPWATCH_END(...) 
> +#endif 
> + 
> +extern struct vfsops rofs_vfsops; 
> +extern struct vnops rofs_vnops; 
> + 
> +struct rofs_super_block { 
> +    uint64_t magic; 
> +    uint64_t version; 
> +    uint64_t block_size; 
> +    uint64_t structure_info_first_block; 
> +    uint64_t structure_info_blocks_count; 
> +    uint64_t directory_entries_count; 
> +    uint64_t symlinks_count; 
> +    uint64_t inodes_count; 
> +}; 
> + 
> +struct rofs_inode { 
> +    mode_t mode; 
> +    uint64_t inode_no; 
> +    uint64_t data_offset; 
> +    union { 
> +        uint64_t file_size; 
> +        uint64_t dir_children_count; 
> +    }; 
> +}; 
> + 
> +struct rofs_dir_entry { 
> +    char *filename; 
> +    uint64_t inode_no; 
> +}; 
> + 
> +struct rofs_info { 
> +    struct rofs_super_block *sb; 
> +    struct rofs_dir_entry *dir_entries; 
> +    char **symlinks; 
> +    struct rofs_inode *inodes; 
> +}; 
> + 
> +int rofs_read_blocks(struct device *device, uint64_t starting_block, 
> uint64_t blocks_count, void *buf); 
> + 
> +void rofs_set_vnode(struct vnode *vnode, struct rofs_inode *inode); 
> + 
> +#endif 
> diff --git a/fs/rofs/rofs_common.cc b/fs/rofs/rofs_common.cc 
> new file mode 100644 
> index 0000000..8b04f5c 
> --- /dev/null 
> +++ b/fs/rofs/rofs_common.cc 
> @@ -0,0 +1,85 @@ 
> +/* 
> + * Copyright (c) 2015 Carnegie Mellon University. 
> + * All Rights Reserved. 
> + * 
> + * THIS SOFTWARE IS PROVIDED "AS IS," WITH NO WARRANTIES WHATSOEVER. 
> CARNEGIE 
> + * MELLON UNIVERSITY EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT 
> PERMITTEDBY LAW 
> + * ALL EXPRESS, IMPLIED, AND STATUTORY WARRANTIES, INCLUDING, WITHOUT 
> + * LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
> PARTICULAR 
> + * PURPOSE, AND NON-INFRINGEMENT OF PROPRIETARY RIGHTS. 
> + * 
> + * Released under a modified BSD license. For full terms, please see 
> mfs.txt in 
> + * the licenses folder or contact permi...@sei.cmu.edu. 
> + * 
> + * DM-0002621 
> + * 
> + * Based on https://github.com/jdroot/mfs 
> + * 
> + * Copyright (C) 2017 Waldemar Kozaczuk 
> + * Inspired by original MFS implementation by James Root from 2015 
> + * 
> + * 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 "rofs.hh" 
> +#include <osv/device.h> 
> +#include <osv/bio.h> 
> + 
> +#if defined(ROFS_DIAGNOSTICS_ENABLED) 
> +extern std::atomic<long> rofs_block_read_count; 
> +extern std::atomic<long> rofs_block_read_ms; 
> +#endif 
> + 
> +void rofs_set_vnode(struct vnode *vnode, struct rofs_inode *inode) 
> +{ 
> +    off_t size = 0; 
> +    if (vnode == nullptr || inode == nullptr) { 
> +        return; 
> +    } 
> + 
> +    vnode->v_data = inode; 
> +    vnode->v_ino = inode->inode_no; 
> + 
> +    // Set type 
> +    if (S_ISDIR(inode->mode)) { 
> +        size = ROFS_INODE_SIZE; //TODO: Revisit 
> +        vnode->v_type = VDIR; 
> +    } else if (S_ISREG(inode->mode)) { 
> +        size = inode->file_size; 
> +        vnode->v_type = VREG; 
> +    } else if (S_ISLNK(inode->mode)) { 
> +        size = 512; // TODO: Revisit 
> +        vnode->v_type = VLNK; 
> +    } 
> + 
> +    vnode->v_mode = 0555; 
> +    vnode->v_size = size; 
> +} 
> + 
> +int 
> +rofs_read_blocks(struct device *device, uint64_t starting_block, uint64_t 
> blocks_count, void *buf) 
> +{ 
> +    ROFS_STOPWATCH_START 
> +    struct bio *bio = alloc_bio(); 
> +    if (!bio) 
> +        return ENOMEM; 
> + 
> +    bio->bio_cmd = BIO_READ; 
> +    bio->bio_dev = device; 
> +    bio->bio_data = buf; 
> +    bio->bio_offset = starting_block << 9; 
> +    bio->bio_bcount = blocks_count * BSIZE; 
> + 
> +    bio->bio_dev->driver->devops->strategy(bio); 
> +    int error = bio_wait(bio); 
> + 
> +    destroy_bio(bio); 
> + 
> +#if defined(ROFS_DIAGNOSTICS_ENABLED) 
> +    rofs_block_read_count += blocks_count; 
> +#endif 
> +    ROFS_STOPWATCH_END(rofs_block_read_ms) 
> + 
> +    return error; 
> +} 
> diff --git a/fs/rofs/rofs_vfsops.cc b/fs/rofs/rofs_vfsops.cc 
> new file mode 100644 
> index 0000000..f6d7b3b 
> --- /dev/null 
> +++ b/fs/rofs/rofs_vfsops.cc 
> @@ -0,0 +1,216 @@ 
> +/* 
> + * Copyright (c) 2015 Carnegie Mellon University. 
> + * All Rights Reserved. 
> + * 
> + * THIS SOFTWARE IS PROVIDED "AS IS," WITH NO WARRANTIES WHATSOEVER. 
> CARNEGIE 
> + * MELLON UNIVERSITY EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT 
> PERMITTEDBY LAW 
> + * ALL EXPRESS, IMPLIED, AND STATUTORY WARRANTIES, INCLUDING, WITHOUT 
> + * LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
> PARTICULAR 
> + * PURPOSE, AND NON-INFRINGEMENT OF PROPRIETARY RIGHTS. 
> + * 
> + * Released under a modified BSD license. For full terms, please see 
> mfs.txt in 
> + * the licenses folder or contact permi...@sei.cmu.edu. 
> + * 
> + * DM-0002621 
> + * 
> + * Based on https://github.com/jdroot/mfs 
> + * 
> + * Copyright (C) 2017 Waldemar Kozaczuk 
> + * Inspired by original MFS implementation by James Root from 2015 
> + * 
> + * 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 "rofs.hh" 
> +#include <sys/types.h> 
> +#include <osv/device.h> 
> +#include <osv/debug.h> 
> +#include <iomanip> 
> +#include <iostream> 
> + 
> +static int rofs_mount(struct mount *mp, const char *dev, int flags, const 
> void *data); 
> +static int rofs_sync(struct mount *mp); 
> +static int rofs_statfs(struct mount *mp, struct statfs *statp); 
> +static int rofs_unmount(struct mount *mp, int flags); 
> + 
> +#define rofs_vget ((vfsop_vget_t)vfs_nullop) 
> + 
> +#if defined(ROFS_DIAGNOSTICS_ENABLED) 
> +std::atomic<long> rofs_block_read_ms(0); 
> +std::atomic<long> rofs_block_read_count(0); 
> +#endif 
> + 
> +struct vfsops rofs_vfsops = { 
> +    rofs_mount,                /* mount */ 
> +    rofs_unmount,        /* unmount */ 
> +    rofs_sync,                /* sync */ 
> +    rofs_vget,      /* vget */ 
> +    rofs_statfs,        /* statfs */ 
> +    &rofs_vnops            /* vnops */ 
> +}; 
> + 
> +static int 
> +rofs_mount(struct mount *mp, const char *dev, int flags, const void 
> *data) 
> +{ 
> +    struct device *device; 
> +    struct rofs_info *rofs = nullptr; 
> +    struct rofs_super_block *sb = nullptr; 
> +    int error = -1; 
> + 
> +    error = device_open(dev + 5, DO_RDWR, &device); 
> +    if (error) { 
> +        kprintf("[rofs] Error opening device!\n"); 
> +        return error; 
> +    } 
> + 
> +    void *buf = malloc(BSIZE); //Just enough for single block of 512 
> bytes 
> +    error = rofs_read_blocks(device, ROFS_SUPERBLOCK_BLOCK, 1, buf); 
> +    if (error) { 
> +        kprintf("[rofs] Error reading rofs superblock\n"); 
> +        device_close(device); 
> +        free(buf); 
> +        return error; 
> +    } 
> + 
> +    // We see if the file system is ROFS, if not, return error and close 
> everything 
> +    sb = (struct rofs_super_block *) buf; 
> +    if (sb->magic != ROFS_MAGIC) { 
> +        print("[rofs] Error magics do not match!\n"); 
> +        print("[rofs] Expecting %016llX but got %016llX\n", ROFS_MAGIC, 
> sb->magic); 
> +        free(buf); 
> +        device_close(device); 
> +        return -1; // TODO: Proper error code 
> +    } 
> + 
> +    if (sb->version != ROFS_VERSION) { 
> +        kprintf("[rofs] Found rofs volume but incompatible version!\n"); 
> +        kprintf("[rofs] Expecting %llu but found %llu\n", ROFS_VERSION, 
> sb->version); 
> +        free(buf); 
> +        device_close(device); 
> +        return -1; 
> +    } 
> + 
> +    print("[rofs] Got superblock version:   0x%016llX\n", sb->version); 
> +    print("[rofs] Got magic:                0x%016llX\n", sb->magic); 
> +    print("[rofs] Got block size:                  %d\n", 
> sb->block_size); 
> +    print("[rofs] Got structure info first block:  %d\n", 
> sb->structure_info_first_block); 
> +    print("[rofs] Got structure info blocks count: %d\n", 
> sb->structure_info_blocks_count); 
> +    print("[rofs] Got directory entries count:     %d\n", 
> sb->directory_entries_count); 
> +    print("[rofs] Got symlinks count:              %d\n", 
> sb->symlinks_count); 
> +    print("[rofs] Got inode count:                 %d\n", 
> sb->inodes_count); 
> +    // 
> +    // Since we have found ROFS, we can copy the superblock now 
> +    sb = new rofs_super_block; 
> +    memcpy(sb, buf, ROFS_SUPERBLOCK_SIZE); 
> +    free(buf); 
> +    // 
> +    // Read structure_info_blocks_count to construct array of directory 
> enries, symlinks and i-nodes 
> +    buf = malloc(BSIZE * sb->structure_info_blocks_count); 
> +    error = rofs_read_blocks(device, sb->structure_info_first_block, 
> sb->structure_info_blocks_count, buf); 
> +    if (error) { 
> +        kprintf("[rofs] Error reading rofs structure info blocks\n"); 
> +        device_close(device); 
> +        free(buf); 
> +        return error; 
> +    } 
> + 
> +    rofs = new struct rofs_info; 
> +    rofs->sb = sb; 
> +    rofs->dir_entries = (struct rofs_dir_entry *) malloc(sizeof(struct 
> rofs_dir_entry) * sb->directory_entries_count); 
> + 
> +    void *data_ptr = buf; 
> +    // 
> +    // Read directory entries 
> +    for (unsigned int idx = 0; idx < sb->directory_entries_count; idx++) 
> { 
> +        struct rofs_dir_entry *dir_entry = &(rofs->dir_entries[idx]); 
> +        dir_entry->inode_no = *((uint64_t *) data_ptr); 
> +        data_ptr += sizeof(uint64_t); 
> + 
> +        unsigned short *filename_size = (unsigned short *) data_ptr; 
> +        data_ptr += sizeof(unsigned short); 
> + 
> +        dir_entry->filename = (char *) malloc(*filename_size + 1); 
> +        strncpy(dir_entry->filename, (char *) data_ptr, *filename_size); 
> +        dir_entry->filename[*filename_size] = 0; 
> +        print("[rofs] i-node: %d -> directory entry: %s\n", 
> dir_entry->inode_no, dir_entry->filename); 
> +        data_ptr += *filename_size * sizeof(char); 
> +    } 
> +    // 
> +    // Read symbolic links 
> +    rofs->symlinks = (char **) malloc(sizeof(char *) * 
> sb->symlinks_count); 
> + 
> +    for (unsigned int idx = 0; idx < sb->symlinks_count; idx++) { 
> +        unsigned short *symlink_path_size = (unsigned short *) data_ptr; 
> +        data_ptr += sizeof(unsigned short); 
> + 
> +        rofs->symlinks[idx] = (char *) malloc(*symlink_path_size + 1); 
> +        strncpy(rofs->symlinks[idx], (char *) data_ptr, 
> *symlink_path_size); 
> +        rofs->symlinks[idx][*symlink_path_size] = 0; 
> +        print("[rofs] symlink: %s\n", rofs->symlinks[idx]); 
> +        data_ptr += *symlink_path_size * sizeof(char); 
> +    } 
> +    // 
> +    // Read i-nodes 
> +    rofs->inodes = (struct rofs_inode *) malloc(sizeof(struct rofs_inode) 
> * sb->inodes_count); 
> +    memcpy(rofs->inodes, data_ptr, sb->inodes_count * sizeof(struct 
> rofs_inode)); 
> + 
> +    for (unsigned int idx = 0; idx < sb->inodes_count; idx++) { 
> +        print("[rofs] inode: %d, size: %d\n", rofs->inodes[idx].inode_no, 
> rofs->inodes[idx].file_size); 
> +    } 
> + 
> +    free(buf); 
> + 
> +    // Save a reference to our superblock 
> +    mp->m_data = rofs; 
> +    mp->m_dev = device; 
> + 
> +    rofs_set_vnode(mp->m_root->d_vnode, rofs->inodes); 
> + 
> +    print("[rofs] returning from mount\n"); 
> + 
> +    return 0; 
> +} 
> + 
> +static int rofs_sync(struct mount *mp) { 
> +    return 0; 
> +} 
> + 
> +static int rofs_statfs(struct mount *mp, struct statfs *statp) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) mp->m_data; 
> +    struct rofs_super_block *sb = rofs->sb; 
> + 
> +    statp->f_bsize = sb->block_size; 
> + 
> +    // Total blocks 
> +    statp->f_blocks = sb->structure_info_blocks_count + 
> sb->structure_info_first_block; 
> +    // Read only. 0 blocks free 
> +    statp->f_bfree = 0; 
> +    statp->f_bavail = 0; 
> + 
> +    statp->f_ffree = 0; 
> +    statp->f_files = sb->inodes_count; //Needs to be inode count 
> + 
> +    statp->f_namelen = 0; //FIXME - unlimited ROFS_FILENAME_MAXLEN; 
> + 
> +    return 0; 
> +} 
> + 
> +static int 
> +rofs_unmount(struct mount *mp, int flags) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) mp->m_data; 
> +    struct rofs_super_block *sb = rofs->sb; 
> +    struct device *dev = mp->m_dev; 
> + 
> +    int error = device_close(dev); 
> +    delete sb; 
> +    delete rofs; 
> + 
> +#if defined(ROFS_DIAGNOSTICS_ENABLED) 
> +    debugf("ROFS: spent %.2f ms reading from disk\n", ((double) 
> rofs_block_read_ms.load()) / 1000); 
> +    debugf("ROFS: read %d 512-byte blocks from disk\n", 
> rofs_block_read_count.load()); 
> +#endif 
> +    return error; 
> +} 
> diff --git a/fs/rofs/rofs_vnops.cc b/fs/rofs/rofs_vnops.cc 
> new file mode 100644 
> index 0000000..5d2cff0 
> --- /dev/null 
> +++ b/fs/rofs/rofs_vnops.cc 
> @@ -0,0 +1,303 @@ 
> +/* 
> + * Copyright (c) 2015 Carnegie Mellon University. 
> + * All Rights Reserved. 
> + * 
> + * THIS SOFTWARE IS PROVIDED "AS IS," WITH NO WARRANTIES WHATSOEVER. 
> CARNEGIE 
> + * MELLON UNIVERSITY EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT 
> PERMITTEDBY LAW 
> + * ALL EXPRESS, IMPLIED, AND STATUTORY WARRANTIES, INCLUDING, WITHOUT 
> + * LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
> PARTICULAR 
> + * PURPOSE, AND NON-INFRINGEMENT OF PROPRIETARY RIGHTS. 
> + * 
> + * Released under a modified BSD license. For full terms, please see 
> mfs.txt in 
> + * the licenses folder or contact permi...@sei.cmu.edu. 
> + * 
> + * DM-0002621 
> + * 
> + * Based on https://github.com/jdroot/mfs 
> + * 
> + * Copyright (C) 2017 Waldemar Kozaczuk 
> + * Inspired by original MFS implementation by James Root from 2015 
> + * 
> + * 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 <sys/stat.h> 
> +#include <dirent.h> 
> +#include <sys/param.h> 
> + 
> +#include <errno.h> 
> +#include <string.h> 
> +#include <stdlib.h> 
> +#include <fcntl.h> 
> + 
> +#include <osv/prex.h> 
> +#include <osv/vnode.h> 
> +#include <osv/file.h> 
> +#include <osv/mount.h> 
> +#include <osv/debug.h> 
> + 
> +#include <sys/types.h> 
> +#include <osv/device.h> 
> +#include <osv/sched.hh> 
> + 
> +#include "rofs.hh" 
> + 
> +#define VERIFY_READ_INPUT_ARGUMENTS() \ 
> +    /* Cant read directories */\ 
> +    if (vnode->v_type == VDIR) \ 
> +        return EISDIR; \ 
> +    /* Cant read anything but reg */\ 
> +    if (vnode->v_type != VREG) \ 
> +        return EINVAL; \ 
> +    /* Cant start reading before the first byte */\ 
> +    if (uio->uio_offset < 0) \ 
> +        return EINVAL; \ 
> +    /* Need to read more than 1 byte */\ 
> +    if (uio->uio_resid == 0) \ 
> +        return 0; \ 
> +    /* Cant read after the end of the file */\ 
> +    if (uio->uio_offset >= (off_t)vnode->v_size) \ 
> +        return 0; 
> + 
> +int rofs_init(void) { 
> +    return 0; 
> +} 
> + 
> +static int rofs_open(struct file *fp) 
> +{ 
> +    if ((file_flags(fp) & FWRITE)) { 
> +        // Do no allow opening files to write 
> +        return (EROFS); 
> +    } 
> +    print("[rofs] rofs_open called for inode [%d] \n", 
> +          ((struct rofs_inode *) 
> fp->f_dentry.get()->d_vnode->v_data)->inode_no); 
> +    return 0; 
> +} 
> + 
> +static int rofs_close(struct vnode *vp, struct file *fp) { 
> +    print("[rofs] rofs_close called\n"); 
> +    // Nothing to do really... 
> +    return 0; 
> +} 
> + 
> +// 
> +// This function reads symbolic link information from directory structure 
> in memory 
> +// under rofs->symlinks table 
> +static int rofs_readlink(struct vnode *vnode, struct uio *uio) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) vnode->v_mount->m_data; 
> +    struct rofs_inode *inode = (struct rofs_inode *) vnode->v_data; 
> + 
> +    if (!S_ISLNK(inode->mode)) { 
> +        return EINVAL; //This node is not a symbolic link 
> +    } 
> + 
> +    assert(inode->data_offset >= 0 && inode->data_offset < 
> rofs->sb->symlinks_count); 
> + 
> +    char *link_path = rofs->symlinks[inode->data_offset]; 
> + 
> +    print("[rofs] rofs_readlink returned link [%s]\n", link_path); 
> +    return uiomove(link_path, strlen(link_path), uio); 
> +} 
> + 
> +// 
> +// This function reads as much data as requested per uio in single read 
> from the disk but 
> +// the data does not get retained for subsequent reads 
> +static int rofs_read(struct vnode *vnode, struct file *fp, struct uio 
> *uio, int ioflag) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) vnode->v_mount->m_data; 
> +    struct rofs_super_block *sb = rofs->sb; 
> +    struct rofs_inode *inode = (struct rofs_inode *) vnode->v_data; 
> +    struct device *device = vnode->v_mount->m_dev; 
> + 
> +    VERIFY_READ_INPUT_ARGUMENTS() 
> + 
> +    int rv = 0; 
> +    int error = -1; 
> +    uint64_t block = inode->data_offset; 
> +    uint64_t offset = 0; 
> + 
> +    // Total read amount is what they requested, or what is left 
> +    uint64_t read_amt = std::min<uint64_t>(inode->file_size - 
> uio->uio_offset, uio->uio_resid); 
> + 
> +    // Calculate which block we need actually need to read 
> +    block += uio->uio_offset / sb->block_size; 
> +    offset = uio->uio_offset % sb->block_size; 
> + 
> +    uint64_t block_count = (offset + read_amt) / sb->block_size; 
> +    if ((offset + read_amt) % sb->block_size > 0) 
> +        block_count++; 
> + 
> +    void *buf = malloc(BSIZE * block_count); 
> + 
> +    print("[rofs] rofs_read [%d], inode: %d, [%d -> %d] at %d of %d 
> bytes\n", 
> +          sched::thread::current()->id(), inode->inode_no, block, 
> block_count, uio->uio_offset, read_amt); 
> + 
> +    error = rofs_read_blocks(device, block, block_count, buf); 
> + 
> +    if (error) { 
> +        kprintf("[rofs_read] Error reading data\n"); 
> +        free(buf); 
> +        return error; 
> +    } 
> + 
> +    rv = uiomove(buf + offset, read_amt, uio); 
> + 
> +    free(buf); 
> +    return rv; 
> +} 
> + 
> +// 
> +// This functions reads directory information (dentries) based on 
> information in memory 
> +// under rofs->dir_entries table 
> +static int rofs_readdir(struct vnode *vnode, struct file *fp, struct 
> dirent *dir) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) vnode->v_mount->m_data; 
> +    struct rofs_inode *inode = (struct rofs_inode *) vnode->v_data; 
> + 
> +    uint64_t index = 0; 
> + 
> +    if (!S_ISDIR(inode->mode)) { 
> +        return ENOTDIR; 
> +    } 
> + 
> +    if (fp->f_offset == 0) { 
> +        dir->d_type = DT_DIR; 
> +        strlcpy((char *) &dir->d_name, ".", sizeof(dir->d_name)); 
> +    } else if (fp->f_offset == 1) { 
> +        dir->d_type = DT_DIR; 
> +        strlcpy((char *) &dir->d_name, "..", sizeof(dir->d_name)); 
> +    } else { 
> +        index = fp->f_offset - 2; 
> +        if (index >= inode->dir_children_count) { 
> +            return ENOENT; 
> +        } 
> + 
> +        dir->d_fileno = fp->f_offset; 
> + 
> +        // Set the name 
> +        struct rofs_dir_entry *directory_entry = rofs->dir_entries + 
> (inode->data_offset + index); 
> +        strlcpy((char *) &dir->d_name, directory_entry->filename, 
> sizeof(dir->d_name)); 
> +        dir->d_ino = directory_entry->inode_no; 
> + 
> +        struct rofs_inode *directory_entry_inode = rofs->inodes + 
> (dir->d_ino - 1); 
> +        if (S_ISDIR(directory_entry_inode->mode)) 
> +            dir->d_type = DT_DIR; 
> +        else if (S_ISLNK(directory_entry_inode->mode)) 
> +            dir->d_type = DT_LNK; 
> +        else 
> +            dir->d_type = DT_REG; 
> +    } 
> + 
> +    fp->f_offset++; 
> + 
> +    return 0; 
> +} 
> + 
> +// 
> +// This functions looks up directory entry based on the directory 
> information stored in memory 
> +// under rofs->dir_entries table 
> +static int rofs_lookup(struct vnode *vnode, char *name, struct vnode 
> **vpp) 
> +{ 
> +    struct rofs_info *rofs = (struct rofs_info *) vnode->v_mount->m_data; 
> +    struct rofs_inode *inode = (struct rofs_inode *) vnode->v_data; 
> +    struct vnode *vp = nullptr; 
> + 
> +    if (*name == '\0') { 
> +        return ENOENT; 
> +    } 
> + 
> +    if (!S_ISDIR(inode->mode)) { 
> +        print("[rofs] ABORTED lookup up %s at inode %d because not a 
> directory\n", name, inode->inode_no); 
> +        return ENOTDIR; 
> +    } 
> + 
> +    for (unsigned int idx = 0; idx < inode->dir_children_count; idx++) { 
> +        if (strcmp(name, rofs->dir_entries[inode->data_offset + 
> idx].filename) == 0) { 
> +            int inode_no = rofs->dir_entries[inode->data_offset + 
> idx].inode_no; 
> + 
> +            if (vget(vnode->v_mount, inode_no, &vp)) { //TODO: Will it 
> ever work? Revisit 
> +                print("[rofs] found vp in cache!\n"); 
> +                *vpp = vp; 
> +                return 0; 
> +            } 
> + 
> +            struct rofs_inode *found_inode = rofs->inodes + (inode_no - 
> 1); //Check if exists 
> +            rofs_set_vnode(vp, found_inode); 
> + 
> +            print("[rofs] found the directory entry [%s] at at inode %d 
> -> %d!\n", name, inode->inode_no, 
> +                  found_inode->inode_no); 
> + 
> +            *vpp = vp; 
> +            return 0; 
> +        } 
> +    } 
> + 
> +    print("[rofs] FAILED to find up %s\n", name); 
> + 
> +    return ENOENT; 
> +} 
> + 
> +static int rofs_getattr(struct vnode *vnode, struct vattr *attr) 
> +{ 
> +    struct rofs_inode *inode = (struct rofs_inode *) vnode->v_data; 
> + 
> +    attr->va_mode = 0555; 
> + 
> +    if (S_ISDIR(inode->mode)) { 
> +        attr->va_type = VDIR; 
> +    } else if (S_ISREG(inode->mode)) { 
> +        attr->va_type = VREG; 
> +    } else if (S_ISLNK(inode->mode)) { 
> +        attr->va_type = VLNK; 
> +    } 
> + 
> +    attr->va_nodeid = vnode->v_ino; 
> +    attr->va_size = vnode->v_size; 
> + 
> +    return 0; 
> +} 
> + 
> +#define rofs_write       ((vnop_write_t)vop_erofs) 
> +#define rofs_seek        ((vnop_seek_t)vop_nullop) 
> +#define rofs_ioctl       ((vnop_ioctl_t)vop_nullop) 
> +#define rofs_create      ((vnop_create_t)vop_erofs) 
> +#define rofs_remove      ((vnop_remove_t)vop_erofs) 
> +#define rofs_rename      ((vnop_rename_t)vop_erofs) 
> +#define rofs_mkdir       ((vnop_mkdir_t)vop_erofs) 
> +#define rofs_rmdir       ((vnop_rmdir_t)vop_erofs) 
> +#define rofs_setattr     ((vnop_setattr_t)vop_erofs) 
> +#define rofs_inactive    ((vnop_inactive_t)vop_nullop) 
> +#define rofs_truncate    ((vnop_truncate_t)vop_erofs) 
> +#define rofs_link        ((vnop_link_t)vop_erofs) 
> +#define rofs_arc         ((vnop_cache_t) nullptr) 
> +#define rofs_fallocate   ((vnop_fallocate_t)vop_erofs) 
> +#define rofs_fsync       ((vnop_fsync_t)vop_nullop) 
> +#define rofs_symlink     ((vnop_symlink_t)vop_erofs) 
> + 
> +struct vnops rofs_vnops = { 
> +    rofs_open,               /* open */ 
> +    rofs_close,              /* close */ 
> +    rofs_read,               /* read */ 
> +    rofs_write,              /* write - returns error when called */ 
> +    rofs_seek,               /* seek */ 
> +    rofs_ioctl,              /* ioctl */ 
> +    rofs_fsync,              /* fsync */ 
> +    rofs_readdir,            /* readdir */ 
> +    rofs_lookup,             /* lookup */ 
> +    rofs_create,             /* create - returns error when called */ 
> +    rofs_remove,             /* remove - returns error when called */ 
> +    rofs_rename,             /* rename - returns error when called */ 
> +    rofs_mkdir,              /* mkdir - returns error when called */ 
> +    rofs_rmdir,              /* rmdir - returns error when called */ 
> +    rofs_getattr,            /* getattr */ 
> +    rofs_setattr,            /* setattr - returns error when called */ 
> +    rofs_inactive,           /* inactive */ 
> +    rofs_truncate,           /* truncate - returns error when called*/ 
> +    rofs_link,               /* link - returns error when called*/ 
> +    rofs_arc,                /* arc */ //TODO: Implement to allow memory 
> re-use when mapping files 
> +    rofs_fallocate,          /* fallocate - returns error when called*/ 
> +    rofs_readlink,           /* read link */ 
> +    rofs_symlink             /* symbolic link - returns error when 
> called*/ 
> +}; 
> diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc 
> index e053a78..cd619f5 100644 
> --- a/fs/vfs/main.cc 
> +++ b/fs/vfs/main.cc 
> @@ -2249,26 +2249,9 @@ static void import_extra_zfs_pools(void) 
>      } 
>  } 
>   
> -extern "C" void mount_zfs_rootfs(bool pivot_root) 
> +void pivot_rootfs(const char* path) 
>  { 
> -    int ret; 
> - 
> -    if (mkdir("/zfs", 0755) < 0) 
> -        kprintf("failed to create /zfs, error = %s\n", strerror(errno)); 
> - 
> -    ret = sys_umount("/dev"); 
> -    if (ret) 
> -        kprintf("failed to unmount /dev, error = %s\n", strerror(ret)); 
> - 
> -    ret = sys_mount("/dev/vblk0.1", "/zfs", "zfs", 0, (void *)"osv/zfs"); 
> -    if (ret) 
> -        kprintf("failed to mount /zfs, error = %s\n", strerror(ret)); 
> - 
> -    if (!pivot_root) { 
> -        return; 
> -    } 
> - 
> -    ret = sys_pivot_root("/zfs", "/"); 
> +    int ret = sys_pivot_root(path, "/"); 
>      if (ret) 
>          kprintf("failed to pivot root, error = %s\n", strerror(ret)); 
>   
> @@ -2294,6 +2277,52 @@ extern "C" void mount_zfs_rootfs(bool pivot_root) 
>          } 
>      } 
>      endmntent(ent); 
> +} 
> + 
> +extern "C" void unmount_devfs() 
> +{ 
> +    int ret = sys_umount("/dev"); 
> +    if (ret) 
> +        kprintf("failed to unmount /dev, error = %s\n", strerror(ret)); 
> +} 
> + 
> +extern "C" int mount_rofs_rootfs(bool pivot_root) 
> +{ 
> +    int ret; 
> + 
> +    if (mkdir("/rofs", 0755) < 0) 
> +        kprintf("failed to create /rofs, error = %s\n", strerror(errno)); 
> + 
> +    ret = sys_mount("/dev/vblk0.1", "/rofs", "rofs", MNT_RDONLY, 0); 
> + 
> +    if (ret) { 
> +        kprintf("failed to mount /rofs, error = %s\n", strerror(ret)); 
> +        rmdir("/rofs"); 
> +        return ret; 
> +    } 
> + 
> +    if (pivot_root) { 
> +        pivot_rootfs("/rofs"); 
> +    } 
> + 
> +    return 0; 
> +} 
> + 
> +extern "C" void mount_zfs_rootfs(bool pivot_root) 
> +{ 
> +    if (mkdir("/zfs", 0755) < 0) 
> +        kprintf("failed to create /zfs, error = %s\n", strerror(errno)); 
> + 
> +    int ret = sys_mount("/dev/vblk0.1", "/zfs", "zfs", 0, (void 
> *)"osv/zfs"); 
> + 
> +    if (ret) 
> +        kprintf("failed to mount /zfs, error = %s\n", strerror(ret)); 
> + 
> +    if (!pivot_root) { 
> +        return; 
> +    } 
> + 
> +    pivot_rootfs("/zfs"); 
>   
>      import_extra_zfs_pools(); 
>  } 
> diff --git a/fs/vfs/vfs_conf.cc b/fs/vfs/vfs_conf.cc 
> index 59e1b79..4ac695b 100644 
> --- a/fs/vfs/vfs_conf.cc 
> +++ b/fs/vfs/vfs_conf.cc 
> @@ -46,12 +46,14 @@ 
>  #include "vfs.h" 
>   
>  extern struct vfsops ramfs_vfsops; 
> +extern struct vfsops rofs_vfsops; 
>  extern struct vfsops devfs_vfsops; 
>  extern struct vfsops nfs_vfsops; 
>  extern struct vfsops procfs_vfsops; 
>  extern struct vfsops zfs_vfsops; 
>   
>  extern int ramfs_init(void); 
> +extern int rofs_init(void); 
>  extern int devfs_init(void); 
>  extern int nfs_init(void); 
>  extern int procfs_init(void); 
> @@ -66,5 +68,6 @@ const struct vfssw vfssw[] = { 
>          {"nfs",                nfs_init,        &nfs_vfsops}, 
>          {"procfs",        procfs_init,        &procfs_vfsops}, 
>          {"zfs",                zfs_init,        &zfs_vfsops}, 
> +        {"rofs",         rofs_init,         &rofs_vfsops}, 
>          {nullptr,        fs_noop,        nullptr}, 
>  }; 
> diff --git a/fs/vfs/vfs_vnode.cc b/fs/vfs/vfs_vnode.cc 
> index 7e1c7ab..13d1e5c 100644 
> --- a/fs/vfs/vfs_vnode.cc 
> +++ b/fs/vfs/vfs_vnode.cc 
> @@ -478,24 +478,27 @@ vnode_dump(void) 
>  int 
>  vop_nullop(void) 
>  { 
> - 
>          return 0; 
>  } 
>   
>  int 
>  vop_einval(void) 
>  { 
> - 
>          return EINVAL; 
>  } 
>   
>  int 
>  vop_eperm(void) 
>  { 
> - 
>          return EPERM; 
>  } 
>   
> +int 
> +vop_erofs(void) 
> +{ 
> +        return EROFS; 
> +} 
> + 
>  /* 
>   * vnode_init() is called once (from vfs_init) 
>   * in initialization. 
> diff --git a/include/osv/vnode.h b/include/osv/vnode.h 
> index 45bd93e..e35aa83 100755 
> --- a/include/osv/vnode.h 
> +++ b/include/osv/vnode.h 
> @@ -210,6 +210,7 @@ struct vnops { 
>  int         vop_nullop(void); 
>  int         vop_einval(void); 
>  int         vop_eperm(void); 
> +int         vop_erofs(void); 
>  struct vnode *vn_lookup(struct mount *, uint64_t); 
>  void         vn_lock(struct vnode *); 
>  void         vn_unlock(struct vnode *); 
> diff --git a/licenses/mfs.txt b/licenses/mfs.txt 
> new file mode 100644 
> index 0000000..b7061e0 
> --- /dev/null 
> +++ b/licenses/mfs.txt 
> @@ -0,0 +1,46 @@ 
> +Copyright (c) 2015 Carnegie Mellon University. 
> +All Rights Reserved. 
> + 
> +Redistribution and use in source and binary forms, with or without 
> +modification, are permitted provided that the following conditions are 
> met: 
> +     
> +    1. Redistributions of source code must retain the above copyright 
> notice, 
> +       this list of conditions and the following acknowledgments and 
> +       disclaimers. 
> + 
> +    2. Redistributions in binary form must reproduce the above copyright 
> +       notice, this list of conditions and the following acknowledgments 
> and 
> +       disclaimers in the documentation and/or other materials provided 
> with 
> +       the distribution. 
> + 
> +    3. Products derived from this software may not include “Carnegie 
> Mellon 
> +       University,” "SEI” and/or “Software Engineering Institute" in the 
> name 
> +       of such derived product, nor shall “Carnegie Mellon University,” 
> "SEI” 
> +       and/or “Software Engineering Institute" be used to endorse or 
> promote 
> +       products derived from this software without prior written 
> permission. 
> +       For written permission, please contact permi...@sei.cmu.edu. 
> + 
> +ACKNOWLEDGMENTS AND DISCLAIMERS: 
> +Copyright 2015 Carnegie Mellon University 
> + 
> +This material is based upon work funded and supported by the Department 
> of 
> +Defense under Contract No. FA8721-05-C-0003 with Carnegie Mellon 
> University 
> +for the operation of the Software Engineering Institute, a federally 
> funded 
> +research and development center. 
> + 
> +Any opinions, findings and conclusions or recommendations expressed in 
> this 
> +material are those of the author(s) and do not necessarily reflect the 
> views of 
> +the United States Department of Defense. 
> + 
> +NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING 
> INSTITUTE 
> +MATERIAL IS FURNISHED ON AN “AS-IS” BASIS. CARNEGIE MELLON UNIVERSITY 
> MAKES NO 
> +WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER 
> +INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR 
> +MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE 
> MATERIAL. 
> +CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH 
> RESPECT 
> +TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. 
> + 
> +This material has been approved for public release and unlimited 
> distribution. 
> + 
> +DM-0002621 
> + 
> diff --git a/loader.cc b/loader.cc 
> index c3a7256..1b2892c 100644 
> --- a/loader.cc 
> +++ b/loader.cc 
> @@ -84,7 +84,10 @@ void setup_tls(elf::init_table inittab) 
>  extern "C" { 
>      void premain(); 
>      void vfs_init(void); 
> +    void unmount_devfs(); 
>      void mount_zfs_rootfs(bool); 
> +    int mount_rofs_rootfs(bool); 
> +    void rofs_disable_cache(); 
>  } 
>   
>  void premain() 
> @@ -165,7 +168,7 @@ void parse_options(int loader_argc, char** 
> loader_argv) 
>          ("maxnic", bpo::value<int>(), "maximum NIC number") 
>          ("norandom", "don't initialize any random device") 
>          ("noshutdown", "continue running after main() returns") 
> -        ("power-off-on-abort", "use poweroff instead of halt if it's 
> aborted") 
> +        ("power-off-on-abort", "use poweroff instead of halt if it's 
> aborted") 
>          ("noinit", "don't run commands from /init") 
>          ("verbose", "be verbose, print debug messages") 
>          ("console", bpo::value<std::vector<std::string>>(), "select 
> console driver") 
> @@ -335,11 +338,22 @@ void* do_main_thread(void *_main_args) 
>      boot_time.event("drivers loaded"); 
>   
>      if (opt_mount) { 
> -        zfsdev::zfsdev_init(); 
> -        mount_zfs_rootfs(opt_pivot); 
> -        bsd_shrinker_init(); 
> +        unmount_devfs(); 
> +        // 
> +        // Try to mount rofs 
> +        if(mount_rofs_rootfs(opt_pivot) != 0) { 
> +            // 
> +            // Failed -> try to mount zfs 
> +            zfsdev::zfsdev_init(); 
> +            mount_zfs_rootfs(opt_pivot); 
> +            bsd_shrinker_init(); 
> + 
> +            boot_time.event("ZFS mounted"); 
> +        } 
> +        else { 
> +            boot_time.event("ROFS mounted"); 
> +        } 
>      } 
> -    boot_time.event("ZFS mounted"); 
>   
>      bool has_if = false; 
>      osv::for_each_if([&has_if] (std::string if_name) { 
> diff --git a/modules/httpserver-api/api/fs.cc 
> b/modules/httpserver-api/api/fs.cc 
> index e30c840..4151cd7 100644 
> --- a/modules/httpserver-api/api/fs.cc 
> +++ b/modules/httpserver-api/api/fs.cc 
> @@ -51,7 +51,7 @@ void init(routes& routes) { 
>          vector<httpserver::json::DFStat> dfstats; 
>   
>          for (mount_desc mount : osv::current_mounts()) { 
> -            if (mount.type == "zfs" && (onemount == "" || onemount == 
> mount.path)) { 
> +            if ((mount.type == "zfs" || mount.type == "rofs") && 
> (onemount == "" || onemount == mount.path)) { 
>                  if (statvfs(mount.path.c_str(),&st) != 0) { 
>                      throw not_found_exception("mount does not exist"); 
>                  } 
> @@ -75,7 +75,7 @@ void init(routes& routes) { 
>              vector<httpserver::json::DFStat> res; 
>   
>              for (osv::mount_desc mount : osv::current_mounts()) { 
> -                if (mount.type == "zfs") { 
> +                if (mount.type == "zfs" || mount.type == "rofs") { 
>                      if (statvfs(mount.path.c_str(),&st) == 0) { 
>                          fill_dfstat(dfstat, mount, st); 
>                          res.push_back(dfstat); 
> diff --git a/scripts/build b/scripts/build 
> index f7cd0ee..eac860f 100755 
> --- a/scripts/build 
> +++ b/scripts/build 
> @@ -134,9 +134,10 @@ manifest=$OUT/bootfs.manifest 
>  fs_type=${vars[fs]-zfs} 
>  usrskel_arg= 
>  case $fs_type in 
> -zfs)        ;; # Nothing to change here. This is our default behavior 
> +zfs);; # Nothing to change here. This is our default behavior 
> +rofs)        usrskel_arg="--usrskel usr_rofs.manifest.skel";; 
>  ramfs)        manifest=$OUT/usr.manifest 
> -        usrskel_arg="--usrskel usr_nozfs.manifest.skel";; 
> +        usrskel_arg="--usrskel usr_ramfs.manifest.skel";; 
>  *)        echo "Unknown filesystem \"$fs_type\"" >&2 
>          exit 2 
>  esac 
> @@ -170,8 +171,7 @@ then 
>  fi 
>   
>  loader_size=`stat --printf %s $OUT/loader.img` 
> -zfs_start=$(($loader_size+2097151 & ~2097151)) 
> -zfs_size=$(($fs_size - $zfs_start)) 
> +kernel_end=$(($loader_size+2097151 & ~2097151)) 
>   
>  # The python scripts called below assume the current directory is $OUT 
> (as was 
>  # the case in our old build.mk). 
> @@ -180,7 +180,8 @@ cd $OUT 
>  case $fs_type in 
>  zfs) 
>          cp loader.img bare.raw 
> -        $SRC/scripts/imgedit.py setpartition "-f raw bare.raw" 2 
> $zfs_start $zfs_size 
> +        zfs_size=$(($fs_size - $kernel_end)) 
> +        $SRC/scripts/imgedit.py setpartition "-f raw bare.raw" 2 
> $kernel_end $zfs_size 
>   
>          qemu-img convert -f raw -O qcow2 bare.raw usr.img 
>          qemu-img resize usr.img ${fs_size}b >/dev/null 2>&1 
> @@ -193,6 +194,17 @@ zfs) 
>                  $SRC/scripts/export_manifest.py -e "$export_dir" -m 
> usr.manifest -D jdkbase=$jdkbase -D gccbase=$gccbase -D 
> glibcbase=$glibcbase -D miscbase=$miscbase 
>          fi 
>          ;; 
> +rofs) 
> +        rm -rf rofs.img 
> +        $SRC/scripts/gen-${fs_type}-img.py -o rofs.img -m usr.manifest -D 
> jdkbase=$jdkbase -D gccbase=$gccbase -D glibcbase=$glibcbase -D 
> miscbase=$miscbase 
> +        rofs_size=`stat --printf %s rofs.img` 
> +        img_size=$((kernel_end + rofs_size)) 
> +        cp loader.img bare.raw 
> +        $SRC/scripts/imgedit.py setpartition "-f raw bare.raw" 2 
> $kernel_end $rofs_size 
> +        qemu-img resize bare.raw ${img_size}b >/dev/null 2>&1 
> +        dd if=rofs.img of=bare.raw obs=${kernel_end} seek=1 >/dev/null 
> 2>&1 
> +        qemu-img convert -f raw -O qcow2 bare.raw usr.img 
> +        ;; 
>  ramfs) 
>          qemu-img convert -f raw -O qcow2 loader.img usr.img 
>          ;; 
> diff --git a/scripts/gen-rofs-img.py b/scripts/gen-rofs-img.py 
> new file mode 100755 
> index 0000000..e825c63 
> --- /dev/null 
> +++ b/scripts/gen-rofs-img.py 
> @@ -0,0 +1,373 @@ 
> +#!/usr/bin/python 
> + 
> +# 
> +# Copyright (c) 2015 Carnegie Mellon University. 
> +# All Rights Reserved. 
> +# 
> +# THIS SOFTWARE IS PROVIDED "AS IS," WITH NO WARRANTIES WHATSOEVER. 
> CARNEGIE 
> +# MELLON UNIVERSITY EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT PERMITTEDBY 
> LAW 
> +# ALL EXPRESS, IMPLIED, AND STATUTORY WARRANTIES, INCLUDING, WITHOUT 
> +# LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
> +# PURPOSE, AND NON-INFRINGEMENT OF PROPRIETARY RIGHTS. 
> +# 
> +# Released under a modified BSD license. For full terms, please see 
> mfs.txt in 
> +# the licenses folder or contact permi...@sei.cmu.edu. 
> +# 
> +# DM-0002621 
> +# 
> +# 
> +# Copyright (C) 2017 Waldemar Kozaczuk 
> +# Inspired by original MFS implementation by James Root from 2015 
> +# 
> +# 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. 
> +# 
> + 
> +##################################################################################
>  
>
> +# The layout of data on the disk in the block order: 
> +# 
> +# Super Block (512 bytes) that contains magic number and specifies meta 
> +# information including block size and location and size of tables 
> containing 
> +# i-nodes, dentries and symbolic links 
> +# 
> +# Files data where each file is padded to 512 bytes block 
> +# 
> +# Table of directory entries referenced by index in directory i-node 
> +# (each entry holds string with direntry name and i-node number) 
> +# 
> +# Table of symlinks referenced by symlink i-node (each entry holds 
> symbolic link 
> +# path string) 
> +# 
> +# Table of inodes where each specifies type (dir,file,symlink) and data 
> offset 
> +# (for files it is a block on a disk, for symlinks and directories it is 
> an 
> +# offset in one of the 2 tables above) 
> +##################################################################################
>  
>
> + 
> +import os, optparse, io 
> +from struct import * 
> +from ctypes import * 
> +from manifest_common import add_var, expand, unsymlink, read_manifest, 
> defines, strip_file 
> + 
> +OSV_BLOCK_SIZE = 512 
> + 
> +DIR_MODE  = int('0x4000', 16) 
> +REG_MODE  = int('0x8000', 16) 
> +LINK_MODE = int('0xA000', 16) 
> + 
> +block = 0 
> + 
> +class SuperBlock(Structure): 
> +    _fields_ = [ 
> +        ('magic', c_ulonglong), 
> +        ('version', c_ulonglong), 
> +        ('block_size', c_ulonglong), 
> +        ('structure_info_first_block', c_ulonglong), 
> +        ('structure_info_blocks_count', c_ulonglong), 
> +        ('directory_entries_count', c_ulonglong), 
> +        ('symlinks_count', c_ulonglong), 
> +        ('inodes_count', c_ulonglong) 
> +    ] 
> + 
> +# data_offset and count represent different things depending on mode: 
> +# file - number of first block on disk and size in bytes (number of 
> blocks can be deduced) 
> +# directory - index of the first entry in the directory entries array and 
> number of entries 
> +# symlink - index of the entry in the symlink entries array and 1 
> +class Inode(Structure): 
> +    _fields_ = [ 
> +        ('mode', c_ulonglong), 
> +        ('inode_no', c_ulonglong), #redundant 
> +        ('data_offset', c_ulonglong), 
> +        ('count', c_ulonglong) # either file size or children count 
> +    ] 
> + 
> +# Represents directory entry - file, subdirectory or symlink 
> +# It has a name and i-node number so to know what type of 
> +# entry it is one has to read the i-node 
> +# filename (length: unsigned short followed by characters) 
> +class DirectoryEntry(object): 
> +    def __init__(self,filename,inode_no): 
> +        self.filename = filename 
> +        self.inode_no = inode_no 
> + 
> +    def write(self,fp): 
> +        pos = fp.tell() 
> +        fp.write(c_ulonglong(self.inode_no)) 
> +        fp.write(c_ushort(len(self.filename))) 
> +        for c in self.filename: 
> +            fp.write(c_char(c)) 
> + 
> +        return fp.tell() - pos 
> + 
> +class Symbo...

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to