xref: /linux/init/initramfs_test.c (revision 83c0b27266ecfe8366d849be77ff6ff8f702ffed)
1*83c0b272SDavid Disseldorp // SPDX-License-Identifier: GPL-2.0
2*83c0b272SDavid Disseldorp #include <kunit/test.h>
3*83c0b272SDavid Disseldorp #include <linux/fcntl.h>
4*83c0b272SDavid Disseldorp #include <linux/file.h>
5*83c0b272SDavid Disseldorp #include <linux/fs.h>
6*83c0b272SDavid Disseldorp #include <linux/init_syscalls.h>
7*83c0b272SDavid Disseldorp #include <linux/stringify.h>
8*83c0b272SDavid Disseldorp #include <linux/timekeeping.h>
9*83c0b272SDavid Disseldorp #include "initramfs_internal.h"
10*83c0b272SDavid Disseldorp 
11*83c0b272SDavid Disseldorp struct initramfs_test_cpio {
12*83c0b272SDavid Disseldorp 	char *magic;
13*83c0b272SDavid Disseldorp 	unsigned int ino;
14*83c0b272SDavid Disseldorp 	unsigned int mode;
15*83c0b272SDavid Disseldorp 	unsigned int uid;
16*83c0b272SDavid Disseldorp 	unsigned int gid;
17*83c0b272SDavid Disseldorp 	unsigned int nlink;
18*83c0b272SDavid Disseldorp 	unsigned int mtime;
19*83c0b272SDavid Disseldorp 	unsigned int filesize;
20*83c0b272SDavid Disseldorp 	unsigned int devmajor;
21*83c0b272SDavid Disseldorp 	unsigned int devminor;
22*83c0b272SDavid Disseldorp 	unsigned int rdevmajor;
23*83c0b272SDavid Disseldorp 	unsigned int rdevminor;
24*83c0b272SDavid Disseldorp 	unsigned int namesize;
25*83c0b272SDavid Disseldorp 	unsigned int csum;
26*83c0b272SDavid Disseldorp 	char *fname;
27*83c0b272SDavid Disseldorp 	char *data;
28*83c0b272SDavid Disseldorp };
29*83c0b272SDavid Disseldorp 
30*83c0b272SDavid Disseldorp static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, char *out)
31*83c0b272SDavid Disseldorp {
32*83c0b272SDavid Disseldorp 	int i;
33*83c0b272SDavid Disseldorp 	size_t off = 0;
34*83c0b272SDavid Disseldorp 
35*83c0b272SDavid Disseldorp 	for (i = 0; i < csz; i++) {
36*83c0b272SDavid Disseldorp 		char *pos = &out[off];
37*83c0b272SDavid Disseldorp 		struct initramfs_test_cpio *c = &cs[i];
38*83c0b272SDavid Disseldorp 		size_t thislen;
39*83c0b272SDavid Disseldorp 
40*83c0b272SDavid Disseldorp 		/* +1 to account for nulterm */
41*83c0b272SDavid Disseldorp 		thislen = sprintf(pos, "%s"
42*83c0b272SDavid Disseldorp 			"%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x"
43*83c0b272SDavid Disseldorp 			"%s",
44*83c0b272SDavid Disseldorp 			c->magic, c->ino, c->mode, c->uid, c->gid, c->nlink,
45*83c0b272SDavid Disseldorp 			c->mtime, c->filesize, c->devmajor, c->devminor,
46*83c0b272SDavid Disseldorp 			c->rdevmajor, c->rdevminor, c->namesize, c->csum,
47*83c0b272SDavid Disseldorp 			c->fname) + 1;
48*83c0b272SDavid Disseldorp 		pr_debug("packing (%zu): %.*s\n", thislen, (int)thislen, pos);
49*83c0b272SDavid Disseldorp 		off += thislen;
50*83c0b272SDavid Disseldorp 		while (off & 3)
51*83c0b272SDavid Disseldorp 			out[off++] = '\0';
52*83c0b272SDavid Disseldorp 
53*83c0b272SDavid Disseldorp 		memcpy(&out[off], c->data, c->filesize);
54*83c0b272SDavid Disseldorp 		off += c->filesize;
55*83c0b272SDavid Disseldorp 		while (off & 3)
56*83c0b272SDavid Disseldorp 			out[off++] = '\0';
57*83c0b272SDavid Disseldorp 	}
58*83c0b272SDavid Disseldorp 
59*83c0b272SDavid Disseldorp 	return off;
60*83c0b272SDavid Disseldorp }
61*83c0b272SDavid Disseldorp 
62*83c0b272SDavid Disseldorp static void __init initramfs_test_extract(struct kunit *test)
63*83c0b272SDavid Disseldorp {
64*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf;
65*83c0b272SDavid Disseldorp 	size_t len;
66*83c0b272SDavid Disseldorp 	struct timespec64 ts_before, ts_after;
67*83c0b272SDavid Disseldorp 	struct kstat st = {};
68*83c0b272SDavid Disseldorp 	struct initramfs_test_cpio c[] = { {
69*83c0b272SDavid Disseldorp 		.magic = "070701",
70*83c0b272SDavid Disseldorp 		.ino = 1,
71*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
72*83c0b272SDavid Disseldorp 		.uid = 12,
73*83c0b272SDavid Disseldorp 		.gid = 34,
74*83c0b272SDavid Disseldorp 		.nlink = 1,
75*83c0b272SDavid Disseldorp 		.mtime = 56,
76*83c0b272SDavid Disseldorp 		.filesize = 0,
77*83c0b272SDavid Disseldorp 		.devmajor = 0,
78*83c0b272SDavid Disseldorp 		.devminor = 1,
79*83c0b272SDavid Disseldorp 		.rdevmajor = 0,
80*83c0b272SDavid Disseldorp 		.rdevminor = 0,
81*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_extract"),
82*83c0b272SDavid Disseldorp 		.csum = 0,
83*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_extract",
84*83c0b272SDavid Disseldorp 	}, {
85*83c0b272SDavid Disseldorp 		.magic = "070701",
86*83c0b272SDavid Disseldorp 		.ino = 2,
87*83c0b272SDavid Disseldorp 		.mode = S_IFDIR | 0777,
88*83c0b272SDavid Disseldorp 		.nlink = 1,
89*83c0b272SDavid Disseldorp 		.mtime = 57,
90*83c0b272SDavid Disseldorp 		.devminor = 1,
91*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_extract_dir"),
92*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_extract_dir",
93*83c0b272SDavid Disseldorp 	}, {
94*83c0b272SDavid Disseldorp 		.magic = "070701",
95*83c0b272SDavid Disseldorp 		.namesize = sizeof("TRAILER!!!"),
96*83c0b272SDavid Disseldorp 		.fname = "TRAILER!!!",
97*83c0b272SDavid Disseldorp 	} };
98*83c0b272SDavid Disseldorp 
99*83c0b272SDavid Disseldorp 	/* +3 to cater for any 4-byte end-alignment */
100*83c0b272SDavid Disseldorp 	cpio_srcbuf = kzalloc(ARRAY_SIZE(c) * (CPIO_HDRLEN + PATH_MAX + 3),
101*83c0b272SDavid Disseldorp 			      GFP_KERNEL);
102*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
103*83c0b272SDavid Disseldorp 
104*83c0b272SDavid Disseldorp 	ktime_get_real_ts64(&ts_before);
105*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
106*83c0b272SDavid Disseldorp 	ktime_get_real_ts64(&ts_after);
107*83c0b272SDavid Disseldorp 	if (err) {
108*83c0b272SDavid Disseldorp 		KUNIT_FAIL(test, "unpack failed %s", err);
109*83c0b272SDavid Disseldorp 		goto out;
110*83c0b272SDavid Disseldorp 	}
111*83c0b272SDavid Disseldorp 
112*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st, 0), 0);
113*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_TRUE(test, S_ISREG(st.mode));
114*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_TRUE(test, uid_eq(st.uid, KUIDT_INIT(c[0].uid)));
115*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_TRUE(test, gid_eq(st.gid, KGIDT_INIT(c[0].gid)));
116*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, st.nlink, 1);
117*83c0b272SDavid Disseldorp 	if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) {
118*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[0].mtime);
119*83c0b272SDavid Disseldorp 	} else {
120*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec);
121*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec);
122*83c0b272SDavid Disseldorp 	}
123*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, st.blocks, c[0].filesize);
124*83c0b272SDavid Disseldorp 
125*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st, 0), 0);
126*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_TRUE(test, S_ISDIR(st.mode));
127*83c0b272SDavid Disseldorp 	if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) {
128*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[1].mtime);
129*83c0b272SDavid Disseldorp 	} else {
130*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec);
131*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec);
132*83c0b272SDavid Disseldorp 	}
133*83c0b272SDavid Disseldorp 
134*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
135*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_rmdir(c[1].fname), 0);
136*83c0b272SDavid Disseldorp out:
137*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
138*83c0b272SDavid Disseldorp }
139*83c0b272SDavid Disseldorp 
140*83c0b272SDavid Disseldorp /*
141*83c0b272SDavid Disseldorp  * Don't terminate filename. Previously, the cpio filename field was passed
142*83c0b272SDavid Disseldorp  * directly to filp_open(collected, O_CREAT|..) without nulterm checks. See
143*83c0b272SDavid Disseldorp  * https://lore.kernel.org/linux-fsdevel/20241030035509.20194-2-ddiss@suse.de
144*83c0b272SDavid Disseldorp  */
145*83c0b272SDavid Disseldorp static void __init initramfs_test_fname_overrun(struct kunit *test)
146*83c0b272SDavid Disseldorp {
147*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf;
148*83c0b272SDavid Disseldorp 	size_t len, suffix_off;
149*83c0b272SDavid Disseldorp 	struct initramfs_test_cpio c[] = { {
150*83c0b272SDavid Disseldorp 		.magic = "070701",
151*83c0b272SDavid Disseldorp 		.ino = 1,
152*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
153*83c0b272SDavid Disseldorp 		.uid = 0,
154*83c0b272SDavid Disseldorp 		.gid = 0,
155*83c0b272SDavid Disseldorp 		.nlink = 1,
156*83c0b272SDavid Disseldorp 		.mtime = 1,
157*83c0b272SDavid Disseldorp 		.filesize = 0,
158*83c0b272SDavid Disseldorp 		.devmajor = 0,
159*83c0b272SDavid Disseldorp 		.devminor = 1,
160*83c0b272SDavid Disseldorp 		.rdevmajor = 0,
161*83c0b272SDavid Disseldorp 		.rdevminor = 0,
162*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_fname_overrun"),
163*83c0b272SDavid Disseldorp 		.csum = 0,
164*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_fname_overrun",
165*83c0b272SDavid Disseldorp 	} };
166*83c0b272SDavid Disseldorp 
167*83c0b272SDavid Disseldorp 	/*
168*83c0b272SDavid Disseldorp 	 * poison cpio source buffer, so we can detect overrun. source
169*83c0b272SDavid Disseldorp 	 * buffer is used by read_into() when hdr or fname
170*83c0b272SDavid Disseldorp 	 * are already available (e.g. no compression).
171*83c0b272SDavid Disseldorp 	 */
172*83c0b272SDavid Disseldorp 	cpio_srcbuf = kmalloc(CPIO_HDRLEN + PATH_MAX + 3, GFP_KERNEL);
173*83c0b272SDavid Disseldorp 	memset(cpio_srcbuf, 'B', CPIO_HDRLEN + PATH_MAX + 3);
174*83c0b272SDavid Disseldorp 	/* limit overrun to avoid crashes / filp_open() ENAMETOOLONG */
175*83c0b272SDavid Disseldorp 	cpio_srcbuf[CPIO_HDRLEN + strlen(c[0].fname) + 20] = '\0';
176*83c0b272SDavid Disseldorp 
177*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
178*83c0b272SDavid Disseldorp 	/* overwrite trailing fname terminator and padding */
179*83c0b272SDavid Disseldorp 	suffix_off = len - 1;
180*83c0b272SDavid Disseldorp 	while (cpio_srcbuf[suffix_off] == '\0') {
181*83c0b272SDavid Disseldorp 		cpio_srcbuf[suffix_off] = 'P';
182*83c0b272SDavid Disseldorp 		suffix_off--;
183*83c0b272SDavid Disseldorp 	}
184*83c0b272SDavid Disseldorp 
185*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
186*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NOT_NULL(test, err);
187*83c0b272SDavid Disseldorp 
188*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
189*83c0b272SDavid Disseldorp }
190*83c0b272SDavid Disseldorp 
191*83c0b272SDavid Disseldorp static void __init initramfs_test_data(struct kunit *test)
192*83c0b272SDavid Disseldorp {
193*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf;
194*83c0b272SDavid Disseldorp 	size_t len;
195*83c0b272SDavid Disseldorp 	struct file *file;
196*83c0b272SDavid Disseldorp 	struct initramfs_test_cpio c[] = { {
197*83c0b272SDavid Disseldorp 		.magic = "070701",
198*83c0b272SDavid Disseldorp 		.ino = 1,
199*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
200*83c0b272SDavid Disseldorp 		.uid = 0,
201*83c0b272SDavid Disseldorp 		.gid = 0,
202*83c0b272SDavid Disseldorp 		.nlink = 1,
203*83c0b272SDavid Disseldorp 		.mtime = 1,
204*83c0b272SDavid Disseldorp 		.filesize = sizeof("ASDF") - 1,
205*83c0b272SDavid Disseldorp 		.devmajor = 0,
206*83c0b272SDavid Disseldorp 		.devminor = 1,
207*83c0b272SDavid Disseldorp 		.rdevmajor = 0,
208*83c0b272SDavid Disseldorp 		.rdevminor = 0,
209*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_data"),
210*83c0b272SDavid Disseldorp 		.csum = 0,
211*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_data",
212*83c0b272SDavid Disseldorp 		.data = "ASDF",
213*83c0b272SDavid Disseldorp 	} };
214*83c0b272SDavid Disseldorp 
215*83c0b272SDavid Disseldorp 	/* +6 for max name and data 4-byte padding */
216*83c0b272SDavid Disseldorp 	cpio_srcbuf = kmalloc(CPIO_HDRLEN + c[0].namesize + c[0].filesize + 6,
217*83c0b272SDavid Disseldorp 			      GFP_KERNEL);
218*83c0b272SDavid Disseldorp 
219*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
220*83c0b272SDavid Disseldorp 
221*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
222*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NULL(test, err);
223*83c0b272SDavid Disseldorp 
224*83c0b272SDavid Disseldorp 	file = filp_open(c[0].fname, O_RDONLY, 0);
225*83c0b272SDavid Disseldorp 	if (IS_ERR(file)) {
226*83c0b272SDavid Disseldorp 		KUNIT_FAIL(test, "open failed");
227*83c0b272SDavid Disseldorp 		goto out;
228*83c0b272SDavid Disseldorp 	}
229*83c0b272SDavid Disseldorp 
230*83c0b272SDavid Disseldorp 	/* read back file contents into @cpio_srcbuf and confirm match */
231*83c0b272SDavid Disseldorp 	len = kernel_read(file, cpio_srcbuf, c[0].filesize, NULL);
232*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, len, c[0].filesize);
233*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_MEMEQ(test, cpio_srcbuf, c[0].data, len);
234*83c0b272SDavid Disseldorp 
235*83c0b272SDavid Disseldorp 	fput(file);
236*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
237*83c0b272SDavid Disseldorp out:
238*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
239*83c0b272SDavid Disseldorp }
240*83c0b272SDavid Disseldorp 
241*83c0b272SDavid Disseldorp static void __init initramfs_test_csum(struct kunit *test)
242*83c0b272SDavid Disseldorp {
243*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf;
244*83c0b272SDavid Disseldorp 	size_t len;
245*83c0b272SDavid Disseldorp 	struct initramfs_test_cpio c[] = { {
246*83c0b272SDavid Disseldorp 		/* 070702 magic indicates a valid csum is present */
247*83c0b272SDavid Disseldorp 		.magic = "070702",
248*83c0b272SDavid Disseldorp 		.ino = 1,
249*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
250*83c0b272SDavid Disseldorp 		.nlink = 1,
251*83c0b272SDavid Disseldorp 		.filesize = sizeof("ASDF") - 1,
252*83c0b272SDavid Disseldorp 		.devminor = 1,
253*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_csum"),
254*83c0b272SDavid Disseldorp 		.csum = 'A' + 'S' + 'D' + 'F',
255*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_csum",
256*83c0b272SDavid Disseldorp 		.data = "ASDF",
257*83c0b272SDavid Disseldorp 	}, {
258*83c0b272SDavid Disseldorp 		/* mix csum entry above with no-csum entry below */
259*83c0b272SDavid Disseldorp 		.magic = "070701",
260*83c0b272SDavid Disseldorp 		.ino = 2,
261*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
262*83c0b272SDavid Disseldorp 		.nlink = 1,
263*83c0b272SDavid Disseldorp 		.filesize = sizeof("ASDF") - 1,
264*83c0b272SDavid Disseldorp 		.devminor = 1,
265*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_csum_not_here"),
266*83c0b272SDavid Disseldorp 		/* csum ignored */
267*83c0b272SDavid Disseldorp 		.csum = 5555,
268*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_csum_not_here",
269*83c0b272SDavid Disseldorp 		.data = "ASDF",
270*83c0b272SDavid Disseldorp 	} };
271*83c0b272SDavid Disseldorp 
272*83c0b272SDavid Disseldorp 	cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
273*83c0b272SDavid Disseldorp 
274*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
275*83c0b272SDavid Disseldorp 
276*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
277*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NULL(test, err);
278*83c0b272SDavid Disseldorp 
279*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
280*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0);
281*83c0b272SDavid Disseldorp 
282*83c0b272SDavid Disseldorp 	/* mess up the csum and confirm that unpack fails */
283*83c0b272SDavid Disseldorp 	c[0].csum--;
284*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
285*83c0b272SDavid Disseldorp 
286*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
287*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NOT_NULL(test, err);
288*83c0b272SDavid Disseldorp 
289*83c0b272SDavid Disseldorp 	/*
290*83c0b272SDavid Disseldorp 	 * file (with content) is still retained in case of bad-csum abort.
291*83c0b272SDavid Disseldorp 	 * Perhaps we should change this.
292*83c0b272SDavid Disseldorp 	 */
293*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
294*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), -ENOENT);
295*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
296*83c0b272SDavid Disseldorp }
297*83c0b272SDavid Disseldorp 
298*83c0b272SDavid Disseldorp /*
299*83c0b272SDavid Disseldorp  * hardlink hashtable may leak when the archive omits a trailer:
300*83c0b272SDavid Disseldorp  * https://lore.kernel.org/r/20241107002044.16477-10-ddiss@suse.de/
301*83c0b272SDavid Disseldorp  */
302*83c0b272SDavid Disseldorp static void __init initramfs_test_hardlink(struct kunit *test)
303*83c0b272SDavid Disseldorp {
304*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf;
305*83c0b272SDavid Disseldorp 	size_t len;
306*83c0b272SDavid Disseldorp 	struct kstat st0, st1;
307*83c0b272SDavid Disseldorp 	struct initramfs_test_cpio c[] = { {
308*83c0b272SDavid Disseldorp 		.magic = "070701",
309*83c0b272SDavid Disseldorp 		.ino = 1,
310*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
311*83c0b272SDavid Disseldorp 		.nlink = 2,
312*83c0b272SDavid Disseldorp 		.devminor = 1,
313*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_hardlink"),
314*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_hardlink",
315*83c0b272SDavid Disseldorp 	}, {
316*83c0b272SDavid Disseldorp 		/* hardlink data is present in last archive entry */
317*83c0b272SDavid Disseldorp 		.magic = "070701",
318*83c0b272SDavid Disseldorp 		.ino = 1,
319*83c0b272SDavid Disseldorp 		.mode = S_IFREG | 0777,
320*83c0b272SDavid Disseldorp 		.nlink = 2,
321*83c0b272SDavid Disseldorp 		.filesize = sizeof("ASDF") - 1,
322*83c0b272SDavid Disseldorp 		.devminor = 1,
323*83c0b272SDavid Disseldorp 		.namesize = sizeof("initramfs_test_hardlink_link"),
324*83c0b272SDavid Disseldorp 		.fname = "initramfs_test_hardlink_link",
325*83c0b272SDavid Disseldorp 		.data = "ASDF",
326*83c0b272SDavid Disseldorp 	} };
327*83c0b272SDavid Disseldorp 
328*83c0b272SDavid Disseldorp 	cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
329*83c0b272SDavid Disseldorp 
330*83c0b272SDavid Disseldorp 	len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
331*83c0b272SDavid Disseldorp 
332*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
333*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NULL(test, err);
334*83c0b272SDavid Disseldorp 
335*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st0, 0), 0);
336*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st1, 0), 0);
337*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, st0.ino, st1.ino);
338*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, st0.nlink, 2);
339*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, st1.nlink, 2);
340*83c0b272SDavid Disseldorp 
341*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0);
342*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0);
343*83c0b272SDavid Disseldorp 
344*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
345*83c0b272SDavid Disseldorp }
346*83c0b272SDavid Disseldorp 
347*83c0b272SDavid Disseldorp #define INITRAMFS_TEST_MANY_LIMIT 1000
348*83c0b272SDavid Disseldorp #define INITRAMFS_TEST_MANY_PATH_MAX (sizeof("initramfs_test_many-") \
349*83c0b272SDavid Disseldorp 			+ sizeof(__stringify(INITRAMFS_TEST_MANY_LIMIT)))
350*83c0b272SDavid Disseldorp static void __init initramfs_test_many(struct kunit *test)
351*83c0b272SDavid Disseldorp {
352*83c0b272SDavid Disseldorp 	char *err, *cpio_srcbuf, *p;
353*83c0b272SDavid Disseldorp 	size_t len = INITRAMFS_TEST_MANY_LIMIT *
354*83c0b272SDavid Disseldorp 		     (CPIO_HDRLEN + INITRAMFS_TEST_MANY_PATH_MAX + 3);
355*83c0b272SDavid Disseldorp 	char thispath[INITRAMFS_TEST_MANY_PATH_MAX];
356*83c0b272SDavid Disseldorp 	int i;
357*83c0b272SDavid Disseldorp 
358*83c0b272SDavid Disseldorp 	p = cpio_srcbuf = kmalloc(len, GFP_KERNEL);
359*83c0b272SDavid Disseldorp 
360*83c0b272SDavid Disseldorp 	for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) {
361*83c0b272SDavid Disseldorp 		struct initramfs_test_cpio c = {
362*83c0b272SDavid Disseldorp 			.magic = "070701",
363*83c0b272SDavid Disseldorp 			.ino = i,
364*83c0b272SDavid Disseldorp 			.mode = S_IFREG | 0777,
365*83c0b272SDavid Disseldorp 			.nlink = 1,
366*83c0b272SDavid Disseldorp 			.devminor = 1,
367*83c0b272SDavid Disseldorp 			.fname = thispath,
368*83c0b272SDavid Disseldorp 		};
369*83c0b272SDavid Disseldorp 
370*83c0b272SDavid Disseldorp 		c.namesize = 1 + sprintf(thispath, "initramfs_test_many-%d", i);
371*83c0b272SDavid Disseldorp 		p += fill_cpio(&c, 1, p);
372*83c0b272SDavid Disseldorp 	}
373*83c0b272SDavid Disseldorp 
374*83c0b272SDavid Disseldorp 	len = p - cpio_srcbuf;
375*83c0b272SDavid Disseldorp 	err = unpack_to_rootfs(cpio_srcbuf, len);
376*83c0b272SDavid Disseldorp 	KUNIT_EXPECT_NULL(test, err);
377*83c0b272SDavid Disseldorp 
378*83c0b272SDavid Disseldorp 	for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) {
379*83c0b272SDavid Disseldorp 		sprintf(thispath, "initramfs_test_many-%d", i);
380*83c0b272SDavid Disseldorp 		KUNIT_EXPECT_EQ(test, init_unlink(thispath), 0);
381*83c0b272SDavid Disseldorp 	}
382*83c0b272SDavid Disseldorp 
383*83c0b272SDavid Disseldorp 	kfree(cpio_srcbuf);
384*83c0b272SDavid Disseldorp }
385*83c0b272SDavid Disseldorp 
386*83c0b272SDavid Disseldorp /*
387*83c0b272SDavid Disseldorp  * The kunit_case/_suite struct cannot be marked as __initdata as this will be
388*83c0b272SDavid Disseldorp  * used in debugfs to retrieve results after test has run.
389*83c0b272SDavid Disseldorp  */
390*83c0b272SDavid Disseldorp static struct kunit_case __refdata initramfs_test_cases[] = {
391*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_extract),
392*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_fname_overrun),
393*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_data),
394*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_csum),
395*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_hardlink),
396*83c0b272SDavid Disseldorp 	KUNIT_CASE(initramfs_test_many),
397*83c0b272SDavid Disseldorp 	{},
398*83c0b272SDavid Disseldorp };
399*83c0b272SDavid Disseldorp 
400*83c0b272SDavid Disseldorp static struct kunit_suite initramfs_test_suite = {
401*83c0b272SDavid Disseldorp 	.name = "initramfs",
402*83c0b272SDavid Disseldorp 	.test_cases = initramfs_test_cases,
403*83c0b272SDavid Disseldorp };
404*83c0b272SDavid Disseldorp kunit_test_init_section_suites(&initramfs_test_suite);
405*83c0b272SDavid Disseldorp 
406*83c0b272SDavid Disseldorp MODULE_DESCRIPTION("Initramfs KUnit test suite");
407*83c0b272SDavid Disseldorp MODULE_LICENSE("GPL v2");
408