summaryrefslogtreecommitdiff
path: root/fs/lfs/t_orphan.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/lfs/t_orphan.c')
-rw-r--r--fs/lfs/t_orphan.c203
1 files changed, 203 insertions, 0 deletions
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();
+}
+