diff options
Diffstat (limited to 'fs/lfs')
| -rw-r--r-- | fs/lfs/t_basic.c | 79 | ||||
| -rw-r--r-- | fs/lfs/t_fcntl.c | 407 | ||||
| -rw-r--r-- | fs/lfs/t_orphan.c | 203 | ||||
| -rw-r--r-- | fs/lfs/t_resize.c | 180 | ||||
| -rw-r--r-- | fs/lfs/util.c | 181 | ||||
| -rw-r--r-- | fs/lfs/util.h | 28 |
6 files changed, 1078 insertions, 0 deletions
diff --git a/fs/lfs/t_basic.c b/fs/lfs/t_basic.c new file mode 100644 index 000000000000..3a01a5a43569 --- /dev/null +++ b/fs/lfs/t_basic.c @@ -0,0 +1,79 @@ +/* $NetBSD: t_basic.c,v 1.1 2025/10/13 00:44:35 perseant Exp $ */ + +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <atf-c.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <ufs/ufs/ufsmount.h> +#include <ufs/lfs/lfs.h> +#include <ufs/lfs/lfs_extern.h> + +#include "h_macros.h" +#include "util.h" + +#define FSSIZE 10000 + +/* Actually run the test */ +void test(int); + +ATF_TC(newfs_fsck32); +ATF_TC_HEAD(newfs_fsck32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 newfs_lfs produces a filesystem that passes fsck_lfs"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC(newfs_fsck64); +ATF_TC_HEAD(newfs_fsck64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 newfs_lfs produces a filesystem that passes fsck_lfs"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC_BODY(newfs_fsck32, tc) +{ + test(32); +} + +ATF_TC_BODY(newfs_fsck64, tc) +{ + test(64); +} + +void test(int width) +{ + setvbuf(stdout, NULL, _IONBF, 0); + + /* + * Initialize. + */ + + /* Create image file larger than filesystem */ + create_lfs(FSSIZE, FSSIZE, width, 1); + + if (fsck()) + atf_tc_fail_errno("fsck found errors after first unmount"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, newfs_fsck32); + ATF_TP_ADD_TC(tp, newfs_fsck64); + return atf_no_error(); +} diff --git a/fs/lfs/t_fcntl.c b/fs/lfs/t_fcntl.c new file mode 100644 index 000000000000..0d7c1f86d79f --- /dev/null +++ b/fs/lfs/t_fcntl.c @@ -0,0 +1,407 @@ +/* $NetBSD: t_fcntl.c,v 1.6 2025/12/10 03:08:52 perseant Exp $ */ + +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <atf-c.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <string.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <ufs/ufs/ufsmount.h> +#include <ufs/lfs/lfs.h> +#include <ufs/lfs/lfs_extern.h> + +#include "h_macros.h" +#include "util.h" + +/* Debugging conditions */ +/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */ +/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */ + +#define UNCHANGED_CONTROL MP "/3-a-random-file" +#define FSSIZE 10000 /* In sectors */ +#define A_FEW_BLOCKS 6500 /* In bytes; a few blocks worth */ +#define MORE_THAN_A_SEGMENT (4 * SEGSIZE) /* In bytes; > SEGSIZE */ + +/* Set up filesystem with a file in it */ +int setup(int, struct ufs_args *, off_t); + +/* Actually run the test */ +void coalesce(int); +void cleanseg(int); +void autoclean(int); + +/* Unmount and check fsck */ +void teardown(int, struct ufs_args *, off_t); + +ATF_TC(coalesce32); +ATF_TC_HEAD(coalesce32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 LFCNSCRAMBLE/LFCNREWRITEFILE"); + /* atf_tc_set_md_var(tc, "timeout", "20"); */ +} + +ATF_TC(coalesce64); +ATF_TC_HEAD(coalesce64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 LFCNSCRAMBLE/LFCNREWRITEFILE"); + /* atf_tc_set_md_var(tc, "timeout", "20"); */ +} + +ATF_TC(cleanseg32); +ATF_TC_HEAD(cleanseg32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 LFCNREWRITESEG"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC(cleanseg64); +ATF_TC_HEAD(cleanseg64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 LFCNREWRITESEG"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC(autoclean32); +ATF_TC_HEAD(autoclean32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 LFCNAUTOCLEAN"); + /* atf_tc_set_md_var(tc, "timeout", "20"); */ +} + +ATF_TC(autoclean64); +ATF_TC_HEAD(autoclean64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 LFCNAUTOCLEAN"); + /* atf_tc_set_md_var(tc, "timeout", "20"); */ +} + +ATF_TC_BODY(coalesce32, tc) +{ + coalesce(32); +} + +ATF_TC_BODY(coalesce64, tc) +{ + coalesce(64); +} + +ATF_TC_BODY(cleanseg32, tc) +{ + cleanseg(32); +} + +ATF_TC_BODY(cleanseg64, tc) +{ + cleanseg(64); +} + +ATF_TC_BODY(autoclean32, tc) +{ + autoclean(32); +} + +ATF_TC_BODY(autoclean64, tc) +{ + autoclean(64); +} + +int setup(int width, struct ufs_args *argsp, off_t filesize) +{ + int fd; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* + * Initialize. + */ + + /* Create image file */ + create_lfs(FSSIZE, FSSIZE, width, 1); + + /* Mount filesystem */ + fprintf(stderr, "* Mount fs [1]\n"); + memset(argsp, 0, sizeof *argsp); + argsp->fspec = __UNCONST(FAKEBLK); + if (rump_sys_mount(MOUNT_LFS, MP, 0, argsp, sizeof *argsp) == -1) + atf_tc_fail_errno("rump_sys_mount failed"); + + /* Payload */ + fprintf(stderr, "* Initial payload\n"); + write_file(UNCHANGED_CONTROL, filesize, 1, 3); + + /* Make the data go to disk */ + fprintf(stderr, "* Double sync\n"); + rump_sys_sync(); + rump_sys_sync(); + + /* Get a handle to the root of the file system */ + fd = rump_sys_open(MP, O_RDONLY); + if (fd < 0) + atf_tc_fail_errno("rump_sys_open mount point root failed"); + + return fd; +} + +void teardown(int fd, struct ufs_args *argsp, off_t filesize) +{ + /* Close descriptor */ + rump_sys_close(fd); + + /* Unmount */ + if (rump_sys_unmount(MP, 0) < 0) + atf_tc_fail_errno("rump_sys_unmount failed[1]"); + + /* Fsck */ + fprintf(stderr, "* Fsck after final unmount\n"); + if (fsck()) + atf_tc_fail("fsck found errors after final unmount"); + + /* + * Check file system contents + */ + + /* Mount filesystem one last time */ + fprintf(stderr, "* Mount fs again to check contents\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, argsp, sizeof *argsp) == -1) + atf_tc_fail_errno("rump_sys_mount failed [4]"); + + if (check_file(UNCHANGED_CONTROL, filesize, 3) != 0) + atf_tc_fail("Unchanged control file differs(!)"); + + /* Umount filesystem */ + if (rump_sys_unmount(MP, 0) < 0) + atf_tc_fail_errno("rump_sys_unmount failed[2]"); + + /* Final fsck to double check */ + fprintf(stderr, "* Fsck after final unmount\n"); + if (fsck()) + atf_tc_fail("fsck found errors after final unmount"); +} + +void coalesce(int width) +{ + struct ufs_args args; + struct stat statbuf; + struct lfs_filestat_req fsr; + struct lfs_filestats fss_before, fss_scrambled, fss_after; + struct lfs_inode_array inotbl; + int fd; + ino_t ino; + + fd = setup(width, &args, A_FEW_BLOCKS); + + /* Get our file's inode number */ + if (rump_sys_stat(UNCHANGED_CONTROL, &statbuf) != 0) + atf_tc_fail_errno("rump_sys_stat failed"); + ino = statbuf.st_ino; + + fprintf(stderr, "Treating inode %d\n", (int)ino); + + /* Retrieve fragmentation statistics */ + memset(&fss_before, 0, sizeof fss_before); + memset(&fss_scrambled, 0, sizeof fss_scrambled); + memset(&fss_after, 0, sizeof fss_after); + + fsr.ino = ino; + fsr.len = 1; + fsr.fss = &fss_before; + if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0) + atf_tc_fail_errno("LFCNFILESTATS[1] failed"); + + inotbl.len = 1; + inotbl.inodes = &ino; + + fprintf(stderr, "Start ino %d nblk %d count %d total %d\n", + (int)ino, (int)fss_before.nblk, (int)fss_before.dc_count, + (int)fss_before.dc_sum); + + /* Scramble */ + if (rump_sys_fcntl(fd, LFCNSCRAMBLE, &inotbl) < 0) + atf_tc_fail_errno("LFCNSCRAMBLE failed"); + fsr.fss = &fss_scrambled; + if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0) + atf_tc_fail_errno("LFCNFILESTATS[2] failed"); + if (fss_scrambled.dc_count <= fss_before.dc_count) + atf_tc_fail("Scramble did not worsen gap count"); + if (fss_scrambled.dc_sum <= fss_before.dc_sum) + atf_tc_fail("Scramble did not worsen total gap length"); + + fprintf(stderr, "Scrambled ino %d nblk %d count %d total %d\n", + (int)ino, (int)fss_scrambled.nblk, (int)fss_scrambled.dc_count, + (int)fss_scrambled.dc_sum); + + /* Coalesce */ + if (rump_sys_fcntl(fd, LFCNREWRITEFILE, &inotbl) < 0) + atf_tc_fail_errno("LFCNREWRITEFILE failed"); + fsr.fss = &fss_after; + if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0) + atf_tc_fail_errno("LFCNFILESTATS[3] failed"); + if (fss_scrambled.dc_count <= fss_after.dc_count) + atf_tc_fail("Rewrite did not improve gap count"); + if (fss_scrambled.dc_sum <= fss_after.dc_sum) + atf_tc_fail("Rewrite did not improve total gap length"); + + fprintf(stderr, "Coalesced ino %d nblk %d count %d total %d\n", + (int)ino, (int)fss_after.nblk, (int)fss_after.dc_count, + (int)fss_after.dc_sum); + + teardown(fd, &args, A_FEW_BLOCKS); +} + +void cleanseg(int width) +{ + struct ufs_args args; + struct lfs_segnum_array sna; + struct lfs_seguse_array sua; + int fd, sn; + + fd = setup(width, &args, MORE_THAN_A_SEGMENT); + + fprintf(stderr, "* Get seguse\n"); + sua.len = LFS_SEGUSE_MAXCNT; + sua.start = 0; + sua.seguse = malloc(LFS_SEGUSE_MAXCNT * sizeof(*sua.seguse)); + if (rump_sys_fcntl(fd, LFCNSEGUSE, &sua) < 0) + atf_tc_fail_errno("LFCNSEGUSE[1] failed"); + + for (sn = 0; sn < (int)sua.len; ++sn) { + if (sua.seguse[sn].su_nbytes == 0) + continue; + if ((sua.seguse[sn].su_flags & (SEGUSE_DIRTY | SEGUSE_ACTIVE)) + != SEGUSE_DIRTY) + continue; + break; + } + if (sn == (int)sua.len) + atf_tc_fail("No segments found to clean"); + + fprintf(stderr, "* Cleaning segment #%d\n", sn); + + memset(&sna, 0, sizeof sna); + sna.len = 1; + sna.segments = &sn; + if (rump_sys_fcntl(fd, LFCNREWRITESEGS, &sna) < 0) + atf_tc_fail_errno("LFCNREWRITESEGS failed"); + + fprintf(stderr, "* Double sync\n"); + rump_sys_sync(); + rump_sys_sync(); + + fprintf(stderr, "* Get seguse again\n"); + sua.len = 1; + sua.start = sn; + if (rump_sys_fcntl(fd, LFCNSEGUSE, &sua) < 0) + atf_tc_fail_errno("LFCNSEGUSE[2] failed"); + + if (sua.seguse[0].su_nbytes > 0) + atf_tc_fail("Empty bytes after clean"); + + fprintf(stderr, "* Teardown\n"); + teardown(fd, &args, MORE_THAN_A_SEGMENT); +} + +void autoclean(int width) +{ + struct ufs_args args; + struct lfs_autoclean_params autoclean; + int i, fd; + char filename[1024]; + struct statvfs statbuf; + CLEANERINFO64 ci_before, ci_after; + unsigned long target; + + fd = setup(width, &args, CHUNKSIZE); + + rump_sys_fstatvfs1(fd, &statbuf, ST_WAIT); + target = 7 * statbuf.f_blocks / 8; + + /* Write a number of files, deleting every other one. */ + fprintf(stderr, "* Write numbered files\n"); + for (i = 4; ; i++) { + fprintf(stderr, "* create %d\n", i); + sprintf(filename, "%s/%d", MP, i); + write_file(filename, SEGSIZE / 3, 1, 0); + rump_sys_fstatvfs1(fd, &statbuf, ST_WAIT); + if (statbuf.f_bfree < target) + break; + fprintf(stderr, " %ld blocks vs target %ld\n", + (long)statbuf.f_bfree, (long)target); + } + rump_sys_sync(); + rump_sys_sync(); + + /* Record cleanerinfo */ + fprintf(stderr, "* Get cleanerinfo\n"); + if (rump_sys_fcntl(fd, LFCNCLEANERINFO, &ci_before) < 0) + atf_tc_fail_errno("LFCNCLEANERINFO[1] failed"); + + /* Start autocleaner */ + autoclean.size = sizeof(autoclean); + autoclean.mode = LFS_CLEANMODE_GREEDY; + autoclean.thresh = -1; + autoclean.target = -1; + if (rump_sys_fcntl(fd, LFCNAUTOCLEAN, &autoclean) < 0) + atf_tc_fail_errno("LFCNAUTOCLEAN[1] failed"); + + /* Make many segments almost empty */ + fprintf(stderr, "* Delete most files\n"); + for (; i > 0; i--) { + if (i % 3 == 0) + continue; + fprintf(stderr, "* delete %d\n", i); + sprintf(filename, "%s/%d", MP, i); + rump_sys_unlink(filename); + } + rump_sys_sync(); + rump_sys_sync(); + + /* Give the cleaner a chance to run */ + fprintf(stderr, "* Sleep\n"); + sleep(5); + + /* Auto reclaim freed segments */ + fprintf(stderr, "* Sync\n"); + rump_sys_sync(); + rump_sys_sync(); + + /* Record cleanerinfo again */ + fprintf(stderr, "* Get cleanerinfo again\n"); + if (rump_sys_fcntl(fd, LFCNCLEANERINFO, &ci_after) < 0) + atf_tc_fail_errno("LFCNCLEANERINFO[2] failed"); + + /* Compare */ + if (ci_before.avail >= ci_after.avail) + atf_tc_fail("No improvement"); + + teardown(fd, &args, CHUNKSIZE); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, coalesce32); + ATF_TP_ADD_TC(tp, coalesce64); + ATF_TP_ADD_TC(tp, cleanseg32); + ATF_TP_ADD_TC(tp, cleanseg64); + ATF_TP_ADD_TC(tp, autoclean32); + ATF_TP_ADD_TC(tp, autoclean64); + return atf_no_error(); +} diff --git a/fs/lfs/t_orphan.c b/fs/lfs/t_orphan.c new file mode 100644 index 000000000000..c1cf7f1f71b8 --- /dev/null +++ b/fs/lfs/t_orphan.c @@ -0,0 +1,203 @@ +/* $NetBSD: t_orphan.c,v 1.4 2025/12/19 20:58:08 perseant Exp $ */ + +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <atf-c.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <string.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <ufs/ufs/ufsmount.h> +#include <ufs/lfs/lfs.h> +#include <ufs/lfs/lfs_extern.h> + +#include "h_macros.h" +#include "util.h" + +/* Debugging conditions */ +/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */ +/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */ + +#define UNCHANGED_CONTROL MP "/3-a-random-file" +#define FSSIZE 10000 /* In sectors */ +#define FILE_SIZE 65000 /* In bytes; a few blocks worth */ +#define TMPFILE "filehandle" + +/* Actually run the test */ +void orphan(int); + +ATF_TC(orphan32); +ATF_TC_HEAD(orphan32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 orphan removal"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC(orphan64); +ATF_TC_HEAD(orphan64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 orphan removal"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC_BODY(orphan32, tc) +{ + orphan(32); +} + +ATF_TC_BODY(orphan64, tc) +{ + orphan(64); +} + +void orphan(int width) +{ + char s[MAXLINE]; + struct ufs_args args; + struct stat statbuf; + int fd, status; + pid_t childpid; + FILE *fp; + int thisinum, version, found; + ino_t inum; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* + * Initialize. + */ + + /* Create image file */ + create_lfs(FSSIZE, FSSIZE, width, 0); + + /* Prepare to mount */ + memset(&args, 0, sizeof args); + args.fspec = __UNCONST(FAKEBLK); + + if ((childpid = fork()) == 0) { + /* Set up rump */ + rump_init(); + if (rump_sys_mkdir(MP, 0777) == -1) + atf_tc_fail_errno("cannot create mountpoint"); + rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK); + + /* Mount filesystem */ + fprintf(stderr, "* Mount fs [1]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof args) == -1) + atf_tc_fail_errno("rump_sys_mount failed"); + + /* Payload */ + fprintf(stderr, "* Initial payload\n"); + fd = write_file(UNCHANGED_CONTROL, FILE_SIZE, 0, 0); + + /* Make the data go to disk */ + rump_sys_sync(); + rump_sys_sync(); + + /* Write the inode number into a temporary file */ + rump_sys_fstat(fd, &statbuf); + fprintf(stderr, "Inode is => %d <=\n", (int)statbuf.st_ino); + + fp = fopen(TMPFILE, "wb"); + fwrite(&statbuf.st_ino, sizeof(ino_t), 1, fp); + fclose(fp); + + /* Delete while still open */ + if (rump_sys_unlink(UNCHANGED_CONTROL) != 0) + atf_tc_fail_errno("rump_sys_unlink failed"); + + /* Sanity check values */ + if (statbuf.st_size != FILE_SIZE) + atf_tc_fail("wrong size in initial stat"); + if (statbuf.st_nlink <= 0) + atf_tc_fail("file already deleted"); + if (statbuf.st_blocks <= 0) + atf_tc_fail("no blocks"); + + /* Make the data go to disk */ + rump_sys_sync(); + rump_sys_sync(); + + /* Simulate a system crash */ + exit(0); + } + + /* Wait for child to terminate */ + waitpid(childpid, &status, 0); + + /* If it died, die ourselves */ + if (WEXITSTATUS(status)) + exit(WEXITSTATUS(status)); + + /* Fsck */ + fprintf(stderr, "* Fsck after crash\n"); + if (fsck()) + atf_tc_fail("fsck found errors after crash"); + + /* Read the inode number from temporary file */ + fp = fopen(TMPFILE, "rb"); + fread(&inum, sizeof(ino_t), 1, fp); + fclose(fp); + + fprintf(stderr, "Seeking inum => %d <=\n", (int)inum); + + /* Set up rump */ + rump_init(); + if (rump_sys_mkdir(MP, 0777) == -1) + atf_tc_fail_errno("cannot create mountpoint"); + rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK); + + /* Remount */ + fprintf(stderr, "* Mount fs [2]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof args) == -1) + atf_tc_fail_errno("rump_sys_mount failed[2]"); + + /* At this point the orphan should be deleted. */ + + /* Unmount */ + fprintf(stderr, "* Unmount\n"); + if (rump_sys_unmount(MP, 0) != 0) + atf_tc_fail_errno("rump_sys_unmount failed[1]"); + + /* Check that it was in fact deleted. */ + fprintf(stderr, "* Check for orphaned file\n"); + found = 0; + fp = popen("dumplfs -i -s9 ./" IMGNAME, "r"); + while (fgets(s, MAXLINE, fp) != NULL) { + if (sscanf(s, "%d FREE %d", &thisinum, &version) == 2 + && (ino_t)thisinum == inum) { + found = 1; + break; + } + } + fclose(fp); + if (!found) + atf_tc_fail("orphaned inode not freed on subsequent mount"); + + /* Fsck */ + fprintf(stderr, "* Fsck after final unmount\n"); + if (fsck()) + atf_tc_fail("fsck found errors after final unmount"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, orphan32); + ATF_TP_ADD_TC(tp, orphan64); + return atf_no_error(); +} + diff --git a/fs/lfs/t_resize.c b/fs/lfs/t_resize.c new file mode 100644 index 000000000000..0dae56429261 --- /dev/null +++ b/fs/lfs/t_resize.c @@ -0,0 +1,180 @@ +/* $NetBSD: t_resize.c,v 1.2 2025/10/30 15:30:17 perseant Exp $ */ + +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/wait.h> + +#include <atf-c.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <ufs/ufs/ufsmount.h> +#include <ufs/lfs/lfs.h> +#include <ufs/lfs/lfs_extern.h> + +#include "h_macros.h" +#include "util.h" + +/* Debugging conditions */ +/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */ +/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */ + +#define UNCHANGED_CONTROL MP "/3-a-random-file" +#define BIGSIZE 15000 +#define SMALLSIZE 10000 +__CTASSERT(BIGSIZE > SMALLSIZE); + +/* Resize filesystem */ +void resize(int, size_t); + +/* Actually run the test */ +void test(int); + +ATF_TC(resize32); +ATF_TC_HEAD(resize32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS32 resize_lfs creates an inconsistent filesystem"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC(resize64); +ATF_TC_HEAD(resize64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS64 resize_lfs creates an inconsistent filesystem"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +ATF_TC_BODY(resize32, tc) +{ + test(32); +} + +ATF_TC_BODY(resize64, tc) +{ + test(64); +} + +void test(int width) +{ + struct ufs_args args; + int fd; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* + * Initialize. + */ + + /* Create image file larger than filesystem */ + create_lfs(BIGSIZE, SMALLSIZE, width, 1); + + /* Mount filesystem */ + fprintf(stderr, "* Mount fs [1]\n"); + memset(&args, 0, sizeof(args)); + args.fspec = __UNCONST(FAKEBLK); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed"); + + /* Payload */ + fprintf(stderr, "* Initial payload\n"); + write_file(UNCHANGED_CONTROL, CHUNKSIZE, 1, 0); + + /* Unmount */ + rump_sys_unmount(MP, 0); + if (fsck()) + atf_tc_fail_errno("fsck found errors after first unmount"); + + /* + * Remount and resize. + */ + + /* Reconfigure and mount filesystem again */ + fprintf(stderr, "* Remount fs [2, to enlarge]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [2]"); + + /* Get a handle to the root of the file system */ + fd = rump_sys_open(MP, O_RDONLY); + if (fd < 0) + atf_tc_fail_errno("rump_sys_open mount point root failed"); + + /* Enlarge filesystem */ + fprintf(stderr, "* Resize (enlarge)\n"); + resize(fd, BIGSIZE); + + /* Unmount fs and check */ + rump_sys_close(fd); + rump_sys_unmount(MP, 0); + if (fsck()) + atf_tc_fail_errno("fsck found errors after enlarge"); + + /* Mount filesystem for shrink */ + fprintf(stderr, "* Mount fs [3, to shrink]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [3]"); + + /* Get a handle to the root of the file system */ + fd = rump_sys_open(MP, O_RDONLY); + if (fd < 0) + atf_tc_fail_errno("rump_sys_open mount point root failed"); + + /* Shrink filesystem */ + fprintf(stderr, "* Resize (shrink)\n"); + resize(fd, SMALLSIZE); + + /* Unmount and check again */ + rump_sys_close(fd); + if (rump_sys_unmount(MP, 0) != 0) + atf_tc_fail_errno("rump_sys_umount failed after shrink"); + fprintf(stderr, "* Fsck after shrink\n"); + if (fsck()) + atf_tc_fail("fsck found errors after shrink"); + + /* + * Check file system contents + */ + + /* Mount filesystem one last time */ + fprintf(stderr, "* Mount fs [4, to check contents]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [4]"); + + if (check_file(UNCHANGED_CONTROL, CHUNKSIZE, 0) != 0) + atf_tc_fail("Unchanged control file differs(!)"); + + /* Umount filesystem */ + rump_sys_unmount(MP, 0); + + /* Final fsck to double check */ + fprintf(stderr, "* Fsck after final unmount\n"); + if (fsck()) + atf_tc_fail("fsck found errors after final unmount"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, resize32); + ATF_TP_ADD_TC(tp, resize64); + return atf_no_error(); +} + +void +resize(int fd, size_t size) +{ + int newnseg = (size * DEV_BSIZE) / SEGSIZE; + + if (rump_sys_fcntl(fd, LFCNRESIZE, &newnseg) != 0) + atf_tc_fail_errno("LFCNRESIZE failed"); +} diff --git a/fs/lfs/util.c b/fs/lfs/util.c new file mode 100644 index 000000000000..75ca59983596 --- /dev/null +++ b/fs/lfs/util.c @@ -0,0 +1,181 @@ +/* $NetBSD: util.c,v 1.6 2025/12/19 20:58:08 perseant Exp $ */ + +#include <sys/mount.h> + +#include <ctype.h> +#include <fcntl.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include "h_macros.h" +#include "util.h" + +long long sbaddr[2] = { -1, -1 }; + +/* Create filesystem, note superblock locations */ +void create_lfs(size_t imgsize, size_t fssize, int width, int do_setup) +{ + FILE *pipe; + char cmd[MAXLINE]; + char buf[MAXLINE]; + + /* Create image file larger than filesystem */ + sprintf(cmd, "dd if=/dev/zero of=%s bs=512 count=%zd", + IMGNAME, imgsize); + if (system(cmd) == -1) + atf_tc_fail_errno("create image failed"); + + /* Create filesystem */ + fprintf(stderr, "* Create file system\n"); + sprintf(cmd, "newfs_lfs -D -F -B %d -s %zd -w%d ./%s > %s", + SEGSIZE, fssize, width, IMGNAME, LOGFILE); + if (system(cmd) == -1) + atf_tc_fail_errno("newfs failed"); + pipe = fopen(LOGFILE, "r"); + if (pipe == NULL) + atf_tc_fail_errno("newfs failed to execute"); + while (fgets(buf, MAXLINE, pipe) != NULL) { + if (sscanf(buf, "%lld,%lld", sbaddr, sbaddr + 1) == 2) + break; + } + while (fgets(buf, MAXLINE, pipe) != NULL) + ; + fclose(pipe); + if (sbaddr[0] < 0 || sbaddr[1] < 0) + atf_tc_fail("superblock not found"); + fprintf(stderr, "* Superblocks at %lld and %lld\n", + sbaddr[0], sbaddr[1]); + + if (do_setup) { + /* Set up rump */ + rump_init(); + if (rump_sys_mkdir(MP, 0777) == -1) + atf_tc_fail_errno("cannot create mountpoint"); + rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK); + } +} + +/* Write some data into a file */ +int write_file(const char *filename, off_t len, int close, unsigned int seed) +{ + int fd; + unsigned i, j; + struct stat statbuf; + int flags = O_CREAT|O_WRONLY; + char buf[1024]; + off_t size; + + srandom(seed); + if (rump_sys_stat(filename, &statbuf) < 0) + size = 0; + else { + size = statbuf.st_size; + flags |= O_APPEND; + + /* Reset randomness */ + for (i = 0; i < size; i++) + random(); + } + + fd = rump_sys_open(filename, flags); + + for (i = 0; i < len; i+= sizeof(buf)) { + for (j = 0; j < sizeof(buf); j++) + buf[j] = ((unsigned)random()) & 0xff; + rump_sys_write(fd, buf, MIN(len - i, (off_t)sizeof(buf))); + } + + if (close) { + rump_sys_close(fd); + fd = -1; + } + + return fd; +} + +/* Check file's existence, size and contents */ +int check_file(const char *filename, int size, unsigned int seed) +{ + int fd, i, j, res; + struct stat statbuf; + unsigned char b, buf[1024]; + + if (rump_sys_stat(filename, &statbuf) < 0) { + fprintf(stderr, "%s: stat failed\n", filename); + return 1; + } + if (size != statbuf.st_size) { + fprintf(stderr, "%s: expected %d bytes, found %d\n", + filename, size, (int)statbuf.st_size); + return 2; + } + + fd = rump_sys_open(filename, O_RDONLY); + + srandom(seed); + for (i = 0; i < size; i += sizeof(buf)) { + res = MIN(size - i, (off_t)sizeof(buf)); + rump_sys_read(fd, buf, res); + for (j = 0; j < res; j++) { + b = (((unsigned)random()) & 0xff); + if (buf[j] != b) { + fprintf(stderr, "%s: byte %d:" + " expected %hhx found %hhx\n", + filename, i + j, + b, buf[j]); + rump_sys_close(fd); + return 3; + } + } + } + rump_sys_close(fd); + fprintf(stderr, "%s: no problem\n", filename); + return 0; +} + +/* Run a file system consistency check */ +int fsck(void) +{ + char s[MAXLINE]; + int i, errors = 0; + FILE *pipe; + char cmd[MAXLINE]; + + for (i = 0; i < 2; i++) { + sprintf(cmd, "fsck_lfs -n -a -b %jd -f " IMGNAME, + (intmax_t)sbaddr[i]); + pipe = popen(cmd, "r"); + while (fgets(s, MAXLINE, pipe) != NULL) { + if (isdigit((int)s[0])) /* "5 files ... " */ + continue; + if (isspace((int)s[0]) || s[0] == '*') + continue; + if (strncmp(s, "Alternate", 9) == 0) + continue; + if (strncmp(s, "ROLL ", 5) == 0) + continue; + fprintf(stderr, "FSCK[sb@%lld]: %s", sbaddr[i], s); + ++errors; + } + pclose(pipe); + if (errors) { + break; + } + } + + return errors; +} + +/* Run dumplfs */ +void dumplfs() +{ + char s[MAXLINE]; + FILE *pipe; + + pipe = popen("dumplfs -S -s 2 -s 1 -s 0 " IMGNAME, "r"); + while (fgets(s, MAXLINE, pipe) != NULL) + fprintf(stderr, "DUMPLFS: %s", s); + pclose(pipe); +} + diff --git a/fs/lfs/util.h b/fs/lfs/util.h new file mode 100644 index 000000000000..b33f89d084ab --- /dev/null +++ b/fs/lfs/util.h @@ -0,0 +1,28 @@ +/* $NetBSD: util.h,v 1.4 2025/10/30 15:30:17 perseant Exp $ */ + +/* Create test image and filesystem, record superblock locations */ +void create_lfs(size_t, size_t, int, int); + +/* Write a well-known byte pattern into a file, appending if it exists */ +int write_file(const char *, off_t, int, unsigned int); + +/* Check the byte pattern and size of the file */ +int check_file(const char *, int, unsigned int); + +/* Check the file system for consistency */ +int fsck(void); + +/* Run dumplfs; for debugging */ +void dumplfs(void); + +#define MAXLINE 132 +#define CHUNKSIZE 300 +#define SEGSIZE 32768 + +#define IMGNAME "disk.img" +#define FAKEBLK "/dev/blk" +#define LOGFILE "newfs.log" + +#define MP "/mp" + +extern long long sbaddr[2]; |
