]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
fs: fix mounted ext2/fat stat and dirent regressions
authorTulio A M Mendes <[email protected]>
Tue, 9 Jun 2026 01:24:40 +0000 (22:24 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 9 Jun 2026 01:24:40 +0000 (22:24 -0300)
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
src/kernel/ext2.c
src/kernel/fat.c
src/kernel/init.c
src/kernel/overlayfs.c
src/kernel/syscall.c
tests/smoke_test.exp
tests/test_battery.exp
user/cmds/fulltest/fulltest.c
user/ulibc/src/dirent.c
user/ulibc/src/stdio.c

index 4d1765ebdbb92351d28f480aac4b130dfc30d79e..8fc35d846be0d412fd661e7c0088a39e048c04aa 100644 (file)
 #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
index 894b66aad1177ab55027ca4ea5613666adbd24b1..f6dd10b922dbb0349a4db4843aa817cad180355e 100644 (file)
@@ -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 */
index f3fe9be15d79d101e48a56c9b27c67ad88ba0b02..934781cd6d6c1e57026a4606b47cfe6203fa48a3 100644 (file)
@@ -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 */
index 21126663c0097bc7deeee194e2f95bc86ff7ba31..b5cb4995ad7771c756feaeb22f5c99bfdbe1d8ec 100644 (file)
@@ -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);
             }
         }
     }
index cf25c8bc13bcbd3c01b9eaa55fd98cd9df971e6e..de459466aab89cf188f40649ae1b6e99b277750d 100644 (file)
@@ -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;
index 55a9afd53a63a76dbc9a1c8bd1faccbb63ee1d95..439ffb71c55471ea3a8a8f142529ff1a68431f54 100644 (file)
@@ -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) {
index c989ec5793ec398e9a6c91e362f2e8c394861255..020cf4a180c425602a4f0ce2eae7ff260e5065b2 100755 (executable)
@@ -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 ----
index a70dd15979508c5f597f04a2e2bb59810de58e1e..70b08153d0227e8b75e3f78ea39b199b9b4570a8 100644 (file)
@@ -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]
index ad6aed7fdd5274197341bafc3b34e9c78a130753..fe74183dbf12ebe6c25e8c8ccee7e5e2920a0350 100644 (file)
@@ -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();
index 05e3d855edd0cb0974451f9e444381df16e4f256..467bd113a88c9696a5620e7fbd2e71884ec78d5a 100644 (file)
@@ -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;
 }
 
index da6ae74fdab02622892e1a67f05b417172deb767..f63ba6386e5fb79194ea9e89275eda46ba96de4b 100644 (file)
@@ -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'; }