Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
*.iml
*.dat

AGENTS.md

# Generate by Makefile
/kernel/aplus

Expand Down Expand Up @@ -64,4 +66,4 @@ Cargo.lock
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
*.pdb
3 changes: 3 additions & 0 deletions apps/test/ext2-write-test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
INCLUDES += $(ROOTDIR)/include
include $(ROOTDIR)/build/cross.mk
include $(ROOTDIR)/build/build-binary.mk
144 changes: 144 additions & 0 deletions apps/test/ext2-write-test/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>


#define TEST_PATH "/ext2-write-test.bin"
#define PAYLOAD_SIZE (5 * 1024 * 1024 + 37)
#define GAP_SIZE 8193


static int fail(const char* operation) {
perror(operation);
unlink(TEST_PATH);
return 1;
}


static void fill_pattern(uint8_t* data, size_t size) {
for (size_t i = 0; i < size; i++)
data[i] = (i * 31U + 17U) & 0xff;
}


int main(void) {
uint8_t* expected = malloc(PAYLOAD_SIZE + GAP_SIZE + 16);
uint8_t* actual = malloc(PAYLOAD_SIZE + GAP_SIZE + 16);

if (!expected || !actual)
return fail("malloc");

fill_pattern(expected, PAYLOAD_SIZE);
unlink(TEST_PATH);

int fd = open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0644);
if (fd < 0)
return fail("open(create)");

size_t written = 0;
while (written < PAYLOAD_SIZE) {
size_t chunk = 7777;
if (chunk > PAYLOAD_SIZE - written)
chunk = PAYLOAD_SIZE - written;

ssize_t result = write(fd, expected + written, chunk);
if (result <= 0)
return fail("write(payload)");

written += result;
}

if (lseek(fd, 13, SEEK_SET) != 13)
return fail("lseek(overwrite)");

static const char overwrite[] = "unaligned-overwrite";
memcpy(expected + 13, overwrite, sizeof(overwrite));

if (write(fd, overwrite, sizeof(overwrite)) != sizeof(overwrite))
return fail("write(overwrite)");

if (lseek(fd, PAYLOAD_SIZE + GAP_SIZE, SEEK_SET) != PAYLOAD_SIZE + GAP_SIZE)
return fail("lseek(gap)");

static const char marker[] = "gap-marker";
memset(expected + PAYLOAD_SIZE, 0, GAP_SIZE);
memcpy(expected + PAYLOAD_SIZE + GAP_SIZE, marker, sizeof(marker));

if (write(fd, marker, sizeof(marker)) != sizeof(marker))
return fail("write(gap)");

size_t expanded_size = PAYLOAD_SIZE + GAP_SIZE + sizeof(marker);

if (lseek(fd, 0, SEEK_SET) != 0)
return fail("lseek(readback)");

size_t read_bytes = 0;
while (read_bytes < expanded_size) {
ssize_t result = read(fd, actual + read_bytes, expanded_size - read_bytes);
if (result <= 0)
return fail("read(readback)");

read_bytes += result;
}

if (memcmp(expected, actual, expanded_size) != 0) {
errno = EIO;
return fail("compare(readback)");
}

size_t shrunk_size = 12345;
if (ftruncate(fd, shrunk_size) < 0)
return fail("ftruncate(shrink)");

if (ftruncate(fd, expanded_size) < 0)
return fail("ftruncate(grow)");

if (lseek(fd, shrunk_size, SEEK_SET) != (off_t)shrunk_size)
return fail("lseek(grown-range)");

memset(actual, 0xff, expanded_size - shrunk_size);
if (read(fd, actual, expanded_size - shrunk_size) != (ssize_t)(expanded_size - shrunk_size))
return fail("read(grown-range)");

for (size_t i = 0; i < expanded_size - shrunk_size; i++) {
if (actual[i] != 0) {
errno = EIO;
return fail("compare(grown-range)");
}
}

if (close(fd) < 0)
return fail("close");

fd = open(TEST_PATH, O_WRONLY | O_TRUNC);
if (fd < 0)
return fail("open(truncate)");

if (close(fd) < 0)
return fail("close(truncate)");

struct stat st;
if (stat(TEST_PATH, &st) < 0 || st.st_size != 0) {
errno = EIO;
return fail("stat(truncate)");
}

if (unlink(TEST_PATH) < 0)
return fail("unlink");

if (stat(TEST_PATH, &st) == 0 || errno != ENOENT) {
errno = EIO;
return fail("stat(unlinked)");
}

free(actual);
free(expected);

printf("ext2-write-test: PASS\n");
return 0;
}
2 changes: 1 addition & 1 deletion drivers/dev/block/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ ssize_t block_write(device_t* device, const void* buf, off_t offset, size_t size

current_task->rusage.ru_oublock++;


__cache_update(device->blk.cache, 0, 0);

offset += device->blk.blkoff * device->blk.blksize;

Expand Down
73 changes: 52 additions & 21 deletions kernel/fs/ext2/ext2.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@


#define EXT2_SIGNATURE 0xEF53
#define EXT2_NAME_LEN 255


// TODO: rewrite all ext2 driver to support cache
Expand Down Expand Up @@ -87,6 +88,8 @@ struct ext2_group_desc {
uint32_t bg_reserved[3];
};

_Static_assert(sizeof(struct ext2_group_desc) == 32, "invalid ext2 group descriptor layout");


/*
* Macro-instructions used to manage group descriptors
Expand Down Expand Up @@ -164,6 +167,8 @@ struct ext2_inode {
} osd2; /* OS dependent 2 */
};

_Static_assert(sizeof(struct ext2_inode) == 128, "invalid ext2 inode layout");

#define i_size_high i_dir_acl
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_frag osd2.linux2.l_i_frag
Expand Down Expand Up @@ -299,6 +304,8 @@ struct ext2_super_block {
uint32_t s_reserved[190]; /* Padding to the end of the block */
};

_Static_assert(sizeof(struct ext2_super_block) == 1024, "invalid ext2 superblock layout");

/*
* Codes for operating systems
*/
Expand Down Expand Up @@ -352,6 +359,11 @@ struct ext2_super_block {
#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP
#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP

#define EXT2_FEATURE_INCOMPAT_WRITE_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE
#define EXT2_FEATURE_RO_COMPAT_WRITE_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT2_FEATURE_RO_COMPAT_LARGE_FILE)

#define EXT2_INDEX_FL 0x00001000



/*
Expand Down Expand Up @@ -446,35 +458,45 @@ typedef struct {



#define ext2_inode_set_size(ext2, inode, size) \
{ \
\
(inode)->i_size = (size) & 0xFFFFFFFF; \
\
if ((ext2)->sb.s_rev_level == EXT2_DYNAMIC_REV) { \
(inode)->i_size_high = ((size) << 32); \
} \
static inline uint64_t ext2_inode_get_size(ext2_t* ext2, struct ext2_inode* inode) {
uint64_t size = inode->i_size;

if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV && S_ISREG(inode->i_mode)) {
size |= (uint64_t)inode->i_size_high << 32;
}

return size;
}

static inline void ext2_inode_set_size(ext2_t* ext2, struct ext2_inode* inode, uint64_t size) {
inode->i_size = size & UINT32_MAX;

if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV && S_ISREG(inode->i_mode)) {
inode->i_size_high = size >> 32;
}
}



int ext2_fsync(inode_t*, int);
int ext2_getattr(inode_t*, struct stat*);
int ext2_setattr(inode_t*, struct stat*);

// int ext2_truncate (inode_t*, off_t);
int ext2_truncate(inode_t*, off_t);

ssize_t ext2_read(inode_t*, void*, off_t, size_t);
ssize_t ext2_write(inode_t*, const void*, off_t, size_t);
ssize_t ext2_readlink(inode_t*, char*, size_t);

// inode_t* ext2_creat (inode_t*, const char*, mode_t);
inode_t* ext2_creat(inode_t*, const char*, mode_t);
inode_t* ext2_finddir(inode_t*, const char*);
ssize_t ext2_readdir(inode_t*, struct dirent*, off_t, size_t);

// int ext2_rename (inode_t*, const char*, const char*);
// int ext2_symlink (inode_t*, const char*, const char*);
// int ext2_unlink (inode_t*, const char*);
int ext2_unlink(inode_t*, const char*);

inode_t* ext2_utils_create_vfs_inode(inode_t*, ino_t, const char*, mode_t);


struct ext2_inode* ext2_icache_fetch(cache_t* cache, ext2_t* ext2, ino_t ino);
Expand All @@ -485,20 +507,29 @@ void* ext2_bcache_fetch(cache_t* cache, ext2_t* ext2, uint64_t block);
void ext2_bcache_commit(cache_t* cache, ext2_t* ext2, uint64_t block, void*);
void ext2_bcache_release(cache_t* cache, ext2_t* ext2, uint64_t block, void*);

void ext2_utils_read_inode(ext2_t*, ino_t, void*);
void ext2_utils_write_inode(ext2_t*, ino_t, const void*);
void ext2_utils_zero_block(ext2_t*, uint32_t);
int ext2_utils_read_inode(ext2_t*, ino_t, void*);
int ext2_utils_write_inode(ext2_t*, ino_t, const void*);
int ext2_utils_alloc_inode(ext2_t*, const struct ext2_inode*, ino_t*);
int ext2_utils_free_inode(ext2_t*, ino_t);

int ext2_utils_read_inode_data(ext2_t*, struct ext2_inode*, uint32_t, uint32_t, void*, size_t);
int ext2_utils_write_inode_data(ext2_t*, struct ext2_inode*, uint32_t, uint32_t, const void*, size_t);
int ext2_utils_free_inode_data(ext2_t*, struct ext2_inode*, uint32_t);

void ext2_utils_read_inode_data(ext2_t*, uint32_t*, uint32_t, uint32_t, void*, size_t);
void ext2_utils_write_inode_data(ext2_t*, uint32_t*, uint32_t, uint32_t, const void*, size_t);
void ext2_utils_alloc_inode_data(ext2_t*, uint32_t*, uint32_t);
int ext2_utils_read_block(ext2_t*, uint32_t, uint32_t, void*, size_t, bool);
int ext2_utils_write_block(ext2_t*, uint32_t, uint32_t, const void*, size_t);
int ext2_utils_zero_block(ext2_t*, uint32_t);

void ext2_utils_read_block(ext2_t*, uint32_t, uint32_t, void*, size_t, bool);
void ext2_utils_write_block(ext2_t*, uint32_t, uint32_t, const void*, size_t);
int ext2_utils_read_group_desc(ext2_t*, uint32_t, struct ext2_group_desc*);
int ext2_utils_write_group_desc(ext2_t*, uint32_t, const struct ext2_group_desc*);
int ext2_utils_write_super(ext2_t*);

void ext2_utils_alloc_block(ext2_t*, uint32_t*);
void ext2_utils_free_block(ext2_t*, uint32_t);
int ext2_utils_alloc_block(ext2_t*, uint32_t*);
int ext2_utils_free_block(ext2_t*, uint32_t);

mode_t ext2_utils_file_type(uint8_t);
uint8_t ext2_utils_dir_type(mode_t);
int ext2_utils_add_dir_entry(ext2_t*, struct ext2_inode*, ino_t, const char*, mode_t);
int ext2_utils_remove_dir_entry(ext2_t*, struct ext2_inode*, const char*, ino_t*, uint8_t*);

#endif
14 changes: 10 additions & 4 deletions kernel/fs/ext2/ext2_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <aplus/memory.h>
#include <aplus/smp.h>
#include <aplus/vfs.h>
#include <sys/mount.h>

#include "ext2.h"

Expand All @@ -38,17 +39,21 @@

struct ext2_inode* ext2_icache_fetch(cache_t* cache, ext2_t* ext2, ino_t ino) {

struct ext2_inode* i = (struct ext2_inode*)kcalloc(1, sizeof(struct ext2_inode), GFP_KERNEL);
struct ext2_inode* i = (struct ext2_inode*)kcalloc(1, ext2->inodesize, GFP_KERNEL);

if (unlikely(!i))
return NULL;

return ext2_utils_read_inode(ext2, ino, i), i;
if (ext2_utils_read_inode(ext2, ino, i) < 0)
return kfree(i), NULL;

return i;
}


void ext2_icache_commit(cache_t* cache, ext2_t* ext2, ino_t ino, struct ext2_inode* inode) {
// ext2_utils_write_inode(ext2, ino, inode);
if (!(ext2->root->sb->flags & MS_RDONLY))
ext2_utils_write_inode(ext2, ino, inode);
}

void ext2_icache_release(cache_t* cache, ext2_t* ext2, ino_t ino, struct ext2_inode* inode) {
Expand All @@ -73,7 +78,8 @@ void* ext2_bcache_fetch(cache_t* cache, ext2_t* ext2, uint64_t block) {


void ext2_bcache_commit(cache_t* cache, ext2_t* ext2, uint64_t block, void* buffer) {
// vfs_write(ext2->dev, buffer, block * ext2->blocksize, ext2->blocksize);
if (!(ext2->root->sb->flags & MS_RDONLY))
ext2_utils_write_block(ext2, block, 0, buffer, ext2->blocksize);
}

void ext2_bcache_release(cache_t* cache, ext2_t* ext2, uint64_t block, void* buffer) {
Expand Down
Loading