From dcc9ea8f8be8e6758c3dd0208fd4d942a3493bc2 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Mon, 8 Jun 2026 22:24:40 -0300 Subject: [PATCH] fs: fix mounted ext2/fat stat and dirent regressions Corrected: - align kernel struct stat with the userspace ABI and update fulltest's local struct stat so stat/fstat copy the expected fields on mounted filesystems - make getdents emit variable-length userspace dirent records with d_reclen and correct d_type values, and update ulibc readdir to consume them - fix ext2 directory entry validation for exact-fit names and replace several 4KB on-stack buffers in ext2 inode/block/bitmap/write paths with heap-backed buffers to avoid stack-related crashes while listing and writing /disk - populate sensible FAT mode/uid/gid defaults so ls -l reports sane metadata on FAT mounts - preserve overlay root/initrd lifetime by keeping the initrd root registered separately and using a no-op close handler for the overlay root wrapper Changed: - add fulltest coverage for open('/disk') + getdents + close and for overlay root lifecycle after getdents/close/exec - extend smoke_test.exp and test_battery.exp with the new /disk readdir and overlay root lifecycle expectations - teach the minimal ulibc stdio formatter to accept a single 'l' length modifier so formats such as %lu work in ls -l output TODO: - add more targeted ext2 create/write regression coverage beyond the current fulltest/smoke battery if a smaller dedicated harness is introduced - keep an eye on remaining direct getdents consumers, but the current tree passes make test and make test-battery in this state --- include/stat.h | 7 + src/kernel/ext2.c | 279 ++++++++++++++++++++++++++-------- src/kernel/fat.c | 22 ++- src/kernel/init.c | 4 +- src/kernel/overlayfs.c | 11 +- src/kernel/syscall.c | 82 +++++++++- tests/smoke_test.exp | 2 + tests/test_battery.exp | 2 + user/cmds/fulltest/fulltest.c | 69 +++++++++ user/ulibc/src/dirent.c | 23 ++- user/ulibc/src/stdio.c | 22 ++- 11 files changed, 428 insertions(+), 95 deletions(-) diff --git a/include/stat.h b/include/stat.h index 4d1765eb..8fc35d84 100644 --- a/include/stat.h +++ b/include/stat.h @@ -32,12 +32,19 @@ #define S_IXOTH 00001 struct stat { + uint32_t st_dev; uint32_t st_ino; uint32_t st_mode; uint32_t st_nlink; uint32_t st_uid; uint32_t st_gid; + uint32_t st_rdev; uint32_t st_size; + uint32_t st_blksize; + uint32_t st_blocks; + int32_t st_atime; + int32_t st_mtime; + int32_t st_ctime; }; #endif diff --git a/src/kernel/ext2.c b/src/kernel/ext2.c index 894b66aa..f6dd10b9 100644 --- a/src/kernel/ext2.c +++ b/src/kernel/ext2.c @@ -147,6 +147,22 @@ struct ext2_node { uint32_t ino; /* inode number */ }; +static int ext2_write_block(struct ext2_mount* em, uint32_t block, const void* buf); + +static uint8_t* ext2_alloc_block_buf(struct ext2_mount* em) { + if (!em || em->block_size == 0 || em->block_size > 4096) return NULL; + return (uint8_t*)kmalloc(em->block_size); +} + +static int ext2_zero_block(struct ext2_mount* em, uint32_t block) { + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return -ENOMEM; + memset(blk_buf, 0, em->block_size); + int rc = ext2_write_block(em, block, blk_buf); + kfree(blk_buf); + return rc; +} + /* ---- Block I/O ---- */ @@ -212,16 +228,21 @@ static int ext2_write_gdt(struct ext2_mount* em) { * since superblock is in block 0 or 1 depending on block_size) */ uint32_t gdt_block = em->first_data_block + 1; uint8_t* p = (uint8_t*)em->gdt; + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return -ENOMEM; for (uint32_t b = 0; b < em->gdt_blocks; b++) { - uint8_t blk_buf[4096]; /* max block size */ memset(blk_buf, 0, em->block_size); uint32_t bytes = em->block_size; uint32_t remain = em->num_groups * (uint32_t)sizeof(struct ext2_group_desc) - b * em->block_size; if (bytes > remain) bytes = remain; memcpy(blk_buf, p + b * em->block_size, bytes); - if (ext2_write_block(em, gdt_block + b, blk_buf) < 0) return -EIO; + if (ext2_write_block(em, gdt_block + b, blk_buf) < 0) { + kfree(blk_buf); + return -EIO; + } } + kfree(blk_buf); return 0; } @@ -239,9 +260,18 @@ static int ext2_read_inode(struct ext2_mount* em, uint32_t ino, struct ext2_inod uint32_t block = inode_table_block + byte_offset / em->block_size; uint32_t offset_in_block = byte_offset % em->block_size; - uint8_t blk_buf[4096]; - if (ext2_read_block(em, block, blk_buf) < 0) return -EIO; - memcpy(out, blk_buf + offset_in_block, sizeof(*out)); + uint32_t sector_index = offset_in_block / EXT2_SECTOR_SIZE; + uint32_t sector_offset = offset_in_block % EXT2_SECTOR_SIZE; + uint32_t need = sector_offset + (uint32_t)sizeof(*out); + uint32_t sectors = (need + EXT2_SECTOR_SIZE - 1U) / EXT2_SECTOR_SIZE; + if (sectors == 0 || sectors > 2) return -EIO; + + uint8_t raw[EXT2_SECTOR_SIZE * 2]; + for (uint32_t s = 0; s < sectors; s++) { + uint32_t lba = em->part_lba + block * em->sectors_per_block + sector_index + s; + if (blockdev_read(em->bdev, lba, raw + s * EXT2_SECTOR_SIZE) < 0) return -EIO; + } + memcpy(out, raw + sector_offset, sizeof(*out)); return 0; } @@ -257,10 +287,23 @@ static int ext2_write_inode(struct ext2_mount* em, uint32_t ino, const struct ex uint32_t block = inode_table_block + byte_offset / em->block_size; uint32_t offset_in_block = byte_offset % em->block_size; - uint8_t blk_buf[4096]; - if (ext2_read_block(em, block, blk_buf) < 0) return -EIO; - memcpy(blk_buf + offset_in_block, in, sizeof(*in)); - return ext2_write_block(em, block, blk_buf); + uint32_t sector_index = offset_in_block / EXT2_SECTOR_SIZE; + uint32_t sector_offset = offset_in_block % EXT2_SECTOR_SIZE; + uint32_t need = sector_offset + (uint32_t)sizeof(*in); + uint32_t sectors = (need + EXT2_SECTOR_SIZE - 1U) / EXT2_SECTOR_SIZE; + if (sectors == 0 || sectors > 2) return -EIO; + + uint8_t raw[EXT2_SECTOR_SIZE * 2]; + for (uint32_t s = 0; s < sectors; s++) { + uint32_t lba = em->part_lba + block * em->sectors_per_block + sector_index + s; + if (blockdev_read(em->bdev, lba, raw + s * EXT2_SECTOR_SIZE) < 0) return -EIO; + } + memcpy(raw + sector_offset, in, sizeof(*in)); + for (uint32_t s = 0; s < sectors; s++) { + uint32_t lba = em->part_lba + block * em->sectors_per_block + sector_index + s; + if (blockdev_write(em->bdev, lba, raw + s * EXT2_SECTOR_SIZE) < 0) return -EIO; + } + return 0; } /* ---- Block mapping: logical block → physical block ---- */ @@ -270,6 +313,7 @@ static int ext2_write_inode(struct ext2_mount* em, uint32_t ino, const struct ex static uint32_t ext2_block_map(struct ext2_mount* em, const struct ext2_inode* inode, uint32_t logical) { if (!em) return 0; uint32_t ptrs_per_block = em->block_size / 4; + uint8_t* blk_buf = NULL; /* Direct blocks (0..11) */ if (logical < EXT2_NDIR_BLOCKS) { @@ -281,9 +325,15 @@ static uint32_t ext2_block_map(struct ext2_mount* em, const struct ext2_inode* i if (logical < ptrs_per_block) { uint32_t ind_block = inode->i_block[EXT2_IND_BLOCK]; if (ind_block == 0) return 0; - uint8_t blk_buf[4096]; - if (ext2_read_block(em, ind_block, blk_buf) < 0) return 0; - return ((uint32_t*)blk_buf)[logical]; + blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return 0; + if (ext2_read_block(em, ind_block, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } + uint32_t phys = ((uint32_t*)blk_buf)[logical]; + kfree(blk_buf); + return phys; } logical -= ptrs_per_block; @@ -291,13 +341,25 @@ static uint32_t ext2_block_map(struct ext2_mount* em, const struct ext2_inode* i if (logical < ptrs_per_block * ptrs_per_block) { uint32_t dind_block = inode->i_block[EXT2_DIND_BLOCK]; if (dind_block == 0) return 0; - uint8_t blk_buf[4096]; - memset(blk_buf, 0, sizeof(blk_buf)); - if (ext2_read_block(em, dind_block, blk_buf) < 0) return 0; + blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return 0; + memset(blk_buf, 0, em->block_size); + if (ext2_read_block(em, dind_block, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } uint32_t ind = ((uint32_t*)blk_buf)[logical / ptrs_per_block]; - if (ind == 0) return 0; - if (ext2_read_block(em, ind, blk_buf) < 0) return 0; - return ((uint32_t*)blk_buf)[logical % ptrs_per_block]; + if (ind == 0) { + kfree(blk_buf); + return 0; + } + if (ext2_read_block(em, ind, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } + uint32_t phys = ((uint32_t*)blk_buf)[logical % ptrs_per_block]; + kfree(blk_buf); + return phys; } logical -= ptrs_per_block * ptrs_per_block; @@ -305,17 +367,35 @@ static uint32_t ext2_block_map(struct ext2_mount* em, const struct ext2_inode* i { uint32_t tind_block = inode->i_block[EXT2_TIND_BLOCK]; if (tind_block == 0) return 0; - uint8_t blk_buf[4096]; - memset(blk_buf, 0, sizeof(blk_buf)); - if (ext2_read_block(em, tind_block, blk_buf) < 0) return 0; + blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return 0; + memset(blk_buf, 0, em->block_size); + if (ext2_read_block(em, tind_block, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } uint32_t dind = ((uint32_t*)blk_buf)[logical / (ptrs_per_block * ptrs_per_block)]; - if (dind == 0) return 0; + if (dind == 0) { + kfree(blk_buf); + return 0; + } uint32_t rem = logical % (ptrs_per_block * ptrs_per_block); - if (ext2_read_block(em, dind, blk_buf) < 0) return 0; + if (ext2_read_block(em, dind, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } uint32_t ind = ((uint32_t*)blk_buf)[rem / ptrs_per_block]; - if (ind == 0) return 0; - if (ext2_read_block(em, ind, blk_buf) < 0) return 0; - return ((uint32_t*)blk_buf)[rem % ptrs_per_block]; + if (ind == 0) { + kfree(blk_buf); + return 0; + } + if (ext2_read_block(em, ind, blk_buf) < 0) { + kfree(blk_buf); + return 0; + } + uint32_t phys = ((uint32_t*)blk_buf)[rem % ptrs_per_block]; + kfree(blk_buf); + return phys; } } @@ -327,9 +407,13 @@ static uint32_t ext2_alloc_block(struct ext2_mount* em) { for (uint32_t g = 0; g < em->num_groups; g++) { if (em->gdt[g].bg_free_blocks_count == 0) continue; - uint8_t bmap[4096]; - memset(bmap, 0, sizeof(bmap)); - if (ext2_read_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) continue; + uint8_t* bmap = ext2_alloc_block_buf(em); + if (!bmap) return 0; + memset(bmap, 0, em->block_size); + if (ext2_read_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) { + kfree(bmap); + continue; + } uint32_t blocks_in_group = em->blocks_per_group; if (g == em->num_groups - 1) { @@ -340,12 +424,18 @@ static uint32_t ext2_alloc_block(struct ext2_mount* em) { for (uint32_t bit = 0; bit < blocks_in_group; bit++) { if ((bmap[bit / 8] & (1 << (bit % 8))) == 0) { bmap[bit / 8] |= (1 << (bit % 8)); - if (ext2_write_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) return 0; + if (ext2_write_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) { + kfree(bmap); + return 0; + } em->gdt[g].bg_free_blocks_count--; (void)ext2_write_gdt(em); - return g * em->blocks_per_group + bit + em->first_data_block; + uint32_t block = g * em->blocks_per_group + bit + em->first_data_block; + kfree(bmap); + return block; } } + kfree(bmap); } return 0; } @@ -358,13 +448,18 @@ static void ext2_free_block(struct ext2_mount* em, uint32_t block) { if (g >= em->num_groups) return; - uint8_t bmap[4096]; - memset(bmap, 0, sizeof(bmap)); - if (ext2_read_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) return; + uint8_t* bmap = ext2_alloc_block_buf(em); + if (!bmap) return; + memset(bmap, 0, em->block_size); + if (ext2_read_block(em, em->gdt[g].bg_block_bitmap, bmap) < 0) { + kfree(bmap); + return; + } bmap[bit / 8] &= ~(1 << (bit % 8)); (void)ext2_write_block(em, em->gdt[g].bg_block_bitmap, bmap); em->gdt[g].bg_free_blocks_count++; (void)ext2_write_gdt(em); + kfree(bmap); } /* Allocate a free inode, returns inode number or 0. */ @@ -373,19 +468,29 @@ static uint32_t ext2_alloc_inode(struct ext2_mount* em) { for (uint32_t g = 0; g < em->num_groups; g++) { if (em->gdt[g].bg_free_inodes_count == 0) continue; - uint8_t bmap[4096]; - memset(bmap, 0, sizeof(bmap)); - if (ext2_read_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) continue; + uint8_t* bmap = ext2_alloc_block_buf(em); + if (!bmap) return 0; + memset(bmap, 0, em->block_size); + if (ext2_read_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) { + kfree(bmap); + continue; + } for (uint32_t bit = 0; bit < em->inodes_per_group; bit++) { if ((bmap[bit / 8] & (1 << (bit % 8))) == 0) { bmap[bit / 8] |= (1 << (bit % 8)); - if (ext2_write_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) return 0; + if (ext2_write_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) { + kfree(bmap); + return 0; + } em->gdt[g].bg_free_inodes_count--; (void)ext2_write_gdt(em); - return g * em->inodes_per_group + bit + 1; + uint32_t ino = g * em->inodes_per_group + bit + 1; + kfree(bmap); + return ino; } } + kfree(bmap); } return 0; } @@ -397,13 +502,18 @@ static void ext2_free_inode(struct ext2_mount* em, uint32_t ino) { if (g >= em->num_groups) return; - uint8_t bmap[4096]; - memset(bmap, 0, sizeof(bmap)); - if (ext2_read_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) return; + uint8_t* bmap = ext2_alloc_block_buf(em); + if (!bmap) return; + memset(bmap, 0, em->block_size); + if (ext2_read_block(em, em->gdt[g].bg_inode_bitmap, bmap) < 0) { + kfree(bmap); + return; + } bmap[bit / 8] &= ~(1 << (bit % 8)); (void)ext2_write_block(em, em->gdt[g].bg_inode_bitmap, bmap); em->gdt[g].bg_free_inodes_count++; (void)ext2_write_gdt(em); + kfree(bmap); } /* ---- Block mapping write: set logical→physical mapping in inode ---- */ @@ -414,10 +524,7 @@ static uint32_t ext2_ensure_indirect(struct ext2_mount* em, uint32_t val) { if (val != 0) return val; uint32_t nb = ext2_alloc_block(em); if (nb == 0) return 0; - /* Zero out the new indirect block */ - uint8_t zero[4096]; - memset(zero, 0, em->block_size); - if (ext2_write_block(em, nb, zero) < 0) { + if (ext2_zero_block(em, nb) < 0) { ext2_free_block(em, nb); return 0; } @@ -443,10 +550,16 @@ static int ext2_block_map_set(struct ext2_mount* em, uint32_t ino, struct ext2_i inode->i_block[EXT2_IND_BLOCK] = ind_blk; if (ext2_write_inode(em, ino, inode) < 0) return -EIO; - uint8_t blk_buf[4096]; - if (ext2_read_block(em, inode->i_block[EXT2_IND_BLOCK], blk_buf) < 0) return -EIO; + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return -ENOMEM; + if (ext2_read_block(em, inode->i_block[EXT2_IND_BLOCK], blk_buf) < 0) { + kfree(blk_buf); + return -EIO; + } ((uint32_t*)blk_buf)[logical] = phys_block; - return ext2_write_block(em, inode->i_block[EXT2_IND_BLOCK], blk_buf); + int rc = ext2_write_block(em, inode->i_block[EXT2_IND_BLOCK], blk_buf); + kfree(blk_buf); + return rc; } logical -= ptrs_per_block; @@ -456,24 +569,41 @@ static int ext2_block_map_set(struct ext2_mount* em, uint32_t ino, struct ext2_i inode->i_block[EXT2_DIND_BLOCK] = dind_blk; if (ext2_write_inode(em, ino, inode) < 0) return -EIO; - uint8_t blk_buf[4096]; - if (ext2_read_block(em, inode->i_block[EXT2_DIND_BLOCK], blk_buf) < 0) return -EIO; + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return -ENOMEM; + if (ext2_read_block(em, inode->i_block[EXT2_DIND_BLOCK], blk_buf) < 0) { + kfree(blk_buf); + return -EIO; + } uint32_t idx1 = logical / ptrs_per_block; uint32_t idx2 = logical % ptrs_per_block; uint32_t ind = ((uint32_t*)blk_buf)[idx1]; if (ind == 0) { ind = ext2_alloc_block(em); - if (ind == 0) return -ENOSPC; - uint8_t zero[4096]; - memset(zero, 0, em->block_size); - if (ext2_write_block(em, ind, zero) < 0) { ext2_free_block(em, ind); return -EIO; } + if (ind == 0) { + kfree(blk_buf); + return -ENOSPC; + } + if (ext2_zero_block(em, ind) < 0) { + ext2_free_block(em, ind); + kfree(blk_buf); + return -EIO; + } ((uint32_t*)blk_buf)[idx1] = ind; - if (ext2_write_block(em, inode->i_block[EXT2_DIND_BLOCK], blk_buf) < 0) return -EIO; + if (ext2_write_block(em, inode->i_block[EXT2_DIND_BLOCK], blk_buf) < 0) { + kfree(blk_buf); + return -EIO; + } } - if (ext2_read_block(em, ind, blk_buf) < 0) return -EIO; + if (ext2_read_block(em, ind, blk_buf) < 0) { + kfree(blk_buf); + return -EIO; + } ((uint32_t*)blk_buf)[idx2] = phys_block; - return ext2_write_block(em, ind, blk_buf); + int rc = ext2_write_block(em, ind, blk_buf); + kfree(blk_buf); + return rc; } /* Triply indirect — not implemented for now */ @@ -552,6 +682,7 @@ static int ext2_rename_impl(struct fs_node* old_dir, const char* old_name, static int ext2_truncate_impl(struct fs_node* node, uint32_t length); static int ext2_link_impl(struct fs_node* dir, const char* name, struct fs_node* target); static void ext2_close_impl(fs_node_t* node); +static void ext2_root_close_impl(fs_node_t* node); static const struct file_operations ext2_file_fops = { .read = ext2_file_read, @@ -567,6 +698,10 @@ static const struct file_operations ext2_dir_fops = { .close = ext2_close_impl, }; +static const struct file_operations ext2_root_dir_fops = { + .close = ext2_root_close_impl, +}; + static const struct inode_operations ext2_dir_iops = { .lookup = ext2_finddir, .readdir = ext2_readdir_impl, @@ -584,11 +719,15 @@ static void ext2_close_impl(fs_node_t* node) { kfree(en); } +static void ext2_root_close_impl(fs_node_t* node) { + (void)node; +} + static struct ext2_node* ext2_make_node(struct ext2_mount* em, uint32_t ino, const struct ext2_inode* inode, const char* name) { struct ext2_node* en = (struct ext2_node*)kmalloc(sizeof(struct ext2_node)); if (!en) return NULL; - en->mount = em; memset(en, 0, sizeof(*en)); + en->mount = em; en->ino = ino; @@ -642,6 +781,8 @@ static uint32_t ext2_file_read(fs_node_t* node, uint32_t offset, uint32_t size, uint32_t bs = em->block_size; uint32_t total = 0; + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return 0; while (total < size) { uint32_t pos = offset + total; @@ -653,12 +794,12 @@ static uint32_t ext2_file_read(fs_node_t* node, uint32_t offset, uint32_t size, uint32_t phys_block = ext2_block_map(em, &inode, logical_block); if (phys_block == 0) break; - uint8_t blk_buf[4096]; if (ext2_read_block(em, phys_block, blk_buf) < 0) break; memcpy(buffer + total, blk_buf + offset_in_block, chunk); total += chunk; } + kfree(blk_buf); return total; } @@ -678,6 +819,8 @@ static uint32_t ext2_file_write(fs_node_t* node, uint32_t offset, uint32_t size, uint32_t bs = em->block_size; uint32_t total = 0; + uint8_t* blk_buf = ext2_alloc_block_buf(em); + if (!blk_buf) return 0; while (total < size) { uint32_t pos = offset + total; @@ -699,9 +842,10 @@ static uint32_t ext2_file_write(fs_node_t* node, uint32_t offset, uint32_t size, (void)ext2_write_inode(em, en->ino, &inode); } - uint8_t blk_buf[4096]; if (offset_in_block != 0 || chunk != bs) { if (ext2_read_block(em, phys_block, blk_buf) < 0) break; + } else { + memset(blk_buf, 0, bs); } memcpy(blk_buf + offset_in_block, buffer + total, chunk); if (ext2_write_block(em, phys_block, blk_buf) < 0) break; @@ -713,6 +857,7 @@ static uint32_t ext2_file_write(fs_node_t* node, uint32_t offset, uint32_t size, } (void)ext2_write_inode(em, en->ino, &inode); node->length = inode.i_size; + kfree(blk_buf); return total; } @@ -751,7 +896,7 @@ static fs_node_t* ext2_finddir(fs_node_t* node, const char* name) { if (de->rec_len % 4 != 0) goto done; if (off + de->rec_len > bs) goto done; /* F01: Validate name_len */ - if (de->name_len >= de->rec_len - 8) goto done; + if (de->name_len > de->rec_len - 8) goto done; if (de->inode != 0 && de->name_len == name_len) { if (memcmp(de->name, name, name_len) == 0) { @@ -886,7 +1031,7 @@ static int ext2_dir_add_entry(struct ext2_mount* em, uint32_t dir_ino, const cha if (de->rec_len % 4 != 0) break; if (off + de->rec_len > bs) break; /* F01: Validate name_len */ - if (de->name_len >= de->rec_len - 8) break; + if (de->name_len > de->rec_len - 8) break; uint32_t actual_len = ((uint32_t)sizeof(struct ext2_dir_entry) + de->name_len + 3) & ~3U; uint32_t free_space = de->rec_len - actual_len; @@ -972,7 +1117,7 @@ static int ext2_dir_remove_entry(struct ext2_mount* em, uint32_t dir_ino, const if (de->rec_len % 4 != 0) break; if (off + de->rec_len > bs) break; /* F01: Validate name_len */ - if (de->name_len >= de->rec_len - 8) break; + if (de->name_len > de->rec_len - 8) break; if (de->inode != 0 && de->name_len == name_len && memcmp(de->name, name, name_len) == 0) { @@ -1026,7 +1171,7 @@ static int ext2_dir_find(struct ext2_mount* em, uint32_t dir_ino, const char* na if (de->rec_len % 4 != 0) goto not_found; if (off + de->rec_len > bs) goto not_found; /* F01: Validate name_len */ - if (de->name_len >= de->rec_len - 8) goto not_found; + if (de->name_len > de->rec_len - 8) goto not_found; if (de->inode != 0 && de->name_len == name_len && memcmp(de->name, name, name_len) == 0) { @@ -1070,7 +1215,7 @@ static int ext2_dir_is_empty(struct ext2_mount* em, uint32_t dir_ino) { if (de->rec_len % 4 != 0) return 1; if (off + de->rec_len > bs) return 1; /* F01: Validate name_len */ - if (de->name_len >= de->rec_len - 8) return 1; + if (de->name_len > de->rec_len - 8) return 1; if (de->inode != 0) { int is_dot = (de->name_len == 1 && de->name[0] == '.'); @@ -1606,7 +1751,7 @@ vfs_mount_result_t ext2_mount(block_device_t* bdev, uint32_t partition_lba) { root->vfs.gid = root_inode.i_gid; root->vfs.mode = root_inode.i_mode; root->ino = EXT2_ROOT_INO; - root->vfs.f_ops = &ext2_dir_fops; + root->vfs.f_ops = &ext2_root_dir_fops; root->vfs.i_ops = &ext2_dir_iops; /* Build superblock */ diff --git a/src/kernel/fat.c b/src/kernel/fat.c index f3fe9be1..934781cd 100644 --- a/src/kernel/fat.c +++ b/src/kernel/fat.c @@ -500,6 +500,7 @@ static int fat_rename_impl(struct fs_node* old_dir, const char* old_name, struct fs_node* new_dir, const char* new_name); static int fat_truncate_impl(struct fs_node* node, uint32_t length); static void fat_close_impl(fs_node_t* node); +static void fat_root_close_impl(fs_node_t* node); static const struct file_operations fat_file_fops = { .read = fat_file_read, @@ -515,6 +516,10 @@ static const struct file_operations fat_dir_fops = { .close = fat_close_impl, }; +static const struct file_operations fat_root_dir_fops = { + .close = fat_root_close_impl, +}; + static const struct inode_operations fat_dir_iops = { .lookup = fat_finddir, .readdir = fat_readdir_impl, @@ -531,6 +536,10 @@ static void fat_close_impl(fs_node_t* node) { kfree(fn); } +static void fat_root_close_impl(fs_node_t* node) { + (void)node; +} + static struct fat_node* fat_make_node(struct fat_mount* fm, const struct fat_dirent* de, uint32_t parent_cluster, uint32_t dirent_offset) { struct fat_node* fn = (struct fat_node*)kmalloc(sizeof(struct fat_node)); if (!fn) return NULL; @@ -541,21 +550,29 @@ static struct fat_node* fat_make_node(struct fat_mount* fm, const struct fat_dir fn->first_cluster = fat_dirent_cluster(fm, de); fn->parent_cluster = parent_cluster; fn->dir_entry_offset = dirent_offset; + fn->vfs.uid = 0; + fn->vfs.gid = 0; if (de->attr & FAT_ATTR_DIRECTORY) { fn->vfs.flags = FS_DIRECTORY; fn->vfs.length = 0; fn->vfs.inode = fn->first_cluster; + fn->vfs.mode = 0755; fn->vfs.f_ops = &fat_dir_fops; fn->vfs.i_ops = &fat_dir_iops; } else { fn->vfs.flags = FS_FILE; fn->vfs.length = de->file_size; fn->vfs.inode = fn->first_cluster; + fn->vfs.mode = 0644; fn->vfs.f_ops = &fat_file_fops; fn->vfs.i_ops = &fat_file_iops; } + if (de->attr & FAT_ATTR_READONLY) { + fn->vfs.mode &= ~0222U; + } + return fn; } @@ -1317,10 +1334,13 @@ vfs_mount_result_t fat_mount(block_device_t* bdev, uint32_t partition_lba) { memcpy(root->vfs.name, "fat", 4); root->vfs.flags = FS_DIRECTORY; root->vfs.inode = 0; + root->vfs.uid = 0; + root->vfs.gid = 0; + root->vfs.mode = 0755; root->first_cluster = (fm->type == FAT_TYPE_32) ? fm->root_cluster : 0; root->parent_cluster = 0; root->dir_entry_offset = 0; - root->vfs.f_ops = &fat_dir_fops; + root->vfs.f_ops = &fat_root_dir_fops; root->vfs.i_ops = &fat_dir_iops; /* Build superblock */ diff --git a/src/kernel/init.c b/src/kernel/init.c index 21126663..b5cb4995 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -265,6 +265,9 @@ int init_start(const struct boot_info* bi) { HAL_MM_MAP_RW, &initrd_virt) == 0) { uint32_t initrd_sz = (uint32_t)(bi->initrd_end - bi->initrd_start); fs_root = initrd_init((uint32_t)initrd_virt, initrd_sz); + if (fs_root) { + vfs_set_initrd_root(fs_root); + } } else { kprintf("[INITRD] Failed to map initrd physical range.\n"); } @@ -276,7 +279,6 @@ int init_start(const struct boot_info* bi) { fs_node_t* ovl = overlayfs_create_root(fs_root, upper); if (ovl) { (void)vfs_mount_full("/", ovl, "overlayfs", "initrd", 0, NULL, NULL); - vfs_set_initrd_root(ovl); } } } diff --git a/src/kernel/overlayfs.c b/src/kernel/overlayfs.c index cf25c8bc..de459466 100644 --- a/src/kernel/overlayfs.c +++ b/src/kernel/overlayfs.c @@ -109,6 +109,15 @@ static const struct file_operations overlay_file_ops = { .close = overlay_wrap_close, }; +static void overlay_root_close(fs_node_t* node) { + (void)node; +} + +static const struct file_operations overlay_root_ops = { + .read = overlay_read_impl, + .close = overlay_root_close, +}; + static const struct file_operations overlay_dir_ops = { .read = overlay_read_impl, .close = overlay_wrap_close, @@ -340,7 +349,7 @@ fs_node_t* overlayfs_create_root(fs_node_t* lower_root, fs_node_t* upper_root) { root->vfs.flags = FS_DIRECTORY; root->vfs.inode = upper_root->inode; root->vfs.length = 0; - root->vfs.f_ops = &overlay_dir_ops; + root->vfs.f_ops = &overlay_root_ops; root->vfs.i_ops = &overlay_dir_iops; root->path[0] = 0; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 55a9afd5..439ffb71 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1963,18 +1963,31 @@ static int syscall_pipe2_impl(int* user_fds, uint32_t flags) { static int stat_from_node(const fs_node_t* node, struct stat* st) { if (!node || !st) return -EFAULT; + memset(st, 0, sizeof(*st)); + + st->st_dev = 0; st->st_ino = node->inode; st->st_nlink = 1; st->st_size = node->length; st->st_uid = node->uid; st->st_gid = node->gid; + st->st_rdev = 0; + st->st_blksize = 4096; + st->st_blocks = (node->length + 511U) / 512U; + st->st_atime = 0; + st->st_mtime = 0; + st->st_ctime = 0; uint32_t mode = node->mode & 07777; if (node->flags == FS_DIRECTORY) mode |= S_IFDIR; else if (node->flags == FS_CHARDEVICE) mode |= S_IFCHR; else if (node->flags == FS_SYMLINK) mode |= S_IFLNK; else mode |= S_IFREG; - if ((mode & 07777) == 0) mode |= 0755; + if ((mode & 07777) == 0) { + if (node->flags == FS_DIRECTORY) mode |= 0755; + else if (node->flags == FS_SYMLINK) mode |= 0777; + else mode |= 0644; + } st->st_mode = mode; return 0; } @@ -2707,6 +2720,13 @@ static int syscall_mkdir_impl(const char* user_path, uint32_t mode) { } static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) { + struct user_dirent { + uint32_t d_ino; + uint16_t d_reclen; + uint8_t d_type; + char d_name[256]; + }; + if (len == 0) return 0; if (!user_buf) return -EFAULT; if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT; @@ -2719,17 +2739,69 @@ static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) { if (!fn_readdir) return -ENOSYS; uint8_t kbuf[256]; - uint32_t klen = len; - if (klen > (uint32_t)sizeof(kbuf)) klen = (uint32_t)sizeof(kbuf); + uint32_t klen = (uint32_t)sizeof(kbuf); uint32_t idx = f->offset; int rc = fn_readdir(f->node, &idx, kbuf, klen); if (rc < 0) return rc; if (rc == 0) return 0; - if (copy_to_user(user_buf, kbuf, (uint32_t)rc) < 0) return -EFAULT; + uint32_t in_count = (uint32_t)rc / (uint32_t)sizeof(struct vfs_dirent); + struct user_dirent* ubuf = (struct user_dirent*)kmalloc(len); + if (!ubuf) return -ENOMEM; + memset(ubuf, 0, len); + + struct vfs_dirent* kin = (struct vfs_dirent*)kbuf; + uint32_t out_len = 0; + for (uint32_t i = 0; i < in_count; i++) { + uint32_t nlen = 0; + while (nlen < (uint32_t)sizeof(kin[i].d_name) && kin[i].d_name[nlen] != '\0') nlen++; + uint32_t reclen = (uint32_t)offsetof(struct user_dirent, d_name) + nlen + 1U; + reclen = (reclen + 3U) & ~3U; + if (out_len + reclen > len) break; + + struct user_dirent* ude = (struct user_dirent*)((uint8_t*)ubuf + out_len); + ude->d_ino = kin[i].d_ino; + ude->d_reclen = (uint16_t)reclen; + switch (kin[i].d_type) { + case 2: + ude->d_type = 4; + break; + case 3: + ude->d_type = 2; + break; + case 4: + ude->d_type = 6; + break; + case 5: + case 7: + ude->d_type = 10; + break; + case 8: + ude->d_type = 8; + break; + case 1: + default: + ude->d_type = 8; + break; + } + memcpy(ude->d_name, kin[i].d_name, nlen); + ude->d_name[nlen] = '\0'; + out_len += reclen; + } + + if (out_len == 0) { + kfree(ubuf); + return 0; + } + + if (copy_to_user(user_buf, ubuf, out_len) < 0) { + kfree(ubuf); + return -EFAULT; + } + kfree(ubuf); f->offset = idx; - return rc; + return (int)out_len; } static int syscall_unlink_impl(const char* user_path) { diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index c989ec57..020cf4a1 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -135,6 +135,7 @@ set tests { {"pipe EOF" "\\[test\\] pipe EOF OK"} {"readdir /proc" "\\[test\\] readdir /proc OK"} {"readdir /bin" "\\[test\\] readdir /bin OK"} + {"readdir /disk" "\\[test\\] readdir /disk OK"} {"gettimeofday" "\\[test\\] gettimeofday OK"} {"mprotect" "\\[test\\] mprotect OK"} {"madvise" "\\[test\\] madvise OK"} @@ -169,6 +170,7 @@ set tests { {"dlopen/dlsym/dlclose" "\\[test\\] dlopen/dlsym/dlclose OK"} {"execveat" "\\[test\\] execveat OK"} {"pivot_root" "\\[test\\] pivot_root OK"} + {"overlay root lifecycle" "\\[test\\] overlay root lifecycle OK"} } # ---- Poll serial.log for results ---- diff --git a/tests/test_battery.exp b/tests/test_battery.exp index a70dd159..70b08153 100644 --- a/tests/test_battery.exp +++ b/tests/test_battery.exp @@ -239,6 +239,7 @@ set patterns { {"pipe EOF" "\\[test\\] pipe EOF OK"} {"readdir /proc" "\\[test\\] readdir /proc OK"} {"readdir /bin" "\\[test\\] readdir /bin OK"} + {"readdir /disk" "\\[test\\] readdir /disk OK"} {"gettimeofday" "\\[test\\] gettimeofday OK"} {"mprotect" "\\[test\\] mprotect OK"} {"madvise" "\\[test\\] madvise OK"} @@ -276,6 +277,7 @@ set patterns { {"dlopen/dlsym/dlclose" "\\[test\\] dlopen/dlsym/dlclose OK"} {"execveat" "\\[test\\] execveat OK"} {"pivot_root" "\\[test\\] pivot_root OK"} + {"overlay root lifecycle" "\\[test\\] overlay root lifecycle OK"} } set res [wait_for_patterns $serial_log $timeout_sec $patterns] diff --git a/user/cmds/fulltest/fulltest.c b/user/cmds/fulltest/fulltest.c index ad6aed7f..fe74183d 100644 --- a/user/cmds/fulltest/fulltest.c +++ b/user/cmds/fulltest/fulltest.c @@ -394,12 +394,19 @@ enum { #define S_IFREG 0100000 struct stat { + uint32_t st_dev; uint32_t st_ino; uint32_t st_mode; uint32_t st_nlink; uint32_t st_uid; uint32_t st_gid; + uint32_t st_rdev; uint32_t st_size; + uint32_t st_blksize; + uint32_t st_blocks; + int32_t st_atime; + int32_t st_mtime; + int32_t st_ctime; }; static int sys_write(int fd, const void* buf, uint32_t len) { @@ -4311,6 +4318,25 @@ void _start(void) { (uint32_t)(sizeof("[test] readdir /bin OK\n") - 1)); } + { + int fd = sys_open("/disk", 0); + if (fd < 0) { + sys_write(1, "[test] readdir /disk open failed\n", + (uint32_t)(sizeof("[test] readdir /disk open failed\n") - 1)); + sys_exit(1); + } + char dbuf[1024]; + int r = sys_getdents(fd, dbuf, 1024); + (void)sys_close(fd); + if (r <= 0) { + sys_write(1, "[test] readdir /disk empty\n", + (uint32_t)(sizeof("[test] readdir /disk empty\n") - 1)); + sys_exit(1); + } + sys_write(1, "[test] readdir /disk OK\n", + (uint32_t)(sizeof("[test] readdir /disk OK\n") - 1)); + } + enum { NCHILD = 100 }; int children[NCHILD]; for (int i = 0; i < NCHILD; i++) { @@ -5563,6 +5589,49 @@ void _start(void) { (uint32_t)(sizeof("[test] pivot_root OK\n") - 1)); } + { + int dfd = sys_open("/", 0); + if (dfd < 0) { + sys_write(1, "[test] overlay root open failed\n", + (uint32_t)(sizeof("[test] overlay root open failed\n") - 1)); + sys_exit(1); + } + uint8_t dbuf[256]; + int drc = sys_getdents(dfd, dbuf, sizeof(dbuf)); + if (drc <= 0) { + sys_write(1, "[test] overlay root getdents failed\n", + (uint32_t)(sizeof("[test] overlay root getdents failed\n") - 1)); + sys_exit(1); + } + if (sys_close(dfd) < 0) { + sys_write(1, "[test] overlay root close failed\n", + (uint32_t)(sizeof("[test] overlay root close failed\n") - 1)); + sys_exit(1); + } + + int pid = sys_fork(); + if (pid < 0) { + sys_write(1, "[test] overlay root exec fork failed\n", + (uint32_t)(sizeof("[test] overlay root exec fork failed\n") - 1)); + sys_exit(1); + } + if (pid == 0) { + static const char* const ev_argv[] = {"echo", "[overlay-root]", "OK", 0}; + static const char* const ev_envp[] = {0}; + (void)sys_execve("/bin/echo", ev_argv, ev_envp); + sys_exit(1); + } + int st = 0; + sys_waitpid(pid, &st, 0); + if (st != 0) { + sys_write(1, "[test] overlay root exec child failed\n", + (uint32_t)(sizeof("[test] overlay root exec child failed\n") - 1)); + sys_exit(1); + } + sys_write(1, "[test] overlay root lifecycle OK\n", + (uint32_t)(sizeof("[test] overlay root lifecycle OK\n") - 1)); + } + // execve — fork a child that replaces itself with /bin/echo { int pid = sys_fork(); diff --git a/user/ulibc/src/dirent.c b/user/ulibc/src/dirent.c index 05e3d855..467bd113 100644 --- a/user/ulibc/src/dirent.c +++ b/user/ulibc/src/dirent.c @@ -15,9 +15,6 @@ #include "syscall.h" #include "errno.h" -/* AdrOS getdents returns fixed-size entries: { uint32_t ino; char name[256]; } = 260 bytes */ -#define ADROS_DIRENT_SIZE 260 - DIR* opendir(const char* name) { int fd = open(name, O_RDONLY); if (fd < 0) return (void*)0; @@ -42,17 +39,17 @@ struct dirent* readdir(DIR* dirp) { dirp->pos = 0; } - if (dirp->pos + ADROS_DIRENT_SIZE > dirp->len) return (void*)0; + struct dirent* ent = (struct dirent*)(dirp->buf + dirp->pos); + if (ent->d_reclen == 0) return (void*)0; + if (dirp->pos + ent->d_reclen > dirp->len) return (void*)0; - char* ent = dirp->buf + dirp->pos; - uint32_t ino; - memcpy(&ino, ent, 4); - _de_static.d_ino = ino; - _de_static.d_reclen = ADROS_DIRENT_SIZE; - _de_static.d_type = DT_UNKNOWN; - strncpy(_de_static.d_name, ent + 4, 255); - _de_static.d_name[255] = '\0'; - dirp->pos += ADROS_DIRENT_SIZE; + memset(&_de_static, 0, sizeof(_de_static)); + if (ent->d_reclen > sizeof(_de_static)) + memcpy(&_de_static, ent, sizeof(_de_static)); + else + memcpy(&_de_static, ent, ent->d_reclen); + _de_static.d_name[sizeof(_de_static.d_name) - 1] = '\0'; + dirp->pos += ent->d_reclen; return &_de_static; } diff --git a/user/ulibc/src/stdio.c b/user/ulibc/src/stdio.c index da6ae74f..f63ba638 100644 --- a/user/ulibc/src/stdio.c +++ b/user/ulibc/src/stdio.c @@ -283,6 +283,12 @@ int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) { fmt++; } + int long_mod = 0; + if (*fmt == 'l') { + long_mod = 1; + fmt++; + } + /* Specifier */ char tmp[32]; int tmplen = 0; @@ -291,11 +297,11 @@ int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) { switch (*fmt) { case 'd': case 'i': { - int v = va_arg(ap, int); + long v = long_mod ? va_arg(ap, long) : (long)va_arg(ap, int); int neg = 0; - unsigned int uv; + unsigned long uv; if (v < 0) { neg = 1; uv = (unsigned int)(-(v + 1)) + 1; } - else { uv = (unsigned int)v; } + else { uv = (unsigned long)v; } if (uv == 0) { tmp[tmplen++] = '0'; } else { while (uv) { tmp[tmplen++] = (char)('0' + uv % 10); uv /= 10; } } if (neg) tmp[tmplen++] = '-'; @@ -307,7 +313,8 @@ int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) { break; } case 'u': { - unsigned int v = va_arg(ap, unsigned int); + unsigned long v = long_mod ? va_arg(ap, unsigned long) + : (unsigned long)va_arg(ap, unsigned int); if (v == 0) { tmp[tmplen++] = '0'; } else { while (v) { tmp[tmplen++] = (char)('0' + v % 10); v /= 10; } } for (int i = 0; i < tmplen / 2; i++) { @@ -320,13 +327,14 @@ int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) { case 'X': case 'p': { const char* hex = (*fmt == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned int v; + unsigned long v; if (*fmt == 'p') { - v = (unsigned int)(uintptr_t)va_arg(ap, void*); + v = (unsigned long)(uintptr_t)va_arg(ap, void*); tmp[tmplen++] = '0'; tmp[tmplen++] = 'x'; } else { - v = va_arg(ap, unsigned int); + v = long_mod ? va_arg(ap, unsigned long) + : (unsigned long)va_arg(ap, unsigned int); } int start = tmplen; if (v == 0) { tmp[tmplen++] = '0'; } -- 2.43.0