| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- // SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
- /*
- * proc sysctl test driver
- *
- * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
- */
- /*
- * This module provides an interface to the proc sysctl interfaces. This
- * driver requires CONFIG_PROC_SYSCTL. It will not normally be loaded by the
- * system unless explicitly requested by name. You can also build this driver
- * into your kernel.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/init.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/printk.h>
- #include <linux/fs.h>
- #include <linux/miscdevice.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <linux/async.h>
- #include <linux/delay.h>
- #include <linux/vmalloc.h>
- static int i_zero;
- static int i_one_hundred = 100;
- static int match_int_ok = 1;
- enum {
- TEST_H_SETUP_NODE,
- TEST_H_MNT,
- TEST_H_MNTERROR,
- TEST_H_EMPTY_ADD,
- TEST_H_EMPTY,
- TEST_H_U8,
- TEST_H_SIZE /* Always at the end */
- };
- static struct ctl_table_header *ctl_headers[TEST_H_SIZE] = {};
- struct test_sysctl_data {
- int int_0001;
- int int_0002;
- int int_0003[4];
- int boot_int;
- unsigned int uint_0001;
- char string_0001[65];
- #define SYSCTL_TEST_BITMAP_SIZE 65536
- unsigned long *bitmap_0001;
- };
- static struct test_sysctl_data test_data = {
- .int_0001 = 60,
- .int_0002 = 1,
- .int_0003[0] = 0,
- .int_0003[1] = 1,
- .int_0003[2] = 2,
- .int_0003[3] = 3,
- .boot_int = 0,
- .uint_0001 = 314,
- .string_0001 = "(none)",
- };
- /* These are all under /proc/sys/debug/test_sysctl/ */
- static const struct ctl_table test_table[] = {
- {
- .procname = "int_0001",
- .data = &test_data.int_0001,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &i_zero,
- .extra2 = &i_one_hundred,
- },
- {
- .procname = "int_0002",
- .data = &test_data.int_0002,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "int_0003",
- .data = &test_data.int_0003,
- .maxlen = sizeof(test_data.int_0003),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "match_int",
- .data = &match_int_ok,
- .maxlen = sizeof(match_int_ok),
- .mode = 0444,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "boot_int",
- .data = &test_data.boot_int,
- .maxlen = sizeof(test_data.boot_int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
- },
- {
- .procname = "uint_0001",
- .data = &test_data.uint_0001,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_douintvec,
- },
- {
- .procname = "string_0001",
- .data = &test_data.string_0001,
- .maxlen = sizeof(test_data.string_0001),
- .mode = 0644,
- .proc_handler = proc_dostring,
- },
- {
- .procname = "bitmap_0001",
- .data = &test_data.bitmap_0001,
- .maxlen = SYSCTL_TEST_BITMAP_SIZE,
- .mode = 0644,
- .proc_handler = proc_do_large_bitmap,
- },
- };
- static void test_sysctl_calc_match_int_ok(void)
- {
- int i;
- struct {
- int defined;
- int wanted;
- } match_int[] = {
- {.defined = *(int *)SYSCTL_ZERO, .wanted = 0},
- {.defined = *(int *)SYSCTL_ONE, .wanted = 1},
- {.defined = *(int *)SYSCTL_TWO, .wanted = 2},
- {.defined = *(int *)SYSCTL_THREE, .wanted = 3},
- {.defined = *(int *)SYSCTL_FOUR, .wanted = 4},
- {.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100},
- {.defined = *(int *)SYSCTL_TWO_HUNDRED, .wanted = 200},
- {.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000},
- {.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000},
- {.defined = *(int *)SYSCTL_INT_MAX, .wanted = INT_MAX},
- {.defined = *(int *)SYSCTL_MAXOLDUID, .wanted = 65535},
- {.defined = *(int *)SYSCTL_NEG_ONE, .wanted = -1},
- };
- for (i = 0; i < ARRAY_SIZE(match_int); i++)
- if (match_int[i].defined != match_int[i].wanted)
- match_int_ok = 0;
- }
- static int test_sysctl_setup_node_tests(void)
- {
- test_sysctl_calc_match_int_ok();
- test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL);
- if (!test_data.bitmap_0001)
- return -ENOMEM;
- ctl_headers[TEST_H_SETUP_NODE] = register_sysctl("debug/test_sysctl", test_table);
- if (!ctl_headers[TEST_H_SETUP_NODE]) {
- kfree(test_data.bitmap_0001);
- return -ENOMEM;
- }
- return 0;
- }
- /* Used to test that unregister actually removes the directory */
- static const struct ctl_table test_table_unregister[] = {
- {
- .procname = "unregister_error",
- .data = &test_data.int_0001,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- },
- };
- static int test_sysctl_run_unregister_nested(void)
- {
- struct ctl_table_header *unregister;
- unregister = register_sysctl("debug/test_sysctl/unregister_error",
- test_table_unregister);
- if (!unregister)
- return -ENOMEM;
- unregister_sysctl_table(unregister);
- return 0;
- }
- static int test_sysctl_run_register_mount_point(void)
- {
- ctl_headers[TEST_H_MNT]
- = register_sysctl_mount_point("debug/test_sysctl/mnt");
- if (!ctl_headers[TEST_H_MNT])
- return -ENOMEM;
- ctl_headers[TEST_H_MNTERROR]
- = register_sysctl("debug/test_sysctl/mnt/mnt_error",
- test_table_unregister);
- /*
- * Don't check the result.:
- * If it fails (expected behavior), return 0.
- * If successful (missbehavior of register mount point), we want to see
- * mnt_error when we run the sysctl test script
- */
- return 0;
- }
- static const struct ctl_table test_table_empty[] = { };
- static int test_sysctl_run_register_empty(void)
- {
- /* Tets that an empty dir can be created */
- ctl_headers[TEST_H_EMPTY_ADD]
- = register_sysctl("debug/test_sysctl/empty_add", test_table_empty);
- if (!ctl_headers[TEST_H_EMPTY_ADD])
- return -ENOMEM;
- /* Test that register on top of an empty dir works */
- ctl_headers[TEST_H_EMPTY]
- = register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty);
- if (!ctl_headers[TEST_H_EMPTY])
- return -ENOMEM;
- return 0;
- }
- static const struct ctl_table table_u8_over[] = {
- {
- .procname = "u8_over",
- .data = &test_data.uint_0001,
- .maxlen = sizeof(u8),
- .mode = 0644,
- .proc_handler = proc_dou8vec_minmax,
- .extra1 = SYSCTL_FOUR,
- .extra2 = SYSCTL_ONE_THOUSAND,
- },
- };
- static const struct ctl_table table_u8_under[] = {
- {
- .procname = "u8_under",
- .data = &test_data.uint_0001,
- .maxlen = sizeof(u8),
- .mode = 0644,
- .proc_handler = proc_dou8vec_minmax,
- .extra1 = SYSCTL_NEG_ONE,
- .extra2 = SYSCTL_ONE_HUNDRED,
- },
- };
- static const struct ctl_table table_u8_valid[] = {
- {
- .procname = "u8_valid",
- .data = &test_data.uint_0001,
- .maxlen = sizeof(u8),
- .mode = 0644,
- .proc_handler = proc_dou8vec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_TWO_HUNDRED,
- },
- };
- static int test_sysctl_register_u8_extra(void)
- {
- /* should fail because it's over */
- ctl_headers[TEST_H_U8]
- = register_sysctl("debug/test_sysctl", table_u8_over);
- if (ctl_headers[TEST_H_U8])
- return -ENOMEM;
- /* should fail because it's under */
- ctl_headers[TEST_H_U8]
- = register_sysctl("debug/test_sysctl", table_u8_under);
- if (ctl_headers[TEST_H_U8])
- return -ENOMEM;
- /* should not fail because it's valid */
- ctl_headers[TEST_H_U8]
- = register_sysctl("debug/test_sysctl", table_u8_valid);
- if (!ctl_headers[TEST_H_U8])
- return -ENOMEM;
- return 0;
- }
- static int __init test_sysctl_init(void)
- {
- int err = 0;
- int (*func_array[])(void) = {
- test_sysctl_setup_node_tests,
- test_sysctl_run_unregister_nested,
- test_sysctl_run_register_mount_point,
- test_sysctl_run_register_empty,
- test_sysctl_register_u8_extra
- };
- for (int i = 0; !err && i < ARRAY_SIZE(func_array); i++)
- err = func_array[i]();
- return err;
- }
- module_init(test_sysctl_init);
- static void __exit test_sysctl_exit(void)
- {
- kfree(test_data.bitmap_0001);
- for (int i = 0; i < TEST_H_SIZE; i++) {
- if (ctl_headers[i])
- unregister_sysctl_table(ctl_headers[i]);
- }
- }
- module_exit(test_sysctl_exit);
- MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
- MODULE_DESCRIPTION("proc sysctl test driver");
- MODULE_LICENSE("GPL");
|