summaryrefslogtreecommitdiff
path: root/tests/fs/lfs/t_rfw.c
blob: 403eed9915254563e94de358fa725745a08ff2e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*	$NetBSD: t_rfw.c,v 1.3 2020/08/23 22:34:29 riastradh 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"

/* Debugging conditions */
/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */

/* Write a well-known byte pattern into a file, appending if it exists */
int write_file(const char *, int);

/* Check the byte pattern and size of the file */
int check_file(const char *, int);

/* Check the file system for consistency */
int fsck(void);

/* Actually run the test, differentiating the orphaned delete problem */
void test(int);

ATF_TC(rfw);
ATF_TC_HEAD(rfw, tc)
{
	atf_tc_set_md_var(tc, "descr",
		"LFS roll-forward creates an inconsistent filesystem");
	atf_tc_set_md_var(tc, "timeout", "20");
}

#define MAXLINE 132
#define CHUNKSIZE 300

#define IMGNAME "disk.img"
#define FAKEBLK "/dev/blk"
#define LOGFILE "newfs.log"
#define SBLOCK0_COPY "sb0.dd"
#define SBLOCK1_COPY "sb1.dd"

#define MP "/mp"
#define UNCHANGED_CONTROL MP "/3-unchanged-control"
#define TO_BE_DELETED     MP "/4-to-be-deleted"
#define TO_BE_APPENDED    MP "/5-to-be-appended"
#define NEWLY_CREATED     MP "/6-newly-created"

long long sbaddr[2] = { -1, -1 };
const char *sblock[2] = { SBLOCK0_COPY, SBLOCK1_COPY };

ATF_TC_BODY(rfw, tc)
{
	struct ufs_args args;
	FILE *pipe;
	char buf[MAXLINE];
	int i;

	setvbuf(stdout, NULL, _IONBF, 0);

	/*
	 * Initialize.
	 */
	atf_tc_expect_fail("roll-forward not yet implemented");

	/* Create filesystem, note superblock locations */
	fprintf(stderr, "* Create file system\n");
	if (system("newfs_lfs -D -F -s 10000 ./" IMGNAME " > " LOGFILE) == -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]);

	/* 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);

	/*
	 * Create initial filesystem state, from which
	 * we will attempt to roll forward.
	 */

	/* Mount filesystem */
	fprintf(stderr, "* Mount fs [1, initial]\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);
	write_file(TO_BE_DELETED, CHUNKSIZE);
	write_file(TO_BE_APPENDED, CHUNKSIZE);
	rump_sys_sync();
	rump_sys_sync();
	sleep(1); /* XXX yuck - but we need the superblocks dirty */

	/* Make copies of superblocks */
	for (i = 0; i < 2; i++) {
		sprintf(buf, "dd if=%s of=%s bs=512 iseek=%lld"
			" count=16 conv=sync", IMGNAME, sblock[i], sbaddr[i]);
		system(buf);
	}

	/* Unmount */
	rump_sys_unmount(MP, 0);

	/*
	 * Make changes which we will attempt to roll forward.
	 */

	/* Reconfigure and mount filesystem again */
	fprintf(stderr, "* Mount fs [2, after changes]\n");
	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
		atf_tc_fail_errno("rump_sys_mount failed [2]");

	/* Add new file */
	write_file(NEWLY_CREATED, CHUNKSIZE);

	/* Append to existing file */
	write_file(TO_BE_APPENDED, CHUNKSIZE);

	/* Delete file */
	rump_sys_unlink(TO_BE_DELETED);

	/* Done with payload, unmount fs */
	rump_sys_unmount(MP, 0);

#ifndef FORCE_SUCCESS
	/*
	 * Copy back old superblocks, reverting FS to old state
	 */
	for (i = 0; i < 2; i++) {
		sprintf(buf, "dd of=%s if=%s bs=512 oseek=%lld count=16"
			" conv=sync,notrunc", IMGNAME, sblock[i], sbaddr[i]);
		system(buf);
	}

	if (fsck())
		atf_tc_fail_errno("fsck found errors with old superblocks");
#endif

	/*
	 * Roll forward.
	 */

	/* Mount filesystem; this will roll forward. */
	fprintf(stderr, "* Mount fs [3, to roll forward]\n");
	if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
		atf_tc_fail_errno("rump_sys_mount failed [3]");

	/* Unmount filesystem */
	if (rump_sys_unmount(MP, 0) != 0)
		atf_tc_fail_errno("rump_sys_umount failed after roll-forward");

	/*
	 * Use fsck_lfs to look for consistency errors.
	 */

	if (fsck()) {
		fprintf(stderr, "*** FAILED FSCK ***\n");
		atf_tc_fail("fsck found errors after roll forward");
	}

	/*
	 * Check file system contents
	 */

	/* Mount filesystem one last time */
	fprintf(stderr, "* Mount fs [4, after roll forward complete]\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)
		atf_tc_fail("Unchanged control file differs(!)");

	if (check_file(TO_BE_APPENDED, 2 * CHUNKSIZE) != 0)
		atf_tc_fail("Appended file differs");

	if (rump_sys_access(NEWLY_CREATED, F_OK) != 0)
		atf_tc_fail("Newly added file missing");

	if (check_file(NEWLY_CREATED, CHUNKSIZE) != 0)
		atf_tc_fail("Newly added file differs");

	if (rump_sys_access(TO_BE_DELETED, F_OK) == 0)
		atf_tc_fail("Removed file still present");

	/* Umount filesystem */
	rump_sys_unmount(MP, 0);

	/* Final fsck to double check */
	if (fsck()) {
		fprintf(stderr, "*** FAILED FSCK ***\n");
		atf_tc_fail("fsck found errors after final unmount");
	}
}

ATF_TP_ADD_TCS(tp)
{

	ATF_TP_ADD_TC(tp, rfw);
	return atf_no_error();
}

/* Write some data into a file */
int write_file(const char *filename, int add)
{
	int fd, size, i;
	struct stat statbuf;
	unsigned char b;
	int flags = O_CREAT|O_WRONLY;

	if (rump_sys_stat(filename, &statbuf) < 0)
		size = 0;
	else {
		size = statbuf.st_size;
		flags |= O_APPEND;
	}

	fd = rump_sys_open(filename, flags);

	for (i = 0; i < add; i++) {
		b = ((unsigned)(size + i)) & 0xff;
		rump_sys_write(fd, &b, 1);
	}
	rump_sys_close(fd);

	return 0;
}

/* Check file's existence, size and contents */
int check_file(const char *filename, int size)
{
	int fd, i;
	struct stat statbuf;
	unsigned char b;

	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);
	for (i = 0; i < size; i++) {
		rump_sys_read(fd, &b, 1);
		if (b != (((unsigned)i) & 0xff)) {
			fprintf(stderr, "%s: byte %d: expected %x found %x\n",
				filename, i, ((unsigned)(i)) & 0xff, b);
			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 -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;
}