forked from cloudius-systems/osv
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
12 changed files
with
1,715 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
usr.manifest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); |
Oops, something went wrong.