• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2024 Cyril Hrubis <chrubis@suse.cz>
4  */
5 #include <sys/mount.h>
6 
7 #define TST_NO_DEFAULT_MAIN
8 #include "tst_test.h"
9 #include "tst_safe_stdio.h"
10 #include "ujson.h"
11 
12 static char *shell_filename;
13 
run_shell(void)14 static void run_shell(void)
15 {
16 	tst_run_script(shell_filename, NULL);
17 }
18 
run_shell_tcnt(unsigned int n)19 static void run_shell_tcnt(unsigned int n)
20 {
21 	char buf[128];
22 	char *const params[] = {buf, NULL};
23 
24 	snprintf(buf, sizeof(buf), "%u", n);
25 
26 	tst_run_script(shell_filename, params);
27 }
28 
29 static struct tst_test test = {
30 	.runs_script = 1,
31 };
32 
print_help(void)33 static void print_help(void)
34 {
35 	printf("Usage: tst_shell_loader ltp_shell_test.sh ...\n");
36 }
37 
38 static char *metadata;
39 static size_t metadata_size;
40 static size_t metadata_used;
41 
metadata_append(const char * line)42 static void metadata_append(const char *line)
43 {
44 	size_t linelen = strlen(line);
45 
46 	if (metadata_size - metadata_used < linelen + 1) {
47 		metadata_size += 4096;
48 		metadata = SAFE_REALLOC(metadata, metadata_size);
49 	}
50 
51 	strcpy(metadata + metadata_used, line);
52 	metadata_used += linelen;
53 }
54 
55 enum test_attr_ids {
56 	ALL_FILESYSTEMS,
57 	DEV_MIN_SIZE,
58 	FILESYSTEMS,
59 	FORMAT_DEVICE,
60 	MIN_CPUS,
61 	MIN_MEM_AVAIL,
62 	MIN_KVER,
63 	MIN_SWAP_AVAIL,
64 	MNTPOINT,
65 	MOUNT_DEVICE,
66 	NEEDS_ABI_BITS,
67 	NEEDS_CMDS,
68 	NEEDS_DEVFS,
69 	NEEDS_DEVICE,
70 	NEEDS_DRIVERS,
71 	NEEDS_HUGETLBFS,
72 	NEEDS_KCONFIGS,
73 	NEEDS_ROFS,
74 	NEEDS_ROOT,
75 	NEEDS_TMPDIR,
76 	RESTORE_WALLCLOCK,
77 	SAVE_RESTORE,
78 	SKIP_FILESYSTEMS,
79 	SKIP_IN_COMPAT,
80 	SKIP_IN_LOCKDOWN,
81 	SKIP_IN_SECUREBOOT,
82 	SUPPORTED_ARCHS,
83 	TAGS,
84 	TAINT_CHECK,
85 	TCNT,
86 };
87 
88 static ujson_obj_attr test_attrs[] = {
89 	UJSON_OBJ_ATTR_IDX(ALL_FILESYSTEMS, "all_filesystems", UJSON_BOOL),
90 	UJSON_OBJ_ATTR_IDX(DEV_MIN_SIZE, "dev_min_size", UJSON_INT),
91 	UJSON_OBJ_ATTR_IDX(FILESYSTEMS, "filesystems", UJSON_ARR),
92 	UJSON_OBJ_ATTR_IDX(FORMAT_DEVICE, "format_device", UJSON_BOOL),
93 	UJSON_OBJ_ATTR_IDX(MIN_CPUS, "min_cpus", UJSON_INT),
94 	UJSON_OBJ_ATTR_IDX(MIN_MEM_AVAIL, "min_mem_avail", UJSON_INT),
95 	UJSON_OBJ_ATTR_IDX(MIN_KVER, "min_kver", UJSON_STR),
96 	UJSON_OBJ_ATTR_IDX(MIN_SWAP_AVAIL, "min_swap_avail", UJSON_INT),
97 	UJSON_OBJ_ATTR_IDX(MNTPOINT, "mntpoint", UJSON_STR),
98 	UJSON_OBJ_ATTR_IDX(MOUNT_DEVICE, "mount_device", UJSON_BOOL),
99 	UJSON_OBJ_ATTR_IDX(NEEDS_ABI_BITS, "needs_abi_bits", UJSON_INT),
100 	UJSON_OBJ_ATTR_IDX(NEEDS_CMDS, "needs_cmds", UJSON_ARR),
101 	UJSON_OBJ_ATTR_IDX(NEEDS_DEVFS, "needs_devfs", UJSON_BOOL),
102 	UJSON_OBJ_ATTR_IDX(NEEDS_DEVICE, "needs_device", UJSON_BOOL),
103 	UJSON_OBJ_ATTR_IDX(NEEDS_DRIVERS, "needs_drivers", UJSON_ARR),
104 	UJSON_OBJ_ATTR_IDX(NEEDS_HUGETLBFS, "needs_hugetlbfs", UJSON_BOOL),
105 	UJSON_OBJ_ATTR_IDX(NEEDS_KCONFIGS, "needs_kconfigs", UJSON_ARR),
106 	UJSON_OBJ_ATTR_IDX(NEEDS_ROFS, "needs_rofs", UJSON_BOOL),
107 	UJSON_OBJ_ATTR_IDX(NEEDS_ROOT, "needs_root", UJSON_BOOL),
108 	UJSON_OBJ_ATTR_IDX(NEEDS_TMPDIR, "needs_tmpdir", UJSON_BOOL),
109 	UJSON_OBJ_ATTR_IDX(RESTORE_WALLCLOCK, "restore_wallclock", UJSON_BOOL),
110 	UJSON_OBJ_ATTR_IDX(SAVE_RESTORE, "save_restore", UJSON_ARR),
111 	UJSON_OBJ_ATTR_IDX(SKIP_FILESYSTEMS, "skip_filesystems", UJSON_ARR),
112 	UJSON_OBJ_ATTR_IDX(SKIP_IN_COMPAT, "skip_in_compat", UJSON_BOOL),
113 	UJSON_OBJ_ATTR_IDX(SKIP_IN_LOCKDOWN, "skip_in_lockdown", UJSON_BOOL),
114 	UJSON_OBJ_ATTR_IDX(SKIP_IN_SECUREBOOT, "skip_in_secureboot", UJSON_BOOL),
115 	UJSON_OBJ_ATTR_IDX(SUPPORTED_ARCHS, "supported_archs", UJSON_ARR),
116 	UJSON_OBJ_ATTR_IDX(TAGS, "tags", UJSON_ARR),
117 	UJSON_OBJ_ATTR_IDX(TAINT_CHECK, "taint_check", UJSON_BOOL),
118 	UJSON_OBJ_ATTR_IDX(TCNT, "tcnt", UJSON_INT)
119 };
120 
121 static ujson_obj test_obj = {
122 	.attrs = test_attrs,
123 	.attr_cnt = UJSON_ARRAY_SIZE(test_attrs),
124 };
125 
parse_strarr(ujson_reader * reader,ujson_val * val)126 static const char *const *parse_strarr(ujson_reader *reader, ujson_val *val)
127 {
128 	unsigned int cnt = 0, i = 0;
129 	char **ret;
130 
131 	ujson_reader_state state = ujson_reader_state_save(reader);
132 
133 	UJSON_ARR_FOREACH(reader, val) {
134 		if (val->type != UJSON_STR) {
135 			ujson_err(reader, "Expected string!");
136 			return NULL;
137 		}
138 
139 		cnt++;
140 	}
141 
142 	ujson_reader_state_load(reader, state);
143 
144 	ret = SAFE_MALLOC(sizeof(char *) * (cnt + 1));
145 
146 	UJSON_ARR_FOREACH(reader, val) {
147 		ret[i++] = strdup(val->val_str);
148 	}
149 
150 	ret[i] = NULL;
151 
152 	return (const char *const *)ret;
153 }
154 
155 enum fs_ids {
156 	FS_MIN_KVER,
157 	MKFS_OPTS,
158 	MKFS_SIZE_OPT,
159 	MKFS_VER,
160 	MNT_FLAGS,
161 	TYPE,
162 };
163 
164 static ujson_obj_attr fs_attrs[] = {
165 	UJSON_OBJ_ATTR_IDX(FS_MIN_KVER, "min_kver", UJSON_STR),
166 	UJSON_OBJ_ATTR_IDX(MKFS_OPTS, "mkfs_opts", UJSON_ARR),
167 	UJSON_OBJ_ATTR_IDX(MKFS_SIZE_OPT, "mkfs_size_opt", UJSON_STR),
168 	UJSON_OBJ_ATTR_IDX(MKFS_VER, "mkfs_ver", UJSON_STR),
169 	UJSON_OBJ_ATTR_IDX(MNT_FLAGS, "mnt_flags", UJSON_ARR),
170 	UJSON_OBJ_ATTR_IDX(TYPE, "type", UJSON_STR),
171 };
172 
173 static ujson_obj fs_obj = {
174 	.attrs = fs_attrs,
175 	.attr_cnt = UJSON_ARRAY_SIZE(fs_attrs),
176 };
177 
parse_mnt_flags(ujson_reader * reader,ujson_val * val)178 static int parse_mnt_flags(ujson_reader *reader, ujson_val *val)
179 {
180 	int ret = 0;
181 
182 	UJSON_ARR_FOREACH(reader, val) {
183 		if (val->type != UJSON_STR) {
184 			ujson_err(reader, "Expected string!");
185 			return ret;
186 		}
187 
188 		if (!strcmp(val->val_str, "RDONLY"))
189 			ret |= MS_RDONLY;
190 		else if (!strcmp(val->val_str, "NOATIME"))
191 			ret |= MS_NOATIME;
192 		else if (!strcmp(val->val_str, "NOEXEC"))
193 			ret |= MS_NOEXEC;
194 		else if (!strcmp(val->val_str, "NOSUID"))
195 			ret |= MS_NOSUID;
196 		else
197 			ujson_err(reader, "Invalid mount flag");
198 	}
199 
200 	return ret;
201 }
202 
parse_filesystems(ujson_reader * reader,ujson_val * val)203 static struct tst_fs *parse_filesystems(ujson_reader *reader, ujson_val *val)
204 {
205 	unsigned int i = 0, cnt = 0;
206 	struct tst_fs *ret;
207 
208 	ujson_reader_state state = ujson_reader_state_save(reader);
209 
210 	UJSON_ARR_FOREACH(reader, val) {
211 		if (val->type != UJSON_OBJ) {
212 			ujson_err(reader, "Expected object!");
213 			return NULL;
214 		}
215 		ujson_obj_skip(reader);
216 		cnt++;
217 	}
218 
219 	ujson_reader_state_load(reader, state);
220 
221 	ret = SAFE_MALLOC(sizeof(struct tst_fs) * (cnt + 1));
222 	memset(ret, 0, sizeof(*ret) * (cnt+1));
223 
224 	UJSON_ARR_FOREACH(reader, val) {
225 		UJSON_OBJ_FOREACH_FILTER(reader, val, &fs_obj, ujson_empty_obj) {
226 			switch ((enum fs_ids)val->idx) {
227 			case MKFS_OPTS:
228 				ret[i].mkfs_opts = parse_strarr(reader, val);
229 			break;
230 			case MKFS_SIZE_OPT:
231 				ret[i].mkfs_size_opt = strdup(val->val_str);
232 			break;
233 			case MKFS_VER:
234 				ret[i].mkfs_ver = strdup(val->val_str);
235 			break;
236 			case MNT_FLAGS:
237 				ret[i].mnt_flags = parse_mnt_flags(reader, val);
238 			break;
239 			case TYPE:
240 				ret[i].type = strdup(val->val_str);
241 			break;
242 			case FS_MIN_KVER:
243 				ret[i].min_kver = strdup(val->val_str);
244 			break;
245 			}
246 
247 		}
248 
249 		i++;
250 	}
251 
252 	return ret;
253 }
254 
parse_tags(ujson_reader * reader,ujson_val * val)255 static struct tst_tag *parse_tags(ujson_reader *reader, ujson_val *val)
256 {
257 	unsigned int i = 0, cnt = 0;
258 	struct tst_tag *ret;
259 
260 	ujson_reader_state state = ujson_reader_state_save(reader);
261 
262 	UJSON_ARR_FOREACH(reader, val) {
263 		if (val->type != UJSON_ARR) {
264 			ujson_err(reader, "Expected array!");
265 			return NULL;
266 		}
267 		ujson_arr_skip(reader);
268 		cnt++;
269 	}
270 
271 	ujson_reader_state_load(reader, state);
272 
273 	ret = SAFE_MALLOC(sizeof(struct tst_tag) * (cnt + 1));
274 	memset(&ret[cnt], 0, sizeof(ret[cnt]));
275 
276 	UJSON_ARR_FOREACH(reader, val) {
277 		char *name = NULL;
278 		char *value = NULL;
279 
280 		UJSON_ARR_FOREACH(reader, val) {
281 			if (val->type != UJSON_STR) {
282 				ujson_err(reader, "Expected string!");
283 				return NULL;
284 			}
285 
286 			if (!name) {
287 				name = strdup(val->val_str);
288 			} else if (!value) {
289 				value = strdup(val->val_str);
290 			} else {
291 				ujson_err(reader, "Expected only two members!");
292 				return NULL;
293 			}
294 		}
295 
296 		ret[i].name = name;
297 		ret[i].value = value;
298 		i++;
299 	}
300 
301 	return ret;
302 }
303 
parse_save_restore(ujson_reader * reader,ujson_val * val)304 static struct tst_path_val *parse_save_restore(ujson_reader *reader, ujson_val *val)
305 {
306 	unsigned int i = 0, cnt = 0;
307 	struct tst_path_val *ret;
308 
309 	ujson_reader_state state = ujson_reader_state_save(reader);
310 
311 	UJSON_ARR_FOREACH(reader, val) {
312 		if (val->type != UJSON_ARR) {
313 			ujson_err(reader, "Expected array!");
314 			return NULL;
315 		}
316 		ujson_arr_skip(reader);
317 		cnt++;
318 	}
319 
320 	ujson_reader_state_load(reader, state);
321 
322 	ret = SAFE_MALLOC(sizeof(struct tst_path_val) * (cnt + 1));
323 	memset(&ret[cnt], 0, sizeof(ret[cnt]));
324 
325 	UJSON_ARR_FOREACH(reader, val) {
326 		char *path = NULL;
327 		char *pval = NULL;
328 		int flags_set = 0;
329 		int val_set = 0;
330 		unsigned int flags = 0;
331 
332 		UJSON_ARR_FOREACH(reader, val) {
333 			if (!path) {
334 				if (val->type != UJSON_STR) {
335 					ujson_err(reader, "Expected string!");
336 					return NULL;
337 				}
338 
339 				path = strdup(val->val_str);
340 			} else if (!val_set) {
341 				if (val->type == UJSON_STR) {
342 					pval = strdup(val->val_str);
343 				} else if (val->type != UJSON_NULL) {
344 					ujson_err(reader, "Expected string or NULL!");
345 					return NULL;
346 				}
347 				val_set = 1;
348 			} else if (!flags_set) {
349 				if (val->type != UJSON_STR) {
350 					ujson_err(reader, "Expected string!");
351 					return NULL;
352 				}
353 
354 				if (!strcmp(val->val_str, "TCONF")) {
355 					flags = TST_SR_TCONF;
356 				} else if (!strcmp(val->val_str, "TBROK")) {
357 					flags = TST_SR_TBROK;
358 				} else if (!strcmp(val->val_str, "SKIP")) {
359 					flags = TST_SR_SKIP;
360 				} else {
361 					ujson_err(reader, "Invalid flags!");
362 					return NULL;
363 				}
364 
365 				flags_set = 1;
366 			} else {
367 				ujson_err(reader, "Expected only two members!");
368 				return NULL;
369 			}
370 		}
371 
372 		if (!path || !flags_set) {
373 			ujson_err(reader, "Expected [\"/{proc,sys}/path\", {\"TCONF\", \"TBROK\", \"TSKIP\"}]!");
374 			return NULL;
375 		}
376 
377 		ret[i].path = path;
378 		ret[i].val = pval;
379 		ret[i].flags = flags;
380 		i++;
381 	}
382 
383 	return ret;
384 }
385 
parse_metadata(void)386 static void parse_metadata(void)
387 {
388 	ujson_reader reader = UJSON_READER_INIT(metadata, metadata_used, UJSON_READER_STRICT);
389 	char str_buf[128];
390 	ujson_val val = UJSON_VAL_INIT(str_buf, sizeof(str_buf));
391 
392 	UJSON_OBJ_FOREACH_FILTER(&reader, &val, &test_obj, ujson_empty_obj) {
393 		switch ((enum test_attr_ids)val.idx) {
394 		case ALL_FILESYSTEMS:
395 			test.all_filesystems = val.val_bool;
396 		break;
397 		case DEV_MIN_SIZE:
398 			if (val.val_int <= 0)
399 				ujson_err(&reader, "Device size must be > 0");
400 			else
401 				test.dev_min_size = val.val_int;
402 		break;
403 		case FILESYSTEMS:
404 			test.filesystems = parse_filesystems(&reader, &val);
405 		break;
406 		case FORMAT_DEVICE:
407 			test.format_device = val.val_bool;
408 		break;
409 		case MIN_CPUS:
410 			if (val.val_int <= 0)
411 				ujson_err(&reader, "Minimal number of cpus must be > 0");
412 			else
413 				test.min_cpus = val.val_int;
414 		break;
415 		case MIN_MEM_AVAIL:
416 			if (val.val_int <= 0)
417 				ujson_err(&reader, "Minimal available memory size must be > 0");
418 			else
419 				test.min_mem_avail = val.val_int;
420 		break;
421 		case MIN_KVER:
422 			test.min_kver = strdup(val.val_str);
423 		break;
424 		case MIN_SWAP_AVAIL:
425 			if (val.val_int <= 0)
426 				ujson_err(&reader, "Minimal available swap size must be > 0");
427 			else
428 				test.min_swap_avail = val.val_int;
429 		break;
430 		case MNTPOINT:
431 			test.mntpoint = strdup(val.val_str);
432 		break;
433 		case MOUNT_DEVICE:
434 			test.mount_device = val.val_bool;
435 		break;
436 		case NEEDS_ABI_BITS:
437 			if (val.val_int == 32 || val.val_int == 64)
438 				test.needs_abi_bits = val.val_int;
439 			else
440 				ujson_err(&reader, "ABI bits must be 32 or 64");
441 		break;
442 		case NEEDS_CMDS:
443 			test.needs_cmds = parse_strarr(&reader, &val);
444 		break;
445 		case NEEDS_DEVFS:
446 			test.needs_devfs = val.val_bool;
447 		break;
448 		case NEEDS_DEVICE:
449 			test.needs_device = val.val_bool;
450 		break;
451 		case NEEDS_DRIVERS:
452 			test.needs_drivers = parse_strarr(&reader, &val);
453 		break;
454 		case NEEDS_HUGETLBFS:
455 			test.needs_hugetlbfs = val.val_bool;
456 		break;
457 		case NEEDS_KCONFIGS:
458 			test.needs_kconfigs = parse_strarr(&reader, &val);
459 		break;
460 		case NEEDS_ROFS:
461 			test.needs_rofs = val.val_bool;
462 		break;
463 		case NEEDS_ROOT:
464 			test.needs_root = val.val_bool;
465 		break;
466 		case NEEDS_TMPDIR:
467 			test.needs_tmpdir = val.val_bool;
468 		break;
469 		case RESTORE_WALLCLOCK:
470 			test.restore_wallclock = val.val_bool;
471 		break;
472 		case SAVE_RESTORE:
473 			test.save_restore = parse_save_restore(&reader, &val);
474 		break;
475 		case SKIP_FILESYSTEMS:
476 			test.skip_filesystems = parse_strarr(&reader, &val);
477 		break;
478 		case SKIP_IN_COMPAT:
479 			test.skip_in_compat = val.val_bool;
480 		break;
481 		case SKIP_IN_LOCKDOWN:
482 			test.skip_in_lockdown = val.val_bool;
483 		break;
484 		case SKIP_IN_SECUREBOOT:
485 			test.skip_in_secureboot = val.val_bool;
486 		break;
487 		case SUPPORTED_ARCHS:
488 			test.supported_archs = parse_strarr(&reader, &val);
489 		break;
490 		case TAGS:
491 			test.tags = parse_tags(&reader, &val);
492 		break;
493 		case TAINT_CHECK:
494 			test.taint_check = val.val_bool;
495 		break;
496 		case TCNT:
497 			if (val.val_int <= 0)
498 				ujson_err(&reader, "Number of tests must be > 0");
499 			else
500 				test.tcnt = val.val_int;
501 		break;
502 		}
503 	}
504 
505 	ujson_reader_finish(&reader);
506 
507 	if (ujson_reader_err(&reader))
508 		tst_brk(TBROK, "Invalid metadata");
509 }
510 
511 enum parser_state {
512 	PAR_NONE,
513 	PAR_ESC,
514 	PAR_DOC,
515 	PAR_ENV,
516 };
517 
extract_metadata(void)518 static void extract_metadata(void)
519 {
520 	FILE *f;
521 	char line[4096];
522 	char path[4096];
523 	enum parser_state state = PAR_NONE;
524 	unsigned int lineno = 1;
525 
526 	if (tst_get_path(shell_filename, path, sizeof(path)) == -1)
527 		tst_brk(TBROK, "Failed to find %s in $PATH", shell_filename);
528 
529 	f = SAFE_FOPEN(path, "r");
530 
531 	while (fgets(line, sizeof(line), f)) {
532 		switch (state) {
533 		case PAR_NONE:
534 			if (!strcmp(line, "# ---\n"))
535 				state = PAR_ESC;
536 		break;
537 		case PAR_ESC:
538 			if (!strcmp(line, "# env\n")) {
539 				state = PAR_ENV;
540 			} else if (!strcmp(line, "# doc\n")) {
541 				state = PAR_DOC;
542 			} else {
543 				tst_brk(TBROK, "%s: %u: Unknown comment block %s",
544 				        path, lineno, line);
545 			}
546 		break;
547 		case PAR_ENV:
548 			if (line[0] != '#') {
549 				tst_brk(TBROK,
550 					"%s: %u: Unexpected end of comment block!",
551 					path, lineno);
552 			}
553 
554 			if (!strcmp(line, "# ---\n"))
555 				state = PAR_NONE;
556 			else
557 				metadata_append(line + 2);
558 		break;
559 		case PAR_DOC:
560 			if (line[0] != '#') {
561 				tst_brk(TBROK,
562 					"%s: %u: Unexpected end of comment block!",
563 					path, lineno);
564 			}
565 
566 			if (!strcmp(line, "# ---\n"))
567 				state = PAR_NONE;
568 		break;
569 		}
570 
571 		lineno++;
572 	}
573 
574 	fclose(f);
575 }
576 
prepare_test_struct(void)577 static void prepare_test_struct(void)
578 {
579 	extract_metadata();
580 
581 	if (metadata)
582 		parse_metadata();
583 	else
584 		tst_brk(TBROK, "No metadata found!");
585 }
586 
main(int argc,char * argv[])587 int main(int argc, char *argv[])
588 {
589 	if (argc < 2)
590 		goto help;
591 
592 	shell_filename = argv[1];
593 
594 	prepare_test_struct();
595 
596 	if (test.tcnt)
597 		test.test = run_shell_tcnt;
598 	else
599 		test.test_all = run_shell;
600 
601 	tst_run_tcases(argc - 1, argv + 1, &test);
602 help:
603 	print_help();
604 	return 1;
605 }
606