Skip to content

Commit

Permalink
ext4: add initial implementation
Browse files Browse the repository at this point in the history
This commit adds an initial implementation of the ext4
filesystem driver based on the lwext4 project
(https://github.com/gkostka/lwext4). It provides a light weight
read-write alternative to ZFS filesystem.

Please note this implementation is NOT thread-safe
and will need to be enhanced to be so in future. However it is
functional enough to support the test cases examined by
modules/libext/test.sh.

One can build the OSv like so:

./scripts/manifest_from_host.sh -w /usr/bin/find && ./scripts/build fs=rofs image=libext,native-example -j$(nproc)
--append-manifest

Then create an ext4 filesystem:

mkdir -p ext_images
dd if=/dev/zero of=ext_images/ext4 bs=1M count=128
sudo mkfs.ext4 ext_images/ext4

Add some files to it if needed:

sudo losetup -o 0 -f --show ext_images/ext4
sudo mount /dev/loop0 ext_images/image

.. update content

sudo umount ext_images/image
sudo losetup -d /dev/loop0

qemu-img convert -f raw -O qcow2 ext_images/ext4 ext_images/ext4.img

And then run it:

./scripts/run.py --execute='--mount-fs=ext,/dev/vblk1,/data /hello' --second-disk-image ./ext_images/ext4.img

or using the test.sh

./modules/libext/test.sh '/find /data/ -ls'

Fixes cloudius-systems#1179

Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
  • Loading branch information
wkozaczuk committed Mar 18, 2024
1 parent 2d14fb7 commit 1a3ed69
Show file tree
Hide file tree
Showing 12 changed files with 1,715 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2075,6 +2075,7 @@ endif
boost-libs := $(boost-lib-dir)/libboost_system$(boost-mt).a

objects += fs/nfs/nfs_null_vfsops.o
objects += fs/ext/ext_null_vfsops.o

# The OSv kernel is linked into an ordinary, non-PIE, executable, so there is no point in compiling
# with -fPIC or -fpie and objects that can be linked into a PIE. On the contrary, PIE-compatible objects
Expand Down
43 changes: 43 additions & 0 deletions fs/ext/ext_null_vfsops.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2024 Waldemar Kozaczuk
*
* Based on ramfs code Copyright (c) 2006-2007, Kohsuke Ohtani
*
* 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/mount.h>

#define ext_mount ((vfsop_mount_t)vfs_nullop)
#define ext_umount ((vfsop_umount_t)vfs_nullop)
#define ext_sync ((vfsop_sync_t)vfs_nullop)
#define ext_vget ((vfsop_vget_t)vfs_nullop)
#define ext_statfs ((vfsop_statfs_t)vfs_nullop)

static int ext_noop_mount(struct mount *mp, const char *dev, int flags,
const void *data)
{
printf("The ext module is in-active!. Please add ext module to the image.\n");
return -1;
}

/*
* File system operations
*
* This deactivates the EXT file system when libext.so is not loaded.
*
*/
struct vfsops ext_vfsops = {
ext_noop_mount, /* mount */
ext_umount, /* umount */
ext_sync, /* sync */
ext_vget, /* vget */
ext_statfs, /* statfs */
nullptr, /* vnops */
};

extern "C" int ext_init(void)
{
return 0;
}
3 changes: 3 additions & 0 deletions fs/vfs/vfs_conf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern struct vfsops zfs_vfsops;
#if CONF_drivers_virtio_fs
extern struct vfsops virtiofs_vfsops;
#endif
extern struct vfsops ext_vfsops;

extern int ramfs_init(void);
extern int rofs_init(void);
Expand All @@ -67,6 +68,7 @@ extern int nfs_init(void);
extern int procfs_init(void);
extern int sysfs_init(void);
extern "C" int zfs_init(void);
extern "C" int ext_init(void);

/*
* VFS switch table
Expand All @@ -82,5 +84,6 @@ const struct vfssw vfssw[] = {
#if CONF_drivers_virtio_fs
{"virtiofs", virtiofs_init, &virtiofs_vfsops},
#endif
{"ext", ext_init, &ext_vfsops},
{nullptr, fs_noop, nullptr},
};
1 change: 1 addition & 0 deletions modules/libext/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
usr.manifest
33 changes: 33 additions & 0 deletions modules/libext/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
arch=x64
include ../common.gmk

module_out := $(out)/modules/libext

CXXFLAGS = -fPIC -std=gnu++11 $(INCLUDES) -I../lwext4/upstream/lwext4/include -I../lwext4/upstream/lwext4/build_lib_only/include \
-D_KERNEL -D_GNU_SOURCE -Wall -fno-exceptions -fno-rtti

# the build target executable:
TARGET = libext
CPP_FILES := ext_vfsops.cc ext_vnops.cc
OBJ_FILES := $(addprefix $(module_out)/,$(CPP_FILES:.cc=.o))
DEPS := $(OBJ_FILES:.o=.d)

LIBS = -L../lwext4/upstream/lwext4/build_lib_only/src/ -llwext4

$(module_out)/$(TARGET).so: $(OBJ_FILES)
$(call quiet, $(CXX) $(CXXFLAGS) $(LDFLAGS) -static-libstdc++ -shared -o $(module_out)/$(TARGET).so $^ $(LIBS), LINK $@)

$(module_out)/%.o: %.cc
$(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $@)

init:
@echo " MKDIRS"
$(call very-quiet, mkdir -p $(module_out))
.PHONY: init

module: init $(module_out)/$(TARGET).so
echo '/usr/lib/fs/libext.so: ./modules/libext/libext.so' > usr.manifest

clean:
rm -f $(TARGET)*.so usr.manifest
$(call very-quiet, $(RM) -rf $(module_out))
216 changes: 216 additions & 0 deletions modules/libext/ext_vfsops.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Copyright (C) 2024 Waldemar Kozaczuk
*
* 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.
*/

extern "C" {
#define USE_C_INTERFACE 1
#include <osv/device.h>
#include <osv/bio.h>
#include <osv/prex.h>
#include <osv/vnode.h>
#include <osv/mount.h>
#include <osv/debug.h>

void* alloc_contiguous_aligned(size_t size, size_t align);
void free_contiguous_aligned(void* p);
}

#include <ext4_blockdev.h>
#include <ext4_debug.h>
#include <ext4_fs.h>
#include <ext4_super.h>

#include <cstdlib>
#include <cstddef>

extern "C" bool is_linear_mapped(const void *addr);

int ext_init(void) { return 0;}

static int blockdev_open(struct ext4_blockdev *bdev)
{
return EOK;
}

static int blockdev_bread_or_write(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, uint32_t blk_cnt, bool read)
{
struct bio *bio = alloc_bio();
if (!bio)
return ENOMEM;

bio->bio_cmd = read ? BIO_READ : BIO_WRITE;
bio->bio_dev = (struct device*)bdev->bdif->p_user;
bio->bio_offset = blk_id * bdev->bdif->ph_bsize;
bio->bio_bcount = blk_cnt * bdev->bdif->ph_bsize;

if (!is_linear_mapped(buf)) {
bio->bio_data = alloc_contiguous_aligned(bio->bio_bcount, alignof(std::max_align_t));
if (!read) {
memcpy(bio->bio_data, buf, bio->bio_bcount);
}
} else {
bio->bio_data = buf;
}

bio->bio_dev->driver->devops->strategy(bio);
int error = bio_wait(bio);

kprintf("[ext4] %s %ld bytes at offset %ld to %p with error:%d\n", read ? "Read" : "Wrote",
bio->bio_bcount, bio->bio_offset, bio->bio_data, error);

if (!is_linear_mapped(buf)) {
if (read && !error) {
memcpy(buf, bio->bio_data, bio->bio_bcount);
}
free_contiguous_aligned(bio->bio_data);
}
destroy_bio(bio);

return error;
}

static int blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, uint32_t blk_cnt)
{
return blockdev_bread_or_write(bdev, buf, blk_id, blk_cnt, true);
}

static int blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf,
uint64_t blk_id, uint32_t blk_cnt)
{
return blockdev_bread_or_write(bdev, const_cast<void *>(buf), blk_id, blk_cnt, false);
}

static int blockdev_close(struct ext4_blockdev *bdev)
{
return EOK;
}

EXT4_BLOCKDEV_STATIC_INSTANCE(ext_blockdev, 512, 0, blockdev_open,
blockdev_bread, blockdev_bwrite, blockdev_close, 0, 0);

static struct ext4_fs ext_fs;
static struct ext4_bcache ext_block_cache;
extern struct vnops ext_vnops;

static int
ext_mount(struct mount *mp, const char *dev, int flags, const void *data)
{
struct device *device;

const char *dev_name = dev + 5;
kprintf("[ext4] Trying to open device: [%s]\n", dev_name);
int error = device_open(dev_name, DO_RDWR, &device);

if (error) {
kprintf("[ext4] Error opening device!\n");
return error;
}

ext4_dmask_set(DEBUG_ALL);
//
// Save a reference to the filesystem
mp->m_dev = device;
ext_blockdev.bdif->p_user = device;
ext_blockdev.part_offset = 0;
ext_blockdev.part_size = device->size;
ext_blockdev.bdif->ph_bcnt = ext_blockdev.part_size / ext_blockdev.bdif->ph_bsize;

kprintf("[ext4] Trying to mount ext4 on device: [%s] with size:%ld\n", dev_name, device->size);
int r = ext4_block_init(&ext_blockdev);
if (r != EOK)
return r;

r = ext4_fs_init(&ext_fs, &ext_blockdev, false);
if (r != EOK) {
ext4_block_fini(&ext_blockdev);
return r;
}

uint32_t bsize = ext4_sb_get_block_size(&ext_fs.sb);
ext4_block_set_lb_size(&ext_blockdev, bsize);

r = ext4_bcache_init_dynamic(&ext_block_cache, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);
if (r != EOK) {
ext4_block_fini(&ext_blockdev);
return r;
}

if (bsize != ext_block_cache.itemsize)
return ENOTSUP;

/*Bind block cache to block device*/
r = ext4_block_bind_bcache(&ext_blockdev, &ext_block_cache);
if (r != EOK) {
ext4_bcache_cleanup(&ext_block_cache);
ext4_block_fini(&ext_blockdev);
ext4_bcache_fini_dynamic(&ext_block_cache);
return r;
}

ext_blockdev.fs = &ext_fs;
mp->m_data = &ext_fs;
mp->m_root->d_vnode->v_ino = EXT4_INODE_ROOT_INDEX;

kprintf("[ext4] Mounted ext4 on device: [%s] with code:%d\n", dev_name, r);
return r;
}

static int
ext_unmount(struct mount *mp, int flags)
{
int r = ext4_fs_fini(&ext_fs);
if (r == EOK) {
ext4_bcache_cleanup(&ext_block_cache);
ext4_bcache_fini_dynamic(&ext_block_cache);
}

r = ext4_block_fini(&ext_blockdev);
kprintf("[ext4] Trying to unmount ext4 (after %d)!\n", r);
return device_close((struct device*)ext_blockdev.bdif->p_user);
}

static int
ext_sync(struct mount *mp)
{
return EIO;
}

static int
ext_statfs(struct mount *mp, struct statfs *statp)
{
kprintf("[ext4] statfs\n");
struct ext4_fs *fs = (struct ext4_fs *)mp->m_data;
statp->f_bsize = ext4_sb_get_block_size(&fs->sb);

statp->f_blocks = ext4_sb_get_blocks_cnt(&fs->sb);
statp->f_bfree = ext4_sb_get_free_blocks_cnt(&fs->sb);
statp->f_bavail = ext4_sb_get_free_blocks_cnt(&fs->sb);

statp->f_ffree = ext4_get32(&fs->sb, free_inodes_count);
statp->f_files = ext4_get32(&fs->sb, inodes_count);

statp->f_namelen = EXT4_DIRECTORY_FILENAME_LEN;
statp->f_type = EXT4_SUPERBLOCK_MAGIC;

statp->f_fsid = mp->m_fsid; /* File system identifier */
return EOK;
}

// We are relying on vfsops structure defined in kernel
extern struct vfsops ext_vfsops;

// Overwrite "null" vfsops structure fields with "real"
// functions upon loading libext.so shared object
void __attribute__((constructor)) initialize_vfsops() {
ext_vfsops.vfs_mount = ext_mount;
ext_vfsops.vfs_unmount = ext_unmount;
ext_vfsops.vfs_sync = ext_sync;
ext_vfsops.vfs_vget = ((vfsop_vget_t)vfs_nullop);
ext_vfsops.vfs_statfs = ext_statfs;
ext_vfsops.vfs_vnops = &ext_vnops;
}

asm(".pushsection .note.osv-mlock, \"a\"; .long 0, 0, 0; .popsection");
Loading

0 comments on commit 1a3ed69

Please sign in to comment.