Skip to content

Commit

Permalink
Implemented Read-Only File System (ROFS)
Browse files Browse the repository at this point in the history
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

Fixes #195

Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
Message-Id: <1521695448-28564-2-git-send-email-jwkozaczuk@gmail.com>
  • Loading branch information
wkozaczuk authored and nyh committed Mar 22, 2018
1 parent 6454485 commit cd44966
Show file tree
Hide file tree
Showing 17 changed files with 1,256 additions and 34 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
117 changes: 117 additions & 0 deletions fs/rofs/rofs.hh
Original file line number Diff line number Diff line change
@@ -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
85 changes: 85 additions & 0 deletions fs/rofs/rofs_common.cc
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit cd44966

Please sign in to comment.