Home | History | Annotate | Line # | Download | only in lfs
t_rfw.c revision 1.4
      1 /*	$NetBSD: t_rfw.c,v 1.4 2025/08/27 00:28:20 perseant Exp $	*/
      2 
      3 #include <sys/types.h>
      4 #include <sys/mount.h>
      5 #include <sys/wait.h>
      6 
      7 #include <atf-c.h>
      8 #include <ctype.h>
      9 #include <errno.h>
     10 #include <fcntl.h>
     11 #include <limits.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <unistd.h>
     15 #include <string.h>
     16 
     17 #include <rump/rump.h>
     18 #include <rump/rump_syscalls.h>
     19 
     20 #include <ufs/ufs/ufsmount.h>
     21 #include <ufs/lfs/lfs.h>
     22 #include <ufs/lfs/lfs_extern.h>
     23 
     24 #include "h_macros.h"
     25 
     26 /* Debugging conditions */
     27 /* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */
     28 /* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */
     29 
     30 /* Write a well-known byte pattern into a file, appending if it exists */
     31 int write_file(const char *, int);
     32 
     33 /* Check the byte pattern and size of the file */
     34 int check_file(const char *, int);
     35 
     36 /* Check the file system for consistency */
     37 int fsck(void);
     38 
     39 /* Run dumplfs; for debugging */
     40 void dumplfs(void);
     41 
     42 /* Actually run the test */
     43 void test(int);
     44 
     45 ATF_TC(rfw32);
     46 ATF_TC_HEAD(rfw32, tc)
     47 {
     48 	atf_tc_set_md_var(tc, "descr",
     49 		"LFS32 roll-forward creates an inconsistent filesystem");
     50 	atf_tc_set_md_var(tc, "timeout", "20");
     51 }
     52 
     53 ATF_TC(rfw64);
     54 ATF_TC_HEAD(rfw64, tc)
     55 {
     56 	atf_tc_set_md_var(tc, "descr",
     57 		"LFS64 roll-forward creates an inconsistent filesystem");
     58 	atf_tc_set_md_var(tc, "timeout", "20");
     59 }
     60 
     61 #define MAXLINE 132
     62 #define CHUNKSIZE 300
     63 
     64 #define IMGNAME "disk.img"
     65 #define FAKEBLK "/dev/blk"
     66 #define LOGFILE "newfs.log"
     67 #define SBLOCK0_COPY "sb0.dd"
     68 #define SBLOCK1_COPY "sb1.dd"
     69 
     70 #define MP "/mp"
     71 #define UNCHANGED_CONTROL MP "/3-unchanged-control"
     72 #define TO_BE_DELETED     MP "/4-to-be-deleted"
     73 #define TO_BE_APPENDED    MP "/5-to-be-appended"
     74 #define NEWLY_CREATED     MP "/6-newly-created"
     75 
     76 long long sbaddr[2] = { -1, -1 };
     77 const char *sblock[2] = { SBLOCK0_COPY, SBLOCK1_COPY };
     78 
     79 ATF_TC_BODY(rfw32, tc)
     80 {
     81 	test(32);
     82 }
     83 
     84 ATF_TC_BODY(rfw64, tc)
     85 {
     86 	test(64);
     87 }
     88 
     89 void test(int width)
     90 {
     91 	struct ufs_args args;
     92 	FILE *pipe;
     93 	char buf[MAXLINE];
     94 	char cmd[MAXLINE];
     95 	int i;
     96 
     97 	setvbuf(stdout, NULL, _IONBF, 0);
     98 
     99 	/*
    100 	 * Initialize.
    101 	 */
    102 	if (width > 32) {
    103 		atf_tc_skip("fsck_lfs does not yet understand LFS64");
    104 		return;
    105 	}
    106 	atf_tc_expect_fail("roll-forward not yet implemented");
    107 
    108 	/* Create filesystem, note superblock locations */
    109 	fprintf(stderr, "* Create file system\n");
    110 	sprintf(cmd, "newfs_lfs -D -F -s 10000 -w%d ./%s > %s",
    111 		width, IMGNAME, LOGFILE);
    112 	if (system(cmd) == -1)
    113 		atf_tc_fail_errno("newfs failed");
    114 	pipe = fopen(LOGFILE, "r");
    115 	if (pipe == NULL)
    116 		atf_tc_fail_errno("newfs failed to execute");
    117 	while (fgets(buf, MAXLINE, pipe) != NULL) {
    118 		if (sscanf(buf, "%lld,%lld", sbaddr, sbaddr + 1) == 2)
    119 			break;
    120 	}
    121 	while (fgets(buf, MAXLINE, pipe) != NULL)
    122 		;
    123 	fclose(pipe);
    124 	if (sbaddr[0] < 0 || sbaddr[1] < 0)
    125 		atf_tc_fail("superblock not found");
    126 	fprintf(stderr, "* Superblocks at %lld and %lld\n",
    127 		sbaddr[0], sbaddr[1]);
    128 
    129 	/* Set up rump */
    130 	rump_init();
    131 	if (rump_sys_mkdir(MP, 0777) == -1)
    132 		atf_tc_fail_errno("cannot create mountpoint");
    133 	rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
    134 
    135 	/*
    136 	 * Create initial filesystem state, from which
    137 	 * we will attempt to roll forward.
    138 	 */
    139 
    140 	/* Mount filesystem */
    141 	fprintf(stderr, "* Mount fs [1, initial]\n");
    142 	memset(&args, 0, sizeof(args));
    143 	args.fspec = __UNCONST(FAKEBLK);
    144 	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
    145 		atf_tc_fail_errno("rump_sys_mount failed");
    146 
    147 	/* Payload */
    148 	fprintf(stderr, "* Initial payload\n");
    149 	write_file(UNCHANGED_CONTROL, CHUNKSIZE);
    150 	write_file(TO_BE_DELETED, CHUNKSIZE);
    151 	write_file(TO_BE_APPENDED, CHUNKSIZE);
    152 	rump_sys_sync();
    153 	rump_sys_sync();
    154 	sleep(1); /* XXX yuck - but we need the superblocks dirty */
    155 
    156 	/* Make copies of superblocks */
    157 	for (i = 0; i < 2; i++) {
    158 		sprintf(buf, "dd if=%s of=%s bs=512 iseek=%lld"
    159 			" count=16 conv=sync", IMGNAME, sblock[i], sbaddr[i]);
    160 		system(buf);
    161 	}
    162 
    163 	/* Unmount */
    164 	rump_sys_unmount(MP, 0);
    165 	if (fsck())
    166 		atf_tc_fail_errno("fsck found errors after first unmount");
    167 
    168 	/*
    169 	 * Make changes which we will attempt to roll forward.
    170 	 */
    171 
    172 	/* Reconfigure and mount filesystem again */
    173 	fprintf(stderr, "* Mount fs [2, after changes]\n");
    174 	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
    175 		atf_tc_fail_errno("rump_sys_mount failed [2]");
    176 
    177 	fprintf(stderr, "* Update payload\n");
    178 
    179 	/* Add new file */
    180 	write_file(NEWLY_CREATED, CHUNKSIZE);
    181 
    182 	/* Append to existing file */
    183 	write_file(TO_BE_APPENDED, CHUNKSIZE);
    184 
    185 	/* Delete file */
    186 	rump_sys_unlink(TO_BE_DELETED);
    187 
    188 	/* Done with payload, unmount fs */
    189 	rump_sys_unmount(MP, 0);
    190 	if (fsck())
    191 		atf_tc_fail_errno("fsck found errors after second unmount");
    192 
    193 #ifndef FORCE_SUCCESS
    194 	fprintf(stderr, "* Revert superblocks\n");
    195 	/*
    196 	 * Copy back old superblocks, reverting FS to old state
    197 	 */
    198 	for (i = 0; i < 2; i++) {
    199 		sprintf(buf, "dd of=%s if=%s bs=512 oseek=%lld count=16"
    200 			" conv=sync,notrunc", IMGNAME, sblock[i], sbaddr[i]);
    201 		system(buf);
    202 	}
    203 
    204 	if (fsck())
    205 		atf_tc_fail_errno("fsck found errors with old superblocks");
    206 #endif
    207 	dumplfs();
    208 
    209 	/*
    210 	 * Roll forward.
    211 	 */
    212 
    213 	/* Mount filesystem; this will roll forward. */
    214 	fprintf(stderr, "* Mount fs [3, to roll forward]\n");
    215 	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
    216 		atf_tc_fail_errno("rump_sys_mount failed [3]");
    217 
    218 	/* Unmount filesystem */
    219 	if (rump_sys_unmount(MP, 0) != 0)
    220 		atf_tc_fail_errno("rump_sys_umount failed after roll-forward");
    221 
    222 	/*
    223 	 * Use fsck_lfs to look for consistency errors.
    224 	 */
    225 
    226 	fprintf(stderr, "* Fsck after roll-forward\n");
    227 	if (fsck()) {
    228 		fprintf(stderr, "*** FAILED FSCK ***\n");
    229 		atf_tc_fail("fsck found errors after roll forward");
    230 	}
    231 	dumplfs();
    232 
    233 	/*
    234 	 * Check file system contents
    235 	 */
    236 
    237 	/* Mount filesystem one last time */
    238 	fprintf(stderr, "* Mount fs [4, after roll forward complete]\n");
    239 	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
    240 		atf_tc_fail_errno("rump_sys_mount failed [4]");
    241 
    242 	if (check_file(UNCHANGED_CONTROL, CHUNKSIZE) != 0)
    243 		atf_tc_fail("Unchanged control file differs(!)");
    244 
    245 	if (rump_sys_access(TO_BE_DELETED, F_OK) == 0)
    246 		atf_tc_fail("Removed file still present");
    247 	else
    248 		fprintf(stderr, "%s: no problem\n", TO_BE_DELETED);
    249 
    250 	if (check_file(TO_BE_APPENDED, 2 * CHUNKSIZE) != 0)
    251 		atf_tc_fail("Appended file differs");
    252 
    253 	if (rump_sys_access(NEWLY_CREATED, F_OK) != 0)
    254 		atf_tc_fail("Newly added file missing");
    255 
    256 	if (check_file(NEWLY_CREATED, CHUNKSIZE) != 0)
    257 		atf_tc_fail("Newly added file differs");
    258 
    259 	/* Umount filesystem */
    260 	rump_sys_unmount(MP, 0);
    261 
    262 	/* Final fsck to double check */
    263 	fprintf(stderr, "* Fsck after final unmount\n");
    264 	if (fsck()) {
    265 		fprintf(stderr, "*** FAILED FSCK ***\n");
    266 		atf_tc_fail("fsck found errors after final unmount");
    267 	}
    268 }
    269 
    270 ATF_TP_ADD_TCS(tp)
    271 {
    272 
    273 	ATF_TP_ADD_TC(tp, rfw32);
    274 	ATF_TP_ADD_TC(tp, rfw64);
    275 	return atf_no_error();
    276 }
    277 
    278 /* Write some data into a file */
    279 int write_file(const char *filename, int add)
    280 {
    281 	int fd, size, i;
    282 	struct stat statbuf;
    283 	unsigned char b;
    284 	int flags = O_CREAT|O_WRONLY;
    285 
    286 	if (rump_sys_stat(filename, &statbuf) < 0)
    287 		size = 0;
    288 	else {
    289 		size = statbuf.st_size;
    290 		flags |= O_APPEND;
    291 	}
    292 
    293 	fd = rump_sys_open(filename, flags);
    294 
    295 	for (i = 0; i < add; i++) {
    296 		b = ((unsigned)(size + i)) & 0xff;
    297 		rump_sys_write(fd, &b, 1);
    298 	}
    299 	rump_sys_close(fd);
    300 
    301 	return 0;
    302 }
    303 
    304 /* Check file's existence, size and contents */
    305 int check_file(const char *filename, int size)
    306 {
    307 	int fd, i;
    308 	struct stat statbuf;
    309 	unsigned char b;
    310 
    311 	if (rump_sys_stat(filename, &statbuf) < 0) {
    312 		fprintf(stderr, "%s: stat failed\n", filename);
    313 		return 1;
    314 	}
    315 	if (size != statbuf.st_size) {
    316 		fprintf(stderr, "%s: expected %d bytes, found %d\n",
    317 			filename, size, (int)statbuf.st_size);
    318 		return 2;
    319 	}
    320 
    321 	fd = rump_sys_open(filename, O_RDONLY);
    322 	for (i = 0; i < size; i++) {
    323 		rump_sys_read(fd, &b, 1);
    324 		if (b != (((unsigned)i) & 0xff)) {
    325 			fprintf(stderr, "%s: byte %d: expected %x found %x\n",
    326 				filename, i, ((unsigned)(i)) & 0xff, b);
    327 			rump_sys_close(fd);
    328 			return 3;
    329 		}
    330 	}
    331 	rump_sys_close(fd);
    332 	fprintf(stderr, "%s: no problem\n", filename);
    333 	return 0;
    334 }
    335 
    336 /* Run a file system consistency check */
    337 int fsck(void)
    338 {
    339 	char s[MAXLINE];
    340 	int i, errors = 0;
    341 	FILE *pipe;
    342 	char cmd[MAXLINE];
    343 
    344 	for (i = 0; i < 2; i++) {
    345 		sprintf(cmd, "fsck_lfs -n -b %jd -f " IMGNAME,
    346 			(intmax_t)sbaddr[i]);
    347 		pipe = popen(cmd, "r");
    348 		while (fgets(s, MAXLINE, pipe) != NULL) {
    349 			if (isdigit((int)s[0])) /* "5 files ... " */
    350 				continue;
    351 			if (isspace((int)s[0]) || s[0] == '*')
    352 				continue;
    353 			if (strncmp(s, "Alternate", 9) == 0)
    354 				continue;
    355 			if (strncmp(s, "ROLL ", 5) == 0)
    356 				continue;
    357 			fprintf(stderr, "FSCK[sb@%lld]: %s", sbaddr[i], s);
    358 			++errors;
    359 		}
    360 		pclose(pipe);
    361 		if (errors) {
    362 			break;
    363 		}
    364 	}
    365 
    366 	return errors;
    367 }
    368 
    369 /* Run dumplfs */
    370 void dumplfs(void)
    371 {
    372 #ifdef USE_DUMPLFS
    373 	char s[MAXLINE];
    374 	FILE *pipe;
    375 
    376 	pipe = popen("dumplfs -S -s 2 -s 1 -s 0 " IMGNAME, "r");
    377 	while (fgets(s, MAXLINE, pipe) != NULL)
    378 		fprintf(stderr, "DUMPLFS: %s", s);
    379 	pclose(pipe);
    380 #endif /* USE_DUMPLFS */
    381 }
    382