diff --git a/.gitignore b/.gitignore index f2b7afbd..6eb57a66 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ *.iml *.dat +AGENTS.md + # Generate by Makefile /kernel/aplus @@ -64,4 +66,4 @@ Cargo.lock **/*.rs.bk # MSVC Windows builds of rustc generate these, which store debugging information -*.pdb \ No newline at end of file +*.pdb diff --git a/apps/test/ext2-write-test/Makefile b/apps/test/ext2-write-test/Makefile new file mode 100644 index 00000000..556b0a7e --- /dev/null +++ b/apps/test/ext2-write-test/Makefile @@ -0,0 +1,3 @@ +INCLUDES += $(ROOTDIR)/include +include $(ROOTDIR)/build/cross.mk +include $(ROOTDIR)/build/build-binary.mk diff --git a/apps/test/ext2-write-test/main.c b/apps/test/ext2-write-test/main.c new file mode 100644 index 00000000..ee3e70fd --- /dev/null +++ b/apps/test/ext2-write-test/main.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#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; +} diff --git a/drivers/dev/block/main.c b/drivers/dev/block/main.c index 80d7280d..9479c9e1 100644 --- a/drivers/dev/block/main.c +++ b/drivers/dev/block/main.c @@ -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; diff --git a/kernel/fs/ext2/ext2.h b/kernel/fs/ext2/ext2.h index 18b2ea34..fa1e29ce 100644 --- a/kernel/fs/ext2/ext2.h +++ b/kernel/fs/ext2/ext2.h @@ -32,6 +32,7 @@ #define EXT2_SIGNATURE 0xEF53 +#define EXT2_NAME_LEN 255 // TODO: rewrite all ext2 driver to support cache @@ -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 @@ -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 @@ -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 */ @@ -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 + /* @@ -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); @@ -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 diff --git a/kernel/fs/ext2/ext2_cache.c b/kernel/fs/ext2/ext2_cache.c index abb89867..ba5b8dae 100644 --- a/kernel/fs/ext2/ext2_cache.c +++ b/kernel/fs/ext2/ext2_cache.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "ext2.h" @@ -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) { @@ -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) { diff --git a/kernel/fs/ext2/ext2_creat.c b/kernel/fs/ext2/ext2_creat.c new file mode 100644 index 00000000..fac1f28c --- /dev/null +++ b/kernel/fs/ext2/ext2_creat.c @@ -0,0 +1,82 @@ +/* + * Author: + * Antonino Natale + * + * Copyright (c) 2013-2019 Antonino Natale + * + * This file is part of aplus. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ext2.h" + + +inode_t* ext2_creat(inode_t* parent, const char* name, mode_t mode) { + DEBUG_ASSERT(parent); + DEBUG_ASSERT(parent->sb); + DEBUG_ASSERT(parent->sb->fsid == FSID_EXT2); + DEBUG_ASSERT(name); + + if (unlikely(parent->sb->flags & MS_RDONLY)) + return errno = EROFS, NULL; + + if (unlikely(!S_ISREG(mode))) + return errno = EOPNOTSUPP, NULL; + + size_t name_len = strlen(name); + + if (unlikely(name_len == 0 || name_len > EXT2_NAME_LEN)) + return errno = ENAMETOOLONG, NULL; + + ext2_t* ext2 = parent->sb->fsinfo; + struct ext2_inode* dir = cache_get(&parent->sb->cache, parent->ino); + struct ext2_inode* created = kcalloc(1, ext2->inodesize, GFP_KERNEL); + ino_t ino = 0; + inode_t* result = NULL; + + if (unlikely(!created)) + return errno = ENOMEM, NULL; + + mode_t umask = 0; + shared_ptr_access(current_task->fs, fs, { umask = fs->umask; }); + + created->i_mode = mode & ~umask; + created->i_uid = current_task->uid; + created->i_gid = current_task->gid; + created->i_links_count = 1; + created->i_atime = arch_timer_gettime(); + created->i_ctime = created->i_atime; + created->i_mtime = created->i_atime; + + scoped_lock(&ext2->lock) { + if (ext2_utils_alloc_inode(ext2, created, &ino) < 0) + break; + + if (ext2_utils_add_dir_entry(ext2, dir, ino, name, created->i_mode) < 0) { + ext2_utils_free_inode(ext2, ino); + ino = 0; + break; + } + + dir->i_mtime = arch_timer_gettime(); + dir->i_ctime = dir->i_mtime; + + if (ext2_utils_write_inode(ext2, parent->ino, dir) < 0) + break; + + result = ext2_utils_create_vfs_inode(parent, ino, name, created->i_mode); + } + + kfree(created); + return result; +} diff --git a/kernel/fs/ext2/ext2_finddir.c b/kernel/fs/ext2/ext2_finddir.c index 70880240..09338b6d 100644 --- a/kernel/fs/ext2/ext2_finddir.c +++ b/kernel/fs/ext2/ext2_finddir.c @@ -4,139 +4,109 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ #include +#include #include -#include #include -#include #include -#include #include #include "ext2.h" +inode_t* ext2_utils_create_vfs_inode(inode_t* parent, ino_t ino, const char* name, mode_t mode) { + inode_t* inode = (inode_t*)kcalloc(1, sizeof(inode_t), GFP_KERNEL); -inode_t* ext2_finddir(inode_t* inode, const char* name) { + if (unlikely(!inode)) + return errno = ENOMEM, NULL; - DEBUG_ASSERT(inode); - DEBUG_ASSERT(inode->sb); - DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); - DEBUG_ASSERT(name); + inode->ino = ino; + inode->sb = parent->sb; + inode->parent = parent; + strncpy(inode->name, name, sizeof(inode->name) - 1); + spinlock_init(&inode->lock); - ext2_t* ext2 = (ext2_t*)inode->sb->fsinfo; + inode->ops.getattr = ext2_getattr; + inode->ops.setattr = ext2_setattr; + inode->ops.fsync = ext2_fsync; + if (S_ISDIR(mode)) { + inode->ops.creat = ext2_creat; + inode->ops.finddir = ext2_finddir; + inode->ops.readdir = ext2_readdir; + inode->ops.unlink = ext2_unlink; + vfs_dcache_init(inode); + } else if (S_ISREG(mode)) { + inode->ops.truncate = ext2_truncate; + inode->ops.read = ext2_read; + inode->ops.write = ext2_write; + } else if (S_ISLNK(mode)) { + inode->ops.readlink = ext2_readlink; + } + return inode; +} - struct ext2_inode* n = cache_get(&inode->sb->cache, inode->ino); - struct inode* d = NULL; +inode_t* ext2_finddir(inode_t* inode, const char* name) { + DEBUG_ASSERT(inode); + DEBUG_ASSERT(inode->sb); + DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); + DEBUG_ASSERT(name); size_t name_len = strlen(name); + if (unlikely(name_len == 0 || name_len > EXT2_NAME_LEN)) + return errno = ENOENT, NULL; - for (size_t q = 0; q < n->i_size; q += ext2->blocksize) { - - - scoped_lock(&ext2->lock) { - - ext2_utils_read_inode_data(ext2, n->i_block, q / ext2->blocksize, 0, ext2->iocache, ext2->blocksize); - - for (size_t i = 0; i < ext2->blocksize;) { - - struct ext2_dir_entry_2* e = (struct ext2_dir_entry_2*)((uintptr_t)ext2->iocache + i); - - DEBUG_ASSERT(e->rec_len); - - + ext2_t* ext2 = inode->sb->fsinfo; + struct ext2_inode* node = cache_get(&inode->sb->cache, inode->ino); + inode_t* result = NULL; - mode_t mode; + errno = 0; - if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV) { + scoped_lock(&ext2->lock) { + for (uint32_t block = 0; (uint64_t)block * ext2->blocksize < node->i_size; block++) { + if (ext2_utils_read_inode_data(ext2, node, block, 0, ext2->iocache, ext2->blocksize) < 0) + break; - mode = ext2_utils_file_type(e->file_type); + for (uint32_t offset = 0; offset < ext2->blocksize;) { + struct ext2_dir_entry_2* entry = (struct ext2_dir_entry_2*)((uint8_t*)ext2->iocache + offset); - } else { - - mode = ((struct ext2_inode*)cache_get(&inode->sb->cache, e->inode))->i_mode; + if (unlikely(entry->rec_len < 8 || entry->rec_len > ext2->blocksize - offset || entry->name_len > entry->rec_len - 8)) { + errno = EIO; + break; } - /* Found? */ - if (name_len == e->name_len && strncmp(name, e->name, e->name_len) == 0) { - - - d = (inode_t*)kcalloc(1, sizeof(inode_t), GFP_KERNEL); - - d->ino = e->inode; - d->sb = inode->sb; - d->parent = inode; - - strncpy(d->name, e->name, e->name_len); - - - d->ops.getattr = ext2_getattr; - // d->ops.setattr = ext2_setattr; - // d->ops.fsync = ext2_fsync; - - if (S_ISDIR(mode)) { - - // d->ops.creat = ext2_creat; - d->ops.finddir = ext2_finddir; - d->ops.readdir = ext2_readdir; - // d->ops.rename = ext2_rename; - // d->ops.symlink = ext2_symlink; - // d->ops.unlink = ext2_unlink; - - vfs_dcache_init(d); - - } - - else if (S_ISREG(mode)) { - - // d->ops.truncate = ext2_truncate; - d->ops.read = ext2_read; - d->ops.write = ext2_write; - - } - - else if (S_ISLNK(mode)) { + if (entry->inode != 0 && entry->name_len == name_len && memcmp(name, entry->name, name_len) == 0) { + mode_t mode; - d->ops.readlink = ext2_readlink; + if (entry->file_type != EXT2_FT_UNKNOWN) { + mode = ext2_utils_file_type(entry->file_type); + } else { + struct ext2_inode* child = cache_get(&inode->sb->cache, entry->inode); + mode = child->i_mode; } - - spinlock_init(&d->lock); + result = ext2_utils_create_vfs_inode(inode, entry->inode, name, mode); break; } - - i += e->rec_len; + offset += entry->rec_len; } - } - - if (d != NULL) - break; + if (result) + break; + } } - return d; + if (!result) + errno = errno == EIO ? EIO : ENOENT; + + return result; } diff --git a/kernel/fs/ext2/ext2_fsync.c b/kernel/fs/ext2/ext2_fsync.c index 09935830..0c0e62d0 100644 --- a/kernel/fs/ext2/ext2_fsync.c +++ b/kernel/fs/ext2/ext2_fsync.c @@ -42,6 +42,5 @@ int ext2_fsync(inode_t* inode, int datasync) { DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); cache_commit_all(&inode->sb->cache); - - return 0; + return vfs_fsync(inode->sb->dev, datasync); } diff --git a/kernel/fs/ext2/ext2_getattr.c b/kernel/fs/ext2/ext2_getattr.c index e2e668f1..6f45ea71 100644 --- a/kernel/fs/ext2/ext2_getattr.c +++ b/kernel/fs/ext2/ext2_getattr.c @@ -52,23 +52,12 @@ int ext2_getattr(inode_t* inode, struct stat* st) { st->st_uid = n->i_uid; st->st_gid = n->i_gid; st->st_rdev = 0; /* FIXME */ - st->st_size = n->i_size; - st->st_blksize = 512; + st->st_size = ext2_inode_get_size(inode->sb->fsinfo, n); + st->st_blksize = inode->sb->st.f_bsize; st->st_blocks = n->i_blocks; st->st_atime = n->i_atime; - st->st_ctime = n->i_mtime; - st->st_mtime = n->i_ctime; - - - if (sizeof(off_t) == 8) { - - ext2_t* ext2 = (ext2_t*)inode->sb->fsinfo; - - if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV) { - - st->st_size |= (off_t)((uint64_t)n->i_size_high << 32); - } - } + st->st_ctime = n->i_ctime; + st->st_mtime = n->i_mtime; return 0; diff --git a/kernel/fs/ext2/ext2_mount.c b/kernel/fs/ext2/ext2_mount.c index 779c3925..2c723bf3 100644 --- a/kernel/fs/ext2/ext2_mount.c +++ b/kernel/fs/ext2/ext2_mount.c @@ -92,26 +92,27 @@ int ext2_mount(inode_t* dev, inode_t* dir, int flags, const char* args) { - DEBUG_ASSERT((1024 << sb.s_log_block_size) <= EXT2_MAX_BLOCK_SIZE); - DEBUG_ASSERT((1024 << sb.s_log_block_size) >= EXT2_MIN_BLOCK_SIZE); - DEBUG_ASSERT((1024 << sb.s_log_frag_size) <= EXT2_MAX_FRAG_SIZE); - DEBUG_ASSERT((1024 << sb.s_log_frag_size) >= EXT2_MIN_FRAG_SIZE); + uint32_t blocksize = 1024 << sb.s_log_block_size; + uint32_t fragsize = 1024 << sb.s_log_frag_size; - DEBUG_ASSERT(!(sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)); - DEBUG_ASSERT(!(sb.s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)); - DEBUG_ASSERT(!(sb.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)); + if (blocksize < EXT2_MIN_BLOCK_SIZE || blocksize > EXT2_MAX_BLOCK_SIZE || fragsize < EXT2_MIN_FRAG_SIZE || fragsize > EXT2_MAX_FRAG_SIZE) + return errno = EINVAL, -1; + + if (sb.s_rev_level != EXT2_DYNAMIC_REV || sb.s_inode_size < sizeof(struct ext2_inode) || sb.s_inode_size > blocksize || sb.s_blocks_per_group == 0 || sb.s_inodes_per_group == 0 || + (sb.s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_WRITE_SUPP)) + return errno = EINVAL, -1; - //! WARN - DEBUG_ASSERT(sb.s_rev_level == EXT2_DYNAMIC_REV); + if (!(flags & MS_RDONLY) && (sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_WRITE_SUPP)) + return errno = EROFS, -1; ext2_t* ext2 = (void*)kcalloc(1, sizeof(ext2_t), GFP_USER); - ext2->iocache = (void*)kmalloc((1024) << sb.s_log_block_size, GFP_KERNEL); + ext2->iocache = (void*)kmalloc(blocksize, GFP_KERNEL); ext2->first_block_group = sb.s_first_data_block + 1; - ext2->count_block_group = sb.s_blocks_count / sb.s_blocks_per_group; - ext2->blocksize = (1024) << sb.s_log_block_size; + ext2->count_block_group = (sb.s_blocks_count - sb.s_first_data_block + sb.s_blocks_per_group - 1) / sb.s_blocks_per_group; + ext2->blocksize = blocksize; ext2->inodesize = sb.s_inode_size; ext2->dev = dev; ext2->root = dir; @@ -143,16 +144,16 @@ int ext2_mount(inode_t* dev, inode_t* dir, int flags, const char* args) { dir->sb->st.f_favail = sb.s_free_inodes_count; dir->sb->st.f_flag = stflags; dir->sb->st.f_fsid = FSID_EXT2; - dir->sb->st.f_namemax = CONFIG_MAXNAMLEN; + dir->sb->st.f_namemax = EXT2_NAME_LEN; dir->sb->ops.getattr = ext2_getattr; - // dir->sb->ops.setattr = ext2_setattr; - // dir->sb->ops.creat = ext2_creat; + dir->sb->ops.setattr = ext2_setattr; + dir->sb->ops.creat = ext2_creat; dir->sb->ops.finddir = ext2_finddir; dir->sb->ops.readdir = ext2_readdir; // dir->sb->ops.rename = ext2_rename; // dir->sb->ops.symlink = ext2_symlink; - // dir->sb->ops.unlink = ext2_unlink; + dir->sb->ops.unlink = ext2_unlink; diff --git a/kernel/fs/ext2/ext2_read.c b/kernel/fs/ext2/ext2_read.c index 8d83bce3..049f9675 100644 --- a/kernel/fs/ext2/ext2_read.c +++ b/kernel/fs/ext2/ext2_read.c @@ -4,112 +4,55 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ #include #include -#include #include -#include -#include -#include #include #include "ext2.h" - ssize_t ext2_read(inode_t* inode, void* buf, off_t pos, size_t len) { - DEBUG_ASSERT(inode); DEBUG_ASSERT(inode->sb); DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); - DEBUG_ASSERT(buf); - DEBUG_ASSERT(len); - - - ext2_t* ext2 = (ext2_t*)inode->sb->fsinfo; - + if (unlikely(pos < 0)) + return errno = EINVAL, -1; - struct ext2_inode* n = cache_get(&inode->sb->cache, inode->ino); - - - - off_t size; - - if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV) { - - size = (off_t)(((uint64_t)n->i_size_high << 32) | n->i_size); - - } else { - - size = n->i_size; - } - + ext2_t* ext2 = inode->sb->fsinfo; + struct ext2_inode* node = cache_get(&inode->sb->cache, inode->ino); + uint64_t size = ext2_inode_get_size(ext2, node); - if (unlikely(pos + len > size)) - len = size - pos; - - if (unlikely(len == 0)) + if ((uint64_t)pos >= size) return 0; + if (len > size - pos) + len = size - pos; + size_t done = 0; - uint32_t* blocks = &n->i_block[0]; - - uint32_t ib = pos / ext2->blocksize; - uint32_t eb = (pos + len - 1) / ext2->blocksize; - uint32_t off = 0; - - - if (pos % ext2->blocksize) { - - long p; - p = ext2->blocksize - (pos % ext2->blocksize); - p = p > len ? len : p; - - - ext2_utils_read_inode_data(ext2, blocks, ib, pos % ext2->blocksize, buf, p); - - off += p; - ib++; - } - - - if ((pos + len) % ext2->blocksize && (ib <= eb)) { - - long p; - p = (pos + len) % ext2->blocksize; - - - ext2_utils_read_inode_data(ext2, blocks, eb, 0, (void*)((uintptr_t)buf + len - p), p); - - eb--; - } + scoped_lock(&ext2->lock) { + while (done < len) { + uint64_t offset = pos + done; + uint32_t block = offset / ext2->blocksize; + uint32_t inblock = offset % ext2->blocksize; + size_t chunk = ext2->blocksize - inblock; + if (chunk > len - done) + chunk = len - done; - for (off_t i = eb - ib + 1; i > 0; i--, ib++, off += ext2->blocksize) { + if (ext2_utils_read_inode_data(ext2, node, block, inblock, (uint8_t*)buf + done, chunk) < 0) + return done ? (ssize_t)done : -1; - ext2_utils_read_inode_data(ext2, blocks, ib, 0, (void*)((uintptr_t)buf + off), ext2->blocksize); + done += chunk; + } } - return len; + return done; } diff --git a/kernel/fs/ext2/ext2_readdir.c b/kernel/fs/ext2/ext2_readdir.c index 00740dd8..7f169777 100644 --- a/kernel/fs/ext2/ext2_readdir.c +++ b/kernel/fs/ext2/ext2_readdir.c @@ -4,108 +4,69 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ -#include #include +#include #include -#include #include -#include -#include -#include #include #include "ext2.h" - -ssize_t ext2_readdir(inode_t* inode, struct dirent* e, off_t pos, size_t count) { - +ssize_t ext2_readdir(inode_t* inode, struct dirent* entries, off_t pos, size_t count) { DEBUG_ASSERT(inode); DEBUG_ASSERT(inode->sb); DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); + DEBUG_ASSERT(entries); - DEBUG_ASSERT(e); - DEBUG_ASSERT(count); - - - ext2_t* ext2 = (ext2_t*)inode->sb->fsinfo; - - - - struct ext2_inode* n = cache_get(&inode->sb->cache, inode->ino); - - int entries = 0; - int q; + if (unlikely(pos < 0)) + return errno = EINVAL, -1; - for (q = 0; q < n->i_size; q += ext2->blocksize) { + if (count == 0) + return 0; - scoped_lock(&ext2->lock) { + ext2_t* ext2 = inode->sb->fsinfo; + struct ext2_inode* node = cache_get(&inode->sb->cache, inode->ino); + size_t seen = 0; + size_t emitted = 0; - ext2_utils_read_inode_data(ext2, n->i_block, q / ext2->blocksize, 0, ext2->iocache, ext2->blocksize); + scoped_lock(&ext2->lock) { + for (uint32_t block = 0; (uint64_t)block * ext2->blocksize < node->i_size && emitted < count; block++) { + if (ext2_utils_read_inode_data(ext2, node, block, 0, ext2->iocache, ext2->blocksize) < 0) + return emitted ? (ssize_t)emitted : -1; - for (size_t i = 0; i < ext2->blocksize;) { + for (uint32_t offset = 0; offset < ext2->blocksize && emitted < count;) { + struct ext2_dir_entry_2* entry = (struct ext2_dir_entry_2*)((uint8_t*)ext2->iocache + offset); - struct ext2_dir_entry_2* d = (struct ext2_dir_entry_2*)((uintptr_t)ext2->iocache + i); + if (unlikely(entry->rec_len < 8 || entry->rec_len > ext2->blocksize - offset || entry->name_len > entry->rec_len - 8)) + return errno = EIO, emitted ? (ssize_t)emitted : -1; - DEBUG_ASSERT(d->rec_len); - DEBUG_ASSERT(d->name_len); + if (entry->inode != 0) { + if (seen++ >= (size_t)pos) { + struct dirent* out = &entries[emitted++]; + memset(out, 0, sizeof(*out)); + out->d_ino = entry->inode; + out->d_off = pos + emitted; + out->d_reclen = sizeof(*out); + out->d_type = MODE_2_DIRENT_TYPE(ext2_utils_file_type(entry->file_type)); - if (pos > 0) { + size_t name_len = entry->name_len; + if (name_len >= sizeof(out->d_name)) + name_len = sizeof(out->d_name) - 1; - pos--; - - } else { - - if (count == 0) - break; - - - e->d_ino = d->inode; - e->d_off = entries; - e->d_reclen = sizeof(struct dirent); - e->d_type = 0; - - strncpy(e->d_name, d->name, d->name_len); - - - if (ext2->sb.s_rev_level == EXT2_DYNAMIC_REV) { - e->d_type = d->file_type; + memcpy(out->d_name, entry->name, name_len); } - - - e++; - entries++; - - count--; } - i += d->rec_len; + offset += entry->rec_len; } } - - - if (count == 0) - break; } - return entries; + return emitted; } diff --git a/kernel/fs/ext2/ext2_readlink.c b/kernel/fs/ext2/ext2_readlink.c index f6c09f7a..0e7c9cce 100644 --- a/kernel/fs/ext2/ext2_readlink.c +++ b/kernel/fs/ext2/ext2_readlink.c @@ -66,7 +66,10 @@ ssize_t ext2_readlink(inode_t* inode, char* buf, size_t len) { } else { - ext2_utils_read_inode_data(ext2, n->i_block, 0, 0, buf, len); + scoped_lock(&ext2->lock) { + if (ext2_utils_read_inode_data(ext2, n, 0, 0, buf, len) < 0) + return -1; + } } return len; diff --git a/kernel/fs/ext2/ext2_setattr.c b/kernel/fs/ext2/ext2_setattr.c index 71c2e9f9..8c0304de 100644 --- a/kernel/fs/ext2/ext2_setattr.c +++ b/kernel/fs/ext2/ext2_setattr.c @@ -43,6 +43,9 @@ int ext2_setattr(inode_t* inode, struct stat* st) { DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); DEBUG_ASSERT(st); + if (unlikely(inode->sb->flags & MS_RDONLY)) + return errno = EROFS, -1; + struct ext2_inode* n = cache_get(&inode->sb->cache, inode->ino); n->i_mode = st->st_mode; @@ -52,9 +55,7 @@ int ext2_setattr(inode_t* inode, struct stat* st) { n->i_ctime = st->st_ctime; n->i_mtime = st->st_mtime; - if (inode->sb->flags & MS_SYNCHRONOUS) { - cache_commit(&inode->sb->cache, inode->ino); - } + ext2_icache_commit(&inode->sb->cache, inode->sb->fsinfo, inode->ino, n); return 0; } diff --git a/kernel/fs/ext2/ext2_truncate.c b/kernel/fs/ext2/ext2_truncate.c new file mode 100644 index 00000000..f0e9e275 --- /dev/null +++ b/kernel/fs/ext2/ext2_truncate.c @@ -0,0 +1,96 @@ +/* + * Author: + * Antonino Natale + * + * Copyright (c) 2013-2019 Antonino Natale + * + * This file is part of aplus. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "ext2.h" + + +int ext2_truncate(inode_t* inode, off_t len) { + DEBUG_ASSERT(inode); + DEBUG_ASSERT(inode->sb); + DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); + + if (unlikely(inode->sb->flags & MS_RDONLY)) + return errno = EROFS, -1; + + if (unlikely(len < 0)) + return errno = EINVAL, -1; + + ext2_t* ext2 = inode->sb->fsinfo; + struct ext2_inode* node = cache_get(&inode->sb->cache, inode->ino); + uint64_t old_size = ext2_inode_get_size(ext2, node); + uint64_t new_size = len; + int result = 0; + + if (new_size == old_size) + return 0; + + scoped_lock(&ext2->lock) { + if (new_size > old_size) { + uint8_t zero[EXT2_MAX_BLOCK_SIZE] = {0}; + uint64_t offset = old_size; + + while (offset < new_size) { + uint32_t block = offset / ext2->blocksize; + uint32_t inblock = offset % ext2->blocksize; + size_t chunk = ext2->blocksize - inblock; + + if (chunk > new_size - offset) + chunk = new_size - offset; + + if (ext2_utils_write_inode_data(ext2, node, block, inblock, zero, chunk) < 0) { + result = -1; + break; + } + + offset += chunk; + } + + if (result == 0) + ext2_inode_set_size(ext2, node, new_size); + + } else { + uint32_t old_blocks = (old_size + ext2->blocksize - 1) / ext2->blocksize; + uint32_t new_blocks = (new_size + ext2->blocksize - 1) / ext2->blocksize; + + if (new_size % ext2->blocksize) { + uint8_t zero[EXT2_MAX_BLOCK_SIZE] = {0}; + uint32_t inblock = new_size % ext2->blocksize; + + if (ext2_utils_write_inode_data(ext2, node, new_size / ext2->blocksize, inblock, zero, ext2->blocksize - inblock) < 0) + result = -1; + } + + for (uint32_t block = old_blocks; block > new_blocks; block--) { + if (ext2_utils_free_inode_data(ext2, node, block - 1) < 0) { + result = -1; + break; + } + } + + ext2_inode_set_size(ext2, node, new_size); + } + + node->i_mtime = arch_timer_gettime(); + node->i_ctime = node->i_mtime; + + if (ext2_utils_write_inode(ext2, inode->ino, node) < 0) + result = -1; + } + + return result; +} diff --git a/kernel/fs/ext2/ext2_umount.c b/kernel/fs/ext2/ext2_umount.c index 97f8f321..9fdcdfc7 100644 --- a/kernel/fs/ext2/ext2_umount.c +++ b/kernel/fs/ext2/ext2_umount.c @@ -47,6 +47,7 @@ int ext2_umount(inode_t* dir) { cache_destroy(&dir->sb->cache); + cache_destroy(&ext2->bcache); kfree(ext2->iocache); kfree(ext2); diff --git a/kernel/fs/ext2/ext2_unlink.c b/kernel/fs/ext2/ext2_unlink.c new file mode 100644 index 00000000..a723aaa8 --- /dev/null +++ b/kernel/fs/ext2/ext2_unlink.c @@ -0,0 +1,98 @@ +/* + * Author: + * Antonino Natale + * + * Copyright (c) 2013-2019 Antonino Natale + * + * This file is part of aplus. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "ext2.h" + + +int ext2_unlink(inode_t* parent, const char* name) { + DEBUG_ASSERT(parent); + DEBUG_ASSERT(parent->sb); + DEBUG_ASSERT(parent->sb->fsid == FSID_EXT2); + DEBUG_ASSERT(name); + + if (unlikely(parent->sb->flags & MS_RDONLY)) + return errno = EROFS, -1; + + inode_t* target = ext2_finddir(parent, name); + + if (!target) + return -1; + + struct ext2_inode* node = cache_get(&parent->sb->cache, target->ino); + + if (!S_ISREG(node->i_mode)) + return kfree(target), errno = EISDIR, -1; + + ext2_t* ext2 = parent->sb->fsinfo; + struct ext2_inode* dir = cache_get(&parent->sb->cache, parent->ino); + ino_t ino = 0; + uint8_t type = EXT2_FT_UNKNOWN; + int result = -1; + + scoped_lock(&ext2->lock) { + if (ext2_utils_remove_dir_entry(ext2, dir, name, &ino, &type) < 0) + break; + + if (unlikely(ino != target->ino)) { + errno = EIO; + break; + } + + if (node->i_links_count > 1) { + node->i_links_count--; + node->i_ctime = arch_timer_gettime(); + + if (ext2_utils_write_inode(ext2, ino, node) < 0) + break; + + } else { + uint64_t size = ext2_inode_get_size(ext2, node); + uint32_t blocks = (size + ext2->blocksize - 1) / ext2->blocksize; + bool failed = false; + + for (uint32_t block = blocks; block > 0; block--) { + if (ext2_utils_free_inode_data(ext2, node, block - 1) < 0) { + failed = true; + break; + } + } + + if (failed) + break; + + memset(node, 0, ext2->inodesize); + + if (ext2_utils_write_inode(ext2, ino, node) < 0 || ext2_utils_free_inode(ext2, ino) < 0) + break; + } + + dir->i_mtime = arch_timer_gettime(); + dir->i_ctime = dir->i_mtime; + + if (ext2_utils_write_inode(ext2, parent->ino, dir) < 0) + break; + + if (node->i_links_count == 0) + cache_remove(&parent->sb->cache, ino); + + result = 0; + } + + kfree(target); + return result; +} diff --git a/kernel/fs/ext2/ext2_write.c b/kernel/fs/ext2/ext2_write.c index f7adbb9b..36b4dd1c 100644 --- a/kernel/fs/ext2/ext2_write.c +++ b/kernel/fs/ext2/ext2_write.c @@ -4,113 +4,98 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ #include +#include +#include #include -#include #include -#include -#include -#include +#include #include -#include #include "ext2.h" +static int ext2_zero_range(ext2_t* ext2, struct ext2_inode* inode, uint64_t start, uint64_t end) { + uint8_t zero[EXT2_MAX_BLOCK_SIZE] = {0}; -ssize_t ext2_write(inode_t* inode, const void* buf, off_t pos, size_t len) { - - DEBUG_ASSERT(inode); - DEBUG_ASSERT(inode->sb); - DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); - - DEBUG_ASSERT(buf); - DEBUG_ASSERT(len); - - - ext2_t* ext2 = (ext2_t*)inode->sb->fsinfo; - - - - struct ext2_inode* n = cache_get(&inode->sb->cache, inode->ino); - - - uint32_t* blocks = &n->i_block[0]; - uint32_t ib = pos / ext2->blocksize; - uint32_t eb = (pos + len - 1) / ext2->blocksize; - uint32_t off = 0; + while (start < end) { + uint32_t block = start / ext2->blocksize; + uint32_t inblock = start % ext2->blocksize; + size_t chunk = ext2->blocksize - inblock; + if (chunk > end - start) + chunk = end - start; + if (ext2_utils_write_inode_data(ext2, inode, block, inblock, zero, chunk) < 0) + return -1; - if (n->i_blocks < (pos + len) / ext2->blocksize + 1) { - - while (n->i_blocks < (pos + len) / ext2->blocksize + 1) { - - if (unlikely(inode->sb->st.f_bavail == 0)) - return errno = ENOSPC, -1; - - - ext2_utils_alloc_inode_data(ext2, blocks, n->i_blocks++); - - inode->sb->st.f_bavail--; - inode->sb->st.f_bfree--; - } - - ext2_inode_set_size(ext2, n, pos + len); - ext2_icache_commit(&inode->sb->cache, ext2, inode->ino, n); + start += chunk; } + return 0; +} - if (pos % ext2->blocksize) { - - long p; - p = ext2->blocksize - (pos % ext2->blocksize); - p = p > len ? len : p; +ssize_t ext2_write(inode_t* inode, const void* buf, off_t pos, size_t len) { + DEBUG_ASSERT(inode); + DEBUG_ASSERT(inode->sb); + DEBUG_ASSERT(inode->sb->fsid == FSID_EXT2); + DEBUG_ASSERT(buf); + if (unlikely(inode->sb->flags & MS_RDONLY)) + return errno = EROFS, -1; - ext2_utils_write_inode_data(ext2, blocks, ib, pos % ext2->blocksize, buf, p); + if (unlikely(pos < 0 || (uint64_t)pos > UINT64_MAX - len)) + return errno = EINVAL, -1; - off += p; - ib++; - } + ext2_t* ext2 = inode->sb->fsinfo; + struct ext2_inode* node = cache_get(&inode->sb->cache, inode->ino); + uint64_t old_size = ext2_inode_get_size(ext2, node); + size_t done = 0; + int result = 0; + scoped_lock(&ext2->lock) { + if ((uint64_t)pos > old_size && ext2_zero_range(ext2, node, old_size, pos) < 0) { + result = -1; + } - if ((pos + len) % ext2->blocksize && (ib <= eb)) { + while (result == 0 && done < len) { + uint64_t offset = pos + done; + uint32_t block = offset / ext2->blocksize; + uint32_t inblock = offset % ext2->blocksize; + size_t chunk = ext2->blocksize - inblock; - long p; - p = (pos + len) % ext2->blocksize; + if (chunk > len - done) + chunk = len - done; + if (ext2_utils_write_inode_data(ext2, node, block, inblock, (const uint8_t*)buf + done, chunk) < 0) { + result = -1; + break; + } - ext2_utils_write_inode_data(ext2, blocks, eb, 0, (void*)((uintptr_t)buf + len - p), p); + done += chunk; + } - eb--; - } + if (done > 0) { + uint64_t new_size = pos + done; + if (new_size > old_size) + ext2_inode_set_size(ext2, node, new_size); - for (off_t i = eb - ib + 1; i > 0; i--, ib++, off += ext2->blocksize) { + node->i_mtime = arch_timer_gettime(); + node->i_ctime = node->i_mtime; + } - ext2_utils_write_inode_data(ext2, blocks, ib, 0, (void*)((uintptr_t)buf + off), ext2->blocksize); + if (((uint64_t)pos > old_size || done > 0) && ext2_utils_write_inode(ext2, inode->ino, node) < 0) + result = -1; } + if (done > 0) + return done; - return len; + return result; } diff --git a/kernel/fs/ext2/utils/ext2_block.c b/kernel/fs/ext2/utils/ext2_block.c index 9d8d4195..a3f6af43 100644 --- a/kernel/fs/ext2/utils/ext2_block.c +++ b/kernel/fs/ext2/utils/ext2_block.c @@ -4,190 +4,205 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ #include #include -#include -#include -#include #include -#include #include -#include -#include -#include +#include #include #include "../ext2.h" +static inline bool ext2_bitmap_test(const uint8_t* bitmap, uint32_t bit) { + return bitmap[bit >> 3] & (1U << (bit & 7)); +} + +static inline void ext2_bitmap_set(uint8_t* bitmap, uint32_t bit) { + bitmap[bit >> 3] |= 1U << (bit & 7); +} + +static inline void ext2_bitmap_clear(uint8_t* bitmap, uint32_t bit) { + bitmap[bit >> 3] &= ~(1U << (bit & 7)); +} -void ext2_utils_read_block(ext2_t* ext2, uint32_t block, uint32_t offset, void* data, size_t size, bool cached) { +int ext2_utils_read_block(ext2_t* ext2, uint32_t block, uint32_t offset, void* data, size_t size, bool cached) { DEBUG_ASSERT(ext2); - DEBUG_ASSERT(block); DEBUG_ASSERT(data); - DEBUG_ASSERT(size); - - if (unlikely(block > ext2->sb.s_blocks_count)) - kpanicf("%s() FAIL! block(%d) > s_blocks_count(%d)", __func__, block, ext2->sb.s_blocks_count); + (void)cached; - if (unlikely(block < ext2->first_block_group - 1)) - kpanicf("%s() FAIL! block(%d) < first_block_group(%d) - 1", __func__, block, ext2->first_block_group); + if (unlikely(block == 0 || block >= ext2->sb.s_blocks_count || offset > ext2->blocksize || size > ext2->blocksize - offset)) + return errno = EIO, -1; + if (unlikely(vfs_read(ext2->dev, data, ((off_t)block * ext2->blocksize) + offset, size) != (ssize_t)size)) + return errno = EIO, -1; - if (cached) { + return 0; +} - DEBUG_ASSERT(offset + size <= ext2->blocksize); +int ext2_utils_write_block(ext2_t* ext2, uint32_t block, uint32_t offset, const void* data, size_t size) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(data); - void* buffer = cache_get(&ext2->bcache, block); + if (unlikely(block == 0 || block >= ext2->sb.s_blocks_count || offset > ext2->blocksize || size > ext2->blocksize - offset)) + return errno = EIO, -1; - if (unlikely(!buffer)) - kpanicf("%s() FAIL! cache_get() errno(%s) on reading %zd bytes at %d", __func__, strerror(errno), size, ((block)*ext2->blocksize) + offset); + if (unlikely(vfs_write(ext2->dev, data, ((off_t)block * ext2->blocksize) + offset, size) != (ssize_t)size)) + return errno = EIO, -1; - memcpy(data, buffer + offset, size); + return 0; +} - } else { +int ext2_utils_zero_block(ext2_t* ext2, uint32_t block) { + DEBUG_ASSERT(ext2); - if (unlikely(vfs_read(ext2->dev, data, ((block)*ext2->blocksize) + offset, size) != size)) - kpanicf("%s() FAIL! vfs_read() errno(%s) on reading %zd bytes at %d", __func__, strerror(errno), size, ((block)*ext2->blocksize) + offset); - } + uint8_t zero[EXT2_MAX_BLOCK_SIZE] = {0}; + return ext2_utils_write_block(ext2, block, 0, zero, ext2->blocksize); } - -void ext2_utils_write_block(ext2_t* ext2, uint32_t block, uint32_t offset, const void* data, size_t size) { - +int ext2_utils_read_group_desc(ext2_t* ext2, uint32_t group, struct ext2_group_desc* desc) { DEBUG_ASSERT(ext2); - DEBUG_ASSERT(block); - DEBUG_ASSERT(data); - DEBUG_ASSERT(size); + DEBUG_ASSERT(desc); + if (unlikely(group >= ext2->count_block_group)) + return errno = EINVAL, -1; - if (unlikely(block > ext2->sb.s_blocks_count)) - kpanicf("%s() FAIL! block(%d) > s_blocks_count(%d)", __func__, block, ext2->sb.s_blocks_count); + uint32_t byte = group * sizeof(*desc); + uint32_t block = ext2->first_block_group + (byte / ext2->blocksize); + uint32_t off = byte % ext2->blocksize; - if (unlikely(block < ext2->first_block_group - 1)) - kpanicf("%s() FAIL! block(%d) < first_block_group(%d) - 1", __func__, block, ext2->first_block_group); - - if (unlikely(vfs_write(ext2->dev, data, ((block)*ext2->blocksize) + offset, size) != size)) - kpanicf("%s() FAIL! vfs_read() errno(%s) on writing %zd bytes at %d", __func__, strerror(errno), size, ((block)*ext2->blocksize) + offset); + return ext2_utils_read_block(ext2, block, off, desc, sizeof(*desc), false); } -void ext2_utils_zero_block(ext2_t* ext2, uint32_t block) { - - uint8_t zero[ext2->blocksize]; - memset(zero, 0, sizeof(zero)); +int ext2_utils_write_group_desc(ext2_t* ext2, uint32_t group, const struct ext2_group_desc* desc) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(desc); - ext2_utils_write_block(ext2, block, 0, zero, sizeof(zero)); -} + if (unlikely(group >= ext2->count_block_group)) + return errno = EINVAL, -1; + uint32_t byte = group * sizeof(*desc); + uint32_t block = ext2->first_block_group + (byte / ext2->blocksize); + uint32_t off = byte % ext2->blocksize; + return ext2_utils_write_block(ext2, block, off, desc, sizeof(*desc)); +} -void ext2_utils_alloc_block(ext2_t* ext2, uint32_t* block) { +int ext2_utils_write_super(ext2_t* ext2) { DEBUG_ASSERT(ext2); - DEBUG_ASSERT(block); - - - *block = 0; + ext2->sb.s_wtime = arch_timer_gettime(); - if (ext2->sb.s_free_blocks_count == 0) - return; + if (unlikely(vfs_write(ext2->dev, &ext2->sb, 1024, sizeof(ext2->sb)) != (ssize_t)sizeof(ext2->sb))) + return errno = EIO, -1; + return 0; +} - scoped_lock(&ext2->lock) { - size_t i; - size_t j; +int ext2_utils_alloc_block(ext2_t* ext2, uint32_t* block) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(block); - for (i = 0; i < (ext2->sb.s_blocks_count / ext2->sb.s_blocks_per_group); i++) { + *block = 0; - struct ext2_group_desc d; - ext2_utils_read_block(ext2, ext2->first_block_group, i * sizeof(d), &d, sizeof(d), false); + if (unlikely(ext2->sb.s_free_blocks_count == 0)) + return errno = ENOSPC, -1; - if (d.bg_free_blocks_count == 0) - continue; + for (uint32_t group = 0; group < ext2->count_block_group; group++) { + struct ext2_group_desc desc; + if (ext2_utils_read_group_desc(ext2, group, &desc) < 0) + return -1; - ext2_utils_read_block(ext2, d.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize, false); + if (desc.bg_free_blocks_count == 0) + continue; + if (ext2_utils_read_block(ext2, desc.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize, false) < 0) + return -1; + uint64_t group_start = ext2->sb.s_first_data_block + ((uint64_t)group * ext2->sb.s_blocks_per_group); + uint32_t bits = ext2->sb.s_blocks_per_group; - uint32_t* bitmap = ext2->iocache; + if (group_start + bits > ext2->sb.s_blocks_count) + bits = ext2->sb.s_blocks_count - group_start; - for (j = 0; j < (ext2->blocksize / sizeof(*bitmap)); j++, bitmap++) { + for (uint32_t bit = 0; bit < bits; bit++) { + uint32_t candidate = group_start + bit; - if (*bitmap == 0xFFFFFFFF) - continue; + if (ext2_bitmap_test(ext2->iocache, bit)) + continue; + if (ext2_utils_zero_block(ext2, candidate) < 0) + return -1; - uint32_t b, q; - b = q = __builtin_ffs(~(*bitmap)) - 1; - b += j * (sizeof(*bitmap) << 3); - b += i * ext2->sb.s_blocks_per_group; + ext2_bitmap_set(ext2->iocache, bit); - *bitmap |= (1LL << q); - *block = b; + if (ext2_utils_write_block(ext2, desc.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; - break; - } + desc.bg_free_blocks_count--; + ext2->sb.s_free_blocks_count--; + if (ext2_utils_write_group_desc(ext2, group, &desc) < 0 || ext2_utils_write_super(ext2) < 0) + return -1; - DEBUG_ASSERT(block); - DEBUG_ASSERT(*block); + ext2->root->sb->st.f_bfree--; + ext2->root->sb->st.f_bavail--; - ext2->sb.s_free_blocks_count--; + *block = candidate; + return 0; + } + } - d.bg_free_blocks_count--; + return errno = ENOSPC, -1; +} - ext2_utils_zero_block(ext2, *block); +int ext2_utils_free_block(ext2_t* ext2, uint32_t block) { + DEBUG_ASSERT(ext2); - ext2_utils_write_block(ext2, d.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize); - ext2_utils_write_block(ext2, ext2->first_block_group, i * sizeof(d), &d, sizeof(d)); + if (unlikely(block < ext2->sb.s_first_data_block || block >= ext2->sb.s_blocks_count)) + return errno = EINVAL, -1; + uint32_t relative = block - ext2->sb.s_first_data_block; + uint32_t group = relative / ext2->sb.s_blocks_per_group; + uint32_t bit = relative % ext2->sb.s_blocks_per_group; + struct ext2_group_desc desc; -#if DEBUG_LEVEL_TRACE - kprintf("ext2: alloc block %d (group %ld, bitmap %d, offset %ld)\n", *block, i, d.bg_block_bitmap, j); -#endif + if (ext2_utils_read_group_desc(ext2, group, &desc) < 0) + return -1; - break; - } - } + if (ext2_utils_read_block(ext2, desc.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize, false) < 0) + return -1; + if (unlikely(!ext2_bitmap_test(ext2->iocache, bit))) + return errno = EINVAL, -1; - DEBUG_ASSERT(*block != 0); -} + ext2_bitmap_clear(ext2->iocache, bit); + if (ext2_utils_write_block(ext2, desc.bg_block_bitmap, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; -void ext2_utils_free_block(ext2_t* ext2, uint32_t block) { + desc.bg_free_blocks_count++; + ext2->sb.s_free_blocks_count++; - DEBUG_ASSERT(ext2); - DEBUG_ASSERT(block > 1); + if (ext2_utils_write_group_desc(ext2, group, &desc) < 0 || ext2_utils_write_super(ext2) < 0) + return -1; + ext2->root->sb->st.f_bfree++; + ext2->root->sb->st.f_bavail++; - /* TODO: free block */ + return 0; } diff --git a/kernel/fs/ext2/utils/ext2_dir.c b/kernel/fs/ext2/utils/ext2_dir.c index c0ee7143..da0386e4 100644 --- a/kernel/fs/ext2/utils/ext2_dir.c +++ b/kernel/fs/ext2/utils/ext2_dir.c @@ -4,69 +4,179 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ #include #include -#include -#include -#include #include -#include #include -#include -#include -#include #include #include "../ext2.h" mode_t ext2_utils_file_type(uint8_t type) { - switch (type) { - case EXT2_FT_REG_FILE: return S_IFREG; - case EXT2_FT_DIR: return S_IFDIR; - case EXT2_FT_CHRDEV: return S_IFCHR; - case EXT2_FT_BLKDEV: return S_IFBLK; - case EXT2_FT_FIFO: return S_IFIFO; - case EXT2_FT_SOCK: return S_IFSOCK; - case EXT2_FT_SYMLINK: return S_IFLNK; - + case EXT2_FT_UNKNOWN: + return 0; default: - break; + return 0; + } +} + + +uint8_t ext2_utils_dir_type(mode_t mode) { + if (S_ISREG(mode)) + return EXT2_FT_REG_FILE; + if (S_ISDIR(mode)) + return EXT2_FT_DIR; + if (S_ISCHR(mode)) + return EXT2_FT_CHRDEV; + if (S_ISBLK(mode)) + return EXT2_FT_BLKDEV; + if (S_ISFIFO(mode)) + return EXT2_FT_FIFO; + if (S_ISSOCK(mode)) + return EXT2_FT_SOCK; + if (S_ISLNK(mode)) + return EXT2_FT_SYMLINK; + + return EXT2_FT_UNKNOWN; +} + + +static void ext2_fill_dir_entry(struct ext2_dir_entry_2* entry, uint16_t rec_len, ino_t ino, const char* name, mode_t mode) { + size_t name_len = strlen(name); + + memset(entry, 0, rec_len); + entry->inode = ino; + entry->rec_len = rec_len; + entry->name_len = name_len; + entry->file_type = ext2_utils_dir_type(mode); + memcpy(entry->name, name, name_len); +} + + +int ext2_utils_add_dir_entry(ext2_t* ext2, struct ext2_inode* dir, ino_t ino, const char* name, mode_t mode) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(dir); + DEBUG_ASSERT(name); + + size_t name_len = strlen(name); + + if (unlikely(name_len == 0 || name_len > EXT2_NAME_LEN)) + return errno = ENAMETOOLONG, -1; + + if (unlikely(dir->i_flags & EXT2_INDEX_FL)) + return errno = EOPNOTSUPP, -1; + + uint16_t needed = EXT2_DIR_REC_LEN(name_len); + uint32_t blocks = (dir->i_size + ext2->blocksize - 1) / ext2->blocksize; + + for (uint32_t block = 0; block < blocks; block++) { + if (ext2_utils_read_inode_data(ext2, dir, block, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; + + for (uint32_t offset = 0; offset < ext2->blocksize;) { + struct ext2_dir_entry_2* entry = (struct ext2_dir_entry_2*)((uint8_t*)ext2->iocache + offset); + + if (unlikely(entry->rec_len < 8 || entry->rec_len > ext2->blocksize - offset || entry->name_len > entry->rec_len - 8)) + return errno = EIO, -1; + + if (entry->inode == 0 && entry->rec_len >= needed) { + ext2_fill_dir_entry(entry, entry->rec_len, ino, name, mode); + return ext2_utils_write_inode_data(ext2, dir, block, 0, ext2->iocache, ext2->blocksize); + } + + uint16_t actual = EXT2_DIR_REC_LEN(entry->name_len); + + if (entry->inode != 0 && entry->rec_len >= actual + needed) { + uint16_t available = entry->rec_len - actual; + entry->rec_len = actual; + + struct ext2_dir_entry_2* added = (struct ext2_dir_entry_2*)((uint8_t*)entry + actual); + ext2_fill_dir_entry(added, available, ino, name, mode); + + return ext2_utils_write_inode_data(ext2, dir, block, 0, ext2->iocache, ext2->blocksize); + } + + offset += entry->rec_len; + } + } + + uint8_t buffer[EXT2_MAX_BLOCK_SIZE] = {0}; + ext2_fill_dir_entry((struct ext2_dir_entry_2*)buffer, ext2->blocksize, ino, name, mode); + + if (ext2_utils_write_inode_data(ext2, dir, blocks, 0, buffer, ext2->blocksize) < 0) + return -1; + + dir->i_size += ext2->blocksize; + return 0; +} + + +int ext2_utils_remove_dir_entry(ext2_t* ext2, struct ext2_inode* dir, const char* name, ino_t* ino, uint8_t* type) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(dir); + DEBUG_ASSERT(name); + DEBUG_ASSERT(ino); + DEBUG_ASSERT(type); + + size_t name_len = strlen(name); + + if (unlikely(dir->i_flags & EXT2_INDEX_FL)) + return errno = EOPNOTSUPP, -1; + + uint32_t blocks = (dir->i_size + ext2->blocksize - 1) / ext2->blocksize; + + for (uint32_t block = 0; block < blocks; block++) { + if (ext2_utils_read_inode_data(ext2, dir, block, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; + + struct ext2_dir_entry_2* previous = NULL; + + for (uint32_t offset = 0; offset < ext2->blocksize;) { + struct ext2_dir_entry_2* entry = (struct ext2_dir_entry_2*)((uint8_t*)ext2->iocache + offset); + + if (unlikely(entry->rec_len < 8 || entry->rec_len > ext2->blocksize - offset || entry->name_len > entry->rec_len - 8)) + return errno = EIO, -1; + + if (entry->inode != 0 && entry->name_len == name_len && memcmp(entry->name, name, name_len) == 0) { + *ino = entry->inode; + *type = entry->file_type; + + if (previous) { + previous->rec_len += entry->rec_len; + } else { + entry->inode = 0; + entry->name_len = 0; + entry->file_type = EXT2_FT_UNKNOWN; + } + + return ext2_utils_write_inode_data(ext2, dir, block, 0, ext2->iocache, ext2->blocksize); + } + + previous = entry; + + offset += entry->rec_len; + } } - kpanicf("ext2: PANIC! invalid file_type: %d\n", type); - return -1; + return errno = ENOENT, -1; } diff --git a/kernel/fs/ext2/utils/ext2_inode.c b/kernel/fs/ext2/utils/ext2_inode.c index 159746bd..890278e5 100644 --- a/kernel/fs/ext2/utils/ext2_inode.c +++ b/kernel/fs/ext2/utils/ext2_inode.c @@ -4,317 +4,405 @@ * * Copyright (c) 2013-2019 Antonino Natale * - * * This file is part of aplus. - * - * aplus is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * aplus is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with aplus. If not, see . */ +#include +#include + #include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include - #include "../ext2.h" +static inline bool ext2_bitmap_test(const uint8_t* bitmap, uint32_t bit) { + return bitmap[bit >> 3] & (1U << (bit & 7)); +} -void ext2_utils_read_inode(ext2_t* ext2, ino_t ino, void* data) { +static inline void ext2_bitmap_set(uint8_t* bitmap, uint32_t bit) { + bitmap[bit >> 3] |= 1U << (bit & 7); +} - DEBUG_ASSERT(ext2); - DEBUG_ASSERT(data); - DEBUG_ASSERT(ino != EXT2_BAD_INO); - DEBUG_ASSERT(ino != 0); +static inline void ext2_bitmap_clear(uint8_t* bitmap, uint32_t bit) { + bitmap[bit >> 3] &= ~(1U << (bit & 7)); +} + +static inline uint32_t ext2_sectors_per_block(ext2_t* ext2) { + return ext2->blocksize / 512; +} - if (ino > ext2->sb.s_inodes_count) - kpanicf("%s() FAIL! ino(%ld) > s_inodes_count(%d)", __func__, ino, ext2->sb.s_inodes_count); +static int ext2_inode_location(ext2_t* ext2, ino_t ino, uint32_t* block, uint32_t* offset) { + if (unlikely(ino == 0 || ino > ext2->sb.s_inodes_count)) + return errno = EINVAL, -1; - struct ext2_group_desc d; - ext2_utils_read_block(ext2, ext2->first_block_group, ((ino - 1) / ext2->sb.s_inodes_per_group) * sizeof(d), &d, sizeof(d), true); - ext2_utils_read_block(ext2, d.bg_inode_table, ((ino - 1) % ext2->sb.s_inodes_per_group) * ext2->inodesize, data, ext2->inodesize, false); -} + uint32_t group = (ino - 1) / ext2->sb.s_inodes_per_group; + uint32_t index = (ino - 1) % ext2->sb.s_inodes_per_group; + uint32_t byte = index * ext2->inodesize; + struct ext2_group_desc desc; + if (ext2_utils_read_group_desc(ext2, group, &desc) < 0) + return -1; + *block = desc.bg_inode_table + (byte / ext2->blocksize); + *offset = byte % ext2->blocksize; -void ext2_utils_write_inode(ext2_t* ext2, ino_t ino, const void* data) { + if (unlikely(*offset + ext2->inodesize > ext2->blocksize)) + return errno = EIO, -1; + return 0; +} + + +int ext2_utils_read_inode(ext2_t* ext2, ino_t ino, void* data) { DEBUG_ASSERT(ext2); DEBUG_ASSERT(data); - DEBUG_ASSERT(ino != EXT2_BAD_INO); - DEBUG_ASSERT(ino != 0); - struct ext2_group_desc d; - ext2_utils_read_block(ext2, ext2->first_block_group, ((ino - 1) / ext2->sb.s_inodes_per_group) * sizeof(d), &d, sizeof(d), true); - ext2_utils_write_block(ext2, d.bg_inode_table, ((ino - 1) % ext2->sb.s_inodes_per_group) * ext2->inodesize, data, ext2->inodesize); -} + uint32_t block; + uint32_t offset; + if (ext2_inode_location(ext2, ino, &block, &offset) < 0) + return -1; + return ext2_utils_read_block(ext2, block, offset, data, ext2->inodesize, false); +} -void ext2_utils_read_inode_data(ext2_t* ext2, uint32_t* blocks, uint32_t block, uint32_t offset, void* data, size_t size) { +int ext2_utils_write_inode(ext2_t* ext2, ino_t ino, const void* data) { DEBUG_ASSERT(ext2); - DEBUG_ASSERT(blocks); - DEBUG_ASSERT(size); - DEBUG_ASSERT(size <= ext2->blocksize); - + DEBUG_ASSERT(data); - uint32_t a = EXT2_NDIR_BLOCKS, b = (ext2->blocksize / sizeof(uint32_t)); + uint32_t block; + uint32_t offset; + if (ext2_inode_location(ext2, ino, &block, &offset) < 0) + return -1; - // Direct Blocks - if (block < a) { + return ext2_utils_write_block(ext2, block, offset, data, ext2->inodesize); +} - ext2_utils_read_block(ext2, blocks[block], offset, data, size, false); - return; - } +int ext2_utils_alloc_inode(ext2_t* ext2, const struct ext2_inode* inode, ino_t* ino) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(inode); + DEBUG_ASSERT(ino); - // Indirect Blocks - block -= a; + *ino = 0; - if (block < b) { + if (unlikely(ext2->sb.s_free_inodes_count == 0)) + return errno = ENOSPC, -1; - ext2_utils_read_block(ext2, blocks[EXT2_IND_BLOCK], sizeof(uint32_t) * block, &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, offset, data, size, false); - return; - } + for (uint32_t group = 0; group < ext2->count_block_group; group++) { + struct ext2_group_desc desc; + if (ext2_utils_read_group_desc(ext2, group, &desc) < 0) + return -1; - // Double Indirect Blocks - block -= b; + if (desc.bg_free_inodes_count == 0) + continue; - if (block < (b * b)) { + if (ext2_utils_read_block(ext2, desc.bg_inode_bitmap, 0, ext2->iocache, ext2->blocksize, false) < 0) + return -1; - uint32_t p = block; + uint64_t group_start = (uint64_t)group * ext2->sb.s_inodes_per_group; + uint32_t bits = ext2->sb.s_inodes_per_group; - ext2_utils_read_block(ext2, blocks[EXT2_DIND_BLOCK], sizeof(uint32_t) * (p / b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * (p % b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, offset, data, size, false); - return; - } + if (group_start + bits > ext2->sb.s_inodes_count) + bits = ext2->sb.s_inodes_count - group_start; + for (uint32_t bit = 0; bit < bits; bit++) { + ino_t candidate = group_start + bit + 1; - // Triply Indirect Blocks - block -= b * b; + if (candidate < ext2->sb.s_first_ino || ext2_bitmap_test(ext2->iocache, bit)) + continue; - if (block < (b * b * b)) { + ext2_bitmap_set(ext2->iocache, bit); - uint32_t p = block; + if (ext2_utils_write_block(ext2, desc.bg_inode_bitmap, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; - ext2_utils_read_block(ext2, blocks[EXT2_TIND_BLOCK], sizeof(uint32_t) * (p / (b * b)), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((p % (b * b)) / b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((p % (b * b)) % b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, offset, data, size, false); - return; - } + desc.bg_free_inodes_count--; + ext2->sb.s_free_inodes_count--; + if (ext2_utils_write_group_desc(ext2, group, &desc) < 0 || ext2_utils_write_super(ext2) < 0 || ext2_utils_write_inode(ext2, candidate, inode) < 0) + return -1; - kpanicf("%s() FAIL! block is too high!", __func__); -} + ext2->root->sb->st.f_ffree--; + ext2->root->sb->st.f_favail--; + *ino = candidate; + return 0; + } + } + return errno = ENOSPC, -1; +} -void ext2_utils_write_inode_data(ext2_t* ext2, uint32_t* blocks, uint32_t block, uint32_t offset, const void* data, size_t size) { +int ext2_utils_free_inode(ext2_t* ext2, ino_t ino) { DEBUG_ASSERT(ext2); - DEBUG_ASSERT(blocks); - DEBUG_ASSERT(size); - DEBUG_ASSERT(size <= ext2->blocksize); - - uint32_t a = EXT2_NDIR_BLOCKS, b = (ext2->blocksize / sizeof(uint32_t)); + if (unlikely(ino < ext2->sb.s_first_ino || ino > ext2->sb.s_inodes_count)) + return errno = EINVAL, -1; + uint32_t group = (ino - 1) / ext2->sb.s_inodes_per_group; + uint32_t bit = (ino - 1) % ext2->sb.s_inodes_per_group; + struct ext2_group_desc desc; - // Direct Blocks - if (block < a) { + if (ext2_utils_read_group_desc(ext2, group, &desc) < 0) + return -1; - ext2_utils_write_block(ext2, blocks[block], offset, data, size); - return; - } + if (ext2_utils_read_block(ext2, desc.bg_inode_bitmap, 0, ext2->iocache, ext2->blocksize, false) < 0) + return -1; + if (unlikely(!ext2_bitmap_test(ext2->iocache, bit))) + return errno = EINVAL, -1; - // Indirect Blocks - block -= a; + ext2_bitmap_clear(ext2->iocache, bit); - if (block < b) { + if (ext2_utils_write_block(ext2, desc.bg_inode_bitmap, 0, ext2->iocache, ext2->blocksize) < 0) + return -1; - ext2_utils_read_block(ext2, blocks[EXT2_IND_BLOCK], sizeof(uint32_t) * block, &block, sizeof(uint32_t), true); - ext2_utils_write_block(ext2, block, offset, data, size); - return; - } + desc.bg_free_inodes_count++; + ext2->sb.s_free_inodes_count++; + if (ext2_utils_write_group_desc(ext2, group, &desc) < 0 || ext2_utils_write_super(ext2) < 0) + return -1; - // Double Indirect Blocks - block -= b; + ext2->root->sb->st.f_ffree++; + ext2->root->sb->st.f_favail++; - if (block < (b * b)) { + return 0; +} - register uint32_t p = block; - ext2_utils_read_block(ext2, blocks[EXT2_DIND_BLOCK], sizeof(uint32_t) * (p / b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * (p % b), &block, sizeof(uint32_t), true); - ext2_utils_write_block(ext2, block, offset, data, size); +static int ext2_inode_block_path(ext2_t* ext2, uint32_t logical, uint32_t* slot, uint32_t indexes[3], uint32_t* levels) { + uint64_t per_block = ext2->blocksize / sizeof(uint32_t); - return; + if (logical < EXT2_NDIR_BLOCKS) { + *slot = logical; + *levels = 0; + return 0; } + logical -= EXT2_NDIR_BLOCKS; - // Triply Indirect Blocks - block -= b * b; - - if (block < (b * b * b)) { + if (logical < per_block) { + *slot = EXT2_IND_BLOCK; + indexes[0] = logical; + *levels = 1; + return 0; + } - register uint32_t p = block; + logical -= per_block; - ext2_utils_read_block(ext2, blocks[EXT2_TIND_BLOCK], sizeof(uint32_t) * (p / (b * b)), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((p % (b * b)) / b), &block, sizeof(uint32_t), true); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((p % (b * b)) % b), &block, sizeof(uint32_t), true); - ext2_utils_write_block(ext2, block, offset, data, size); - return; + if (logical < per_block * per_block) { + *slot = EXT2_DIND_BLOCK; + indexes[0] = logical / per_block; + indexes[1] = logical % per_block; + *levels = 2; + return 0; } + logical -= per_block * per_block; - kpanicf("%s() FAIL! block is too high!", __func__); -} - + if ((uint64_t)logical < per_block * per_block * per_block) { + *slot = EXT2_TIND_BLOCK; + indexes[0] = logical / (per_block * per_block); + indexes[1] = (logical / per_block) % per_block; + indexes[2] = logical % per_block; + *levels = 3; + return 0; + } -void ext2_utils_alloc_inode_data(ext2_t* ext2, uint32_t* blocks, uint32_t block) { + return errno = EFBIG, -1; +} - DEBUG_ASSERT(ext2); - DEBUG_ASSERT(blocks); - uint32_t a = EXT2_NDIR_BLOCKS, b = (ext2->blocksize / sizeof(uint32_t)), c = 0; +static int ext2_inode_get_block(ext2_t* ext2, struct ext2_inode* inode, uint32_t logical, bool create, uint32_t* physical) { + uint32_t indexes[3] = {0}; + uint32_t levels; + uint32_t slot; + if (ext2_inode_block_path(ext2, logical, &slot, indexes, &levels) < 0) + return -1; - // Direct Blocks - if (block < a) { + if (levels == 0) { + if (inode->i_block[slot] == 0 && create) { + if (ext2_utils_alloc_block(ext2, &inode->i_block[slot]) < 0) + return -1; - if (__block_is_free(blocks[block])) - ext2_utils_alloc_block(ext2, &blocks[block]); + inode->i_blocks += ext2_sectors_per_block(ext2); + } - return; + *physical = inode->i_block[slot]; + return 0; } + uint32_t current = inode->i_block[slot]; + if (current == 0) { + if (!create) { + *physical = 0; + return 0; + } - // Indirect Blocks - block -= a; + if (ext2_utils_alloc_block(ext2, ¤t) < 0) + return -1; + + inode->i_block[slot] = current; + inode->i_blocks += ext2_sectors_per_block(ext2); + } - if (block < b) { + for (uint32_t depth = 0; depth < levels; depth++) { + uint32_t child = 0; - if (__block_is_free(blocks[EXT2_IND_BLOCK])) - ext2_utils_alloc_block(ext2, &blocks[EXT2_IND_BLOCK]); + if (ext2_utils_read_block(ext2, current, indexes[depth] * sizeof(child), &child, sizeof(child), false) < 0) + return -1; + if (child == 0) { + if (!create) { + *physical = 0; + return 0; + } - ext2_utils_read_block(ext2, blocks[EXT2_IND_BLOCK], sizeof(uint32_t) * block, &c, sizeof(uint32_t), true); + if (ext2_utils_alloc_block(ext2, &child) < 0) + return -1; - if (__block_is_free(c)) { + if (ext2_utils_write_block(ext2, current, indexes[depth] * sizeof(child), &child, sizeof(child)) < 0) + return -1; - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, blocks[EXT2_IND_BLOCK], sizeof(uint32_t) * block, &c, sizeof(uint32_t)); + inode->i_blocks += ext2_sectors_per_block(ext2); } - return; + current = child; } + *physical = current; + return 0; +} + +static bool ext2_inode_pointer_block_empty(ext2_t* ext2, uint32_t block) { + if (ext2_utils_read_block(ext2, block, 0, ext2->iocache, ext2->blocksize, false) < 0) + return false; - // Double Indirect Blocks - block -= b; + uint32_t* pointers = ext2->iocache; - if (block < (b * b)) { + for (uint32_t i = 0; i < ext2->blocksize / sizeof(*pointers); i++) { + if (pointers[i] != 0) + return false; + } - if (__block_is_free(blocks[EXT2_DIND_BLOCK])) - ext2_utils_alloc_block(ext2, &blocks[EXT2_DIND_BLOCK]); + return true; +} - ext2_utils_read_block(ext2, blocks[EXT2_DIND_BLOCK], sizeof(uint32_t) * (block / b), &c, sizeof(uint32_t), true); +int ext2_utils_free_inode_data(ext2_t* ext2, struct ext2_inode* inode, uint32_t logical) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(inode); - if (__block_is_free(c)) { + uint32_t indexes[3] = {0}; + uint32_t pointers[3] = {0}; + uint32_t levels; + uint32_t slot; - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, blocks[EXT2_DIND_BLOCK], sizeof(uint32_t) * (block / b), &c, sizeof(uint32_t)); - } + if (ext2_inode_block_path(ext2, logical, &slot, indexes, &levels) < 0) + return -1; - block = c; + if (levels == 0) { + if (inode->i_block[slot] == 0) + return 0; + if (ext2_utils_free_block(ext2, inode->i_block[slot]) < 0) + return -1; - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * (block % b), &c, sizeof(uint32_t), true); + inode->i_block[slot] = 0; + inode->i_blocks -= ext2_sectors_per_block(ext2); + return 0; + } - if (__block_is_free(c)) { + uint32_t current = inode->i_block[slot]; - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, block, sizeof(uint32_t) * (block % b), &c, sizeof(uint32_t)); - } + if (current == 0) + return 0; - return; - } + for (uint32_t depth = 0; depth < levels; depth++) { + pointers[depth] = current; + if (ext2_utils_read_block(ext2, current, indexes[depth] * sizeof(current), ¤t, sizeof(current), false) < 0) + return -1; + if (current == 0) + return 0; + } - // Triply Indirect Blocks - block -= b * b; + if (ext2_utils_free_block(ext2, current) < 0) + return -1; - if (block < (b * b * b)) { + inode->i_blocks -= ext2_sectors_per_block(ext2); + current = 0; - if (__block_is_free(blocks[EXT2_TIND_BLOCK])) - ext2_utils_alloc_block(ext2, &blocks[EXT2_TIND_BLOCK]); + if (ext2_utils_write_block(ext2, pointers[levels - 1], indexes[levels - 1] * sizeof(current), ¤t, sizeof(current)) < 0) + return -1; + for (int depth = levels - 1; depth >= 0; depth--) { + if (!ext2_inode_pointer_block_empty(ext2, pointers[depth])) + break; - ext2_utils_read_block(ext2, blocks[EXT2_TIND_BLOCK], sizeof(uint32_t) * (block / (b * b)), &c, sizeof(uint32_t), true); + if (ext2_utils_free_block(ext2, pointers[depth]) < 0) + return -1; - if (__block_is_free(c)) { + inode->i_blocks -= ext2_sectors_per_block(ext2); - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, blocks[EXT2_DIND_BLOCK], sizeof(uint32_t) * (block / (b * b)), &c, sizeof(uint32_t)); + if (depth == 0) { + inode->i_block[slot] = 0; + } else { + if (ext2_utils_write_block(ext2, pointers[depth - 1], indexes[depth - 1] * sizeof(current), ¤t, sizeof(current)) < 0) + return -1; } + } + + return 0; +} - block = c; +int ext2_utils_read_inode_data(ext2_t* ext2, struct ext2_inode* inode, uint32_t block, uint32_t offset, void* data, size_t size) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(inode); + DEBUG_ASSERT(data); - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((block % (b * b)) / b), &c, sizeof(uint32_t), true); + if (unlikely(offset > ext2->blocksize || size > ext2->blocksize - offset)) + return errno = EINVAL, -1; - if (__block_is_free(c)) { + uint32_t physical; - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, block, sizeof(uint32_t) * ((block % (b * b)) / b), &c, sizeof(uint32_t)); - } + if (ext2_inode_get_block(ext2, inode, block, false, &physical) < 0) + return -1; - block = c; + if (physical == 0) { + memset(data, 0, size); + return 0; + } + return ext2_utils_read_block(ext2, physical, offset, data, size, false); +} - ext2_utils_read_block(ext2, block, sizeof(uint32_t) * ((block % (b * b)) % b), &c, sizeof(uint32_t), true); - if (__block_is_free(c)) { +int ext2_utils_write_inode_data(ext2_t* ext2, struct ext2_inode* inode, uint32_t block, uint32_t offset, const void* data, size_t size) { + DEBUG_ASSERT(ext2); + DEBUG_ASSERT(inode); + DEBUG_ASSERT(data); - ext2_utils_alloc_block(ext2, &c); - ext2_utils_write_block(ext2, block, sizeof(uint32_t) * ((block % (b * b)) % b), &c, sizeof(uint32_t)); - } + if (unlikely(offset > ext2->blocksize || size > ext2->blocksize - offset)) + return errno = EINVAL, -1; - return; - } + uint32_t physical; + if (ext2_inode_get_block(ext2, inode, block, true, &physical) < 0) + return -1; - kpanicf("%s() FAIL! block is too high!", __func__); + return ext2_utils_write_block(ext2, physical, offset, data, size); } diff --git a/kernel/syscalls/257_openat.c b/kernel/syscalls/257_openat.c index 75345503..e54192b7 100644 --- a/kernel/syscalls/257_openat.c +++ b/kernel/syscalls/257_openat.c @@ -146,6 +146,13 @@ SYSCALL( } } + if ((flags & O_TRUNC) && ((flags & O_WRONLY) || (flags & O_RDWR)) && S_ISREG(st.st_mode)) { + if (vfs_truncate(r, 0) < 0) + return -errno; + + st.st_size = 0; + } + inode_t* inode = NULL; diff --git a/libk/utils/cache.c b/libk/utils/cache.c index a753ea4a..fe53c75d 100644 --- a/libk/utils/cache.c +++ b/libk/utils/cache.c @@ -196,8 +196,10 @@ void __cache_commit(cache_t* cache, cache_key_t key) { if (v != NULL) { if (cache->ops.commit) { - cache->ops.commit(cache, cache->userdata, key, value); + cache->ops.commit(cache, cache->userdata, key, v); } + + value = v; } }