diff options
-rw-r--r-- | drivers/parport/procfs.c | 185 | ||||
-rw-r--r-- | drivers/parport/share.c | 2 | ||||
-rw-r--r-- | fs/proc/proc_sysctl.c | 244 | ||||
-rw-r--r-- | fs/sysctls.c | 5 | ||||
-rw-r--r-- | include/linux/key.h | 3 | ||||
-rw-r--r-- | include/linux/parport.h | 2 | ||||
-rw-r--r-- | include/linux/sysctl.h | 45 | ||||
-rw-r--r-- | include/linux/umh.h | 2 | ||||
-rw-r--r-- | kernel/signal.c | 23 | ||||
-rw-r--r-- | kernel/sysctl.c | 40 | ||||
-rw-r--r-- | kernel/umh.c | 11 | ||||
-rw-r--r-- | lib/test_sysctl.c | 91 | ||||
-rwxr-xr-x | scripts/check-sysctl-docs | 10 | ||||
-rw-r--r-- | security/keys/sysctl.c | 7 | ||||
-rwxr-xr-x | tools/testing/selftests/sysctl/sysctl.sh | 115 |
15 files changed, 351 insertions, 434 deletions
diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index d740eba3c099..4e5b972c3e26 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -32,6 +32,13 @@ #define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ) #define PARPORT_MIN_SPINTIME_VALUE 1 #define PARPORT_MAX_SPINTIME_VALUE 1000 +/* + * PARPORT_BASE_* is the size of the known parts of the sysctl path + * in dev/partport/%s/devices/%s. "dev/parport/"(12), "/devices/"(9 + * and null char(1). + */ +#define PARPORT_BASE_PATH_SIZE 13 +#define PARPORT_BASE_DEVICES_PATH_SIZE 22 static int do_active_device(struct ctl_table *table, int write, void *result, size_t *lenp, loff_t *ppos) @@ -236,13 +243,6 @@ do { \ return 0; } -#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD } -#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \ - .mode = 0555, .child = CHILD } -#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD } -#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \ - .mode = 0555, .child = NULL } - static const unsigned long parport_min_timeslice_value = PARPORT_MIN_TIMESLICE_VALUE; @@ -257,17 +257,16 @@ PARPORT_MAX_SPINTIME_VALUE; struct parport_sysctl_table { - struct ctl_table_header *sysctl_header; + struct ctl_table_header *port_header; + struct ctl_table_header *devices_header; struct ctl_table vars[12]; struct ctl_table device_dir[2]; - struct ctl_table port_dir[2]; - struct ctl_table parport_dir[2]; - struct ctl_table dev_dir[2]; }; static const struct parport_sysctl_table parport_sysctl_template = { - .sysctl_header = NULL, - { + .port_header = NULL, + .devices_header = NULL, + { { .procname = "spintime", .data = NULL, @@ -305,7 +304,6 @@ static const struct parport_sysctl_table parport_sysctl_template = { .mode = 0444, .proc_handler = do_hardware_modes }, - PARPORT_DEVICES_ROOT_DIR, #ifdef CONFIG_PARPORT_1284 { .procname = "autoprobe", @@ -355,18 +353,6 @@ static const struct parport_sysctl_table parport_sysctl_template = { }, {} }, - { - PARPORT_PORT_DIR(NULL), - {} - }, - { - PARPORT_PARPORT_DIR(NULL), - {} - }, - { - PARPORT_DEV_DIR(NULL), - {} - } }; struct parport_device_sysctl_table @@ -393,6 +379,7 @@ parport_device_sysctl_template = { .extra1 = (void*) &parport_min_timeslice_value, .extra2 = (void*) &parport_max_timeslice_value }, + {} }, { { @@ -400,25 +387,8 @@ parport_device_sysctl_template = { .data = NULL, .maxlen = 0, .mode = 0555, - .child = NULL }, {} - }, - { - PARPORT_DEVICES_ROOT_DIR, - {} - }, - { - PARPORT_PORT_DIR(NULL), - {} - }, - { - PARPORT_PARPORT_DIR(NULL), - {} - }, - { - PARPORT_DEV_DIR(NULL), - {} } }; @@ -454,30 +424,15 @@ parport_default_sysctl_table = { .extra2 = (void*) &parport_max_spintime_value }, {} - }, - { - { - .procname = "default", - .mode = 0555, - .child = parport_default_sysctl_table.vars - }, - {} - }, - { - PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir), - {} - }, - { - PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir), - {} } }; - int parport_proc_register(struct parport *port) { struct parport_sysctl_table *t; - int i; + char *tmp_dir_path; + size_t tmp_path_len, port_name_len; + int bytes_written, i, err = 0; t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) @@ -485,28 +440,64 @@ int parport_proc_register(struct parport *port) t->device_dir[0].extra1 = port; - for (i = 0; i < 5; i++) + t->vars[0].data = &port->spintime; + for (i = 0; i < 5; i++) { t->vars[i].extra1 = port; + t->vars[5 + i].extra2 = &port->probe_info[i]; + } - t->vars[0].data = &port->spintime; - t->vars[5].child = t->device_dir; - - for (i = 0; i < 5; i++) - t->vars[6 + i].extra2 = &port->probe_info[i]; + port_name_len = strnlen(port->name, PARPORT_NAME_MAX_LEN); + /* + * Allocate a buffer for two paths: dev/parport/PORT and dev/parport/PORT/devices. + * We calculate for the second as that will give us enough for the first. + */ + tmp_path_len = PARPORT_BASE_DEVICES_PATH_SIZE + port_name_len; + tmp_dir_path = kzalloc(tmp_path_len, GFP_KERNEL); + if (!tmp_dir_path) { + err = -ENOMEM; + goto exit_free_t; + } - t->port_dir[0].procname = port->name; + bytes_written = snprintf(tmp_dir_path, tmp_path_len, + "dev/parport/%s/devices", port->name); + if (tmp_path_len <= bytes_written) { + err = -ENOENT; + goto exit_free_tmp_dir_path; + } + t->devices_header = register_sysctl(tmp_dir_path, t->device_dir); + if (t->devices_header == NULL) { + err = -ENOENT; + goto exit_free_tmp_dir_path; + } - t->port_dir[0].child = t->vars; - t->parport_dir[0].child = t->port_dir; - t->dev_dir[0].child = t->parport_dir; + tmp_path_len = PARPORT_BASE_PATH_SIZE + port_name_len; + bytes_written = snprintf(tmp_dir_path, tmp_path_len, + "dev/parport/%s", port->name); + if (tmp_path_len <= bytes_written) { + err = -ENOENT; + goto unregister_devices_h; + } - t->sysctl_header = register_sysctl_table(t->dev_dir); - if (t->sysctl_header == NULL) { - kfree(t); - t = NULL; + t->port_header = register_sysctl(tmp_dir_path, t->vars); + if (t->port_header == NULL) { + err = -ENOENT; + goto unregister_devices_h; } + port->sysctl_table = t; + + kfree(tmp_dir_path); return 0; + +unregister_devices_h: + unregister_sysctl_table(t->devices_header); + +exit_free_tmp_dir_path: + kfree(tmp_dir_path); + +exit_free_t: + kfree(t); + return err; } int parport_proc_unregister(struct parport *port) @@ -514,7 +505,8 @@ int parport_proc_unregister(struct parport *port) if (port->sysctl_table) { struct parport_sysctl_table *t = port->sysctl_table; port->sysctl_table = NULL; - unregister_sysctl_table(t->sysctl_header); + unregister_sysctl_table(t->devices_header); + unregister_sysctl_table(t->port_header); kfree(t); } return 0; @@ -522,30 +514,53 @@ int parport_proc_unregister(struct parport *port) int parport_device_proc_register(struct pardevice *device) { + int bytes_written, err = 0; struct parport_device_sysctl_table *t; struct parport * port = device->port; + size_t port_name_len, device_name_len, tmp_dir_path_len; + char *tmp_dir_path; t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; - t->dev_dir[0].child = t->parport_dir; - t->parport_dir[0].child = t->port_dir; - t->port_dir[0].procname = port->name; - t->port_dir[0].child = t->devices_root_dir; - t->devices_root_dir[0].child = t->device_dir; + port_name_len = strnlen(port->name, PARPORT_NAME_MAX_LEN); + device_name_len = strnlen(device->name, PATH_MAX); + + /* Allocate a buffer for two paths: dev/parport/PORT/devices/DEVICE. */ + tmp_dir_path_len = PARPORT_BASE_DEVICES_PATH_SIZE + port_name_len + device_name_len; + tmp_dir_path = kzalloc(tmp_dir_path_len, GFP_KERNEL); + if (!tmp_dir_path) { + err = -ENOMEM; + goto exit_free_t; + } + + bytes_written = snprintf(tmp_dir_path, tmp_dir_path_len, "dev/parport/%s/devices/%s", + port->name, device->name); + if (tmp_dir_path_len <= bytes_written) { + err = -ENOENT; + goto exit_free_path; + } - t->device_dir[0].procname = device->name; - t->device_dir[0].child = t->vars; t->vars[0].data = &device->timeslice; - t->sysctl_header = register_sysctl_table(t->dev_dir); + t->sysctl_header = register_sysctl(tmp_dir_path, t->vars); if (t->sysctl_header == NULL) { kfree(t); t = NULL; } device->sysctl_table = t; + + kfree(tmp_dir_path); return 0; + +exit_free_path: + kfree(tmp_dir_path); + +exit_free_t: + kfree(t); + + return err; } int parport_device_proc_unregister(struct pardevice *device) @@ -564,7 +579,7 @@ static int __init parport_default_proc_register(void) int ret; parport_default_sysctl_table.sysctl_header = - register_sysctl_table(parport_default_sysctl_table.dev_dir); + register_sysctl("dev/parport/default", parport_default_sysctl_table.vars); if (!parport_default_sysctl_table.sysctl_header) return -ENOMEM; ret = parport_bus_init(); diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 62f8407923d4..2d46b1d4fd69 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -467,7 +467,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, atomic_set(&tmp->ref_count, 1); INIT_LIST_HEAD(&tmp->full_list); - name = kmalloc(15, GFP_KERNEL); + name = kmalloc(PARPORT_NAME_MAX_LEN, GFP_KERNEL); if (!name) { kfree(tmp); return NULL; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index ae832e982003..4e5488975415 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -29,9 +29,8 @@ static const struct file_operations proc_sys_dir_file_operations; static const struct inode_operations proc_sys_dir_operations; /* Support for permanently empty directories */ - struct ctl_table sysctl_mount_point[] = { - { } + {.type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY } }; /** @@ -48,21 +47,14 @@ struct ctl_table_header *register_sysctl_mount_point(const char *path) } EXPORT_SYMBOL(register_sysctl_mount_point); -static bool is_empty_dir(struct ctl_table_header *head) -{ - return head->ctl_table[0].child == sysctl_mount_point; -} - -static void set_empty_dir(struct ctl_dir *dir) -{ - dir->header.ctl_table[0].child = sysctl_mount_point; -} - -static void clear_empty_dir(struct ctl_dir *dir) - -{ - dir->header.ctl_table[0].child = NULL; -} +#define sysctl_is_perm_empty_ctl_table(tptr) \ + (tptr[0].type == SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY) +#define sysctl_is_perm_empty_ctl_header(hptr) \ + (sysctl_is_perm_empty_ctl_table(hptr->ctl_table)) +#define sysctl_set_perm_empty_ctl_header(hptr) \ + (hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY) +#define sysctl_clear_perm_empty_ctl_header(hptr) \ + (hptr->ctl_table[0].type = SYSCTL_TABLE_TYPE_DEFAULT) void proc_sys_poll_notify(struct ctl_table_poll *poll) { @@ -230,20 +222,22 @@ static void erase_header(struct ctl_table_header *head) static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) { struct ctl_table *entry; + struct ctl_table_header *dir_h = &dir->header; int err; + /* Is this a permanently empty directory? */ - if (is_empty_dir(&dir->header)) + if (sysctl_is_perm_empty_ctl_header(dir_h)) return -EROFS; /* Am I creating a permanently empty directory? */ - if (header->ctl_table == sysctl_mount_point) { + if (sysctl_is_perm_empty_ctl_table(header->ctl_table)) { if (!RB_EMPTY_ROOT(&dir->root)) return -EINVAL; - set_empty_dir(dir); + sysctl_set_perm_empty_ctl_header(dir_h); } - dir->header.nreg++; + dir_h->nreg++; header->parent = dir; err = insert_links(header); if (err) @@ -259,9 +253,9 @@ fail: put_links(header); fail_links: if (header->ctl_table == sysctl_mount_point) - clear_empty_dir(dir); + sysctl_clear_perm_empty_ctl_header(dir_h); header->parent = NULL; - drop_sysctl_table(&dir->header); + drop_sysctl_table(dir_h); return err; } @@ -479,7 +473,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, inode->i_mode |= S_IFDIR; inode->i_op = &proc_sys_dir_operations; inode->i_fop = &proc_sys_dir_file_operations; - if (is_empty_dir(head)) + if (sysctl_is_perm_empty_ctl_header(head)) make_empty_dir_inode(inode); } @@ -1136,9 +1130,6 @@ static int sysctl_check_table(const char *path, struct ctl_table *table) struct ctl_table *entry; int err = 0; list_for_each_table_entry(entry, table) { - if (entry->child) - err |= sysctl_err(path, entry, "Not a file"); - if ((entry->proc_handler == proc_dostring) || (entry->proc_handler == proc_dobool) || (entry->proc_handler == proc_dointvec) || @@ -1406,7 +1397,6 @@ fail_put_dir_locked: spin_unlock(&sysctl_lock); fail: kfree(header); - dump_stack(); return NULL; } @@ -1466,185 +1456,6 @@ void __init __register_sysctl_init(const char *path, struct ctl_table *table, kmemleak_not_leak(hdr); } -static char *append_path(const char *path, char *pos, const char *name) -{ - int namelen; - namelen = strlen(name); - if (((pos - path) + namelen + 2) >= PATH_MAX) - return NULL; - memcpy(pos, name, namelen); - pos[namelen] = '/'; - pos[namelen + 1] = '\0'; - pos += namelen + 1; - return pos; -} - -static int count_subheaders(struct ctl_table *table) -{ - int has_files = 0; - int nr_subheaders = 0; - struct ctl_table *entry; - - /* special case: no directory and empty directory */ - if (!table || !table->procname) - return 1; - - list_for_each_table_entry(entry, table) { - if (entry->child) - nr_subheaders += count_subheaders(entry->child); - else - has_files = 1; - } - return nr_subheaders + has_files; -} - -static int register_leaf_sysctl_tables(const char *path, char *pos, - struct ctl_table_header ***subheader, struct ctl_table_set *set, - struct ctl_table *table) -{ - struct ctl_table *ctl_table_arg = NULL; - struct ctl_table *entry, *files; - int nr_files = 0; - int nr_dirs = 0; - int err = -ENOMEM; - - list_for_each_table_entry(entry, table) { - if (entry->child) - nr_dirs++; - else - nr_files++; - } - - files = table; - /* If there are mixed files and directories we need a new table */ - if (nr_dirs && nr_files) { - struct ctl_table *new; - files = kcalloc(nr_files + 1, sizeof(struct ctl_table), - GFP_KERNEL); - if (!files) - goto out; - - ctl_table_arg = files; - new = files; - - list_for_each_table_entry(entry, table) { - if (entry->child) - continue; - *new = *entry; - new++; - } - } - - /* Register everything except a directory full of subdirectories */ - if (nr_files || !nr_dirs) { - struct ctl_table_header *header; - header = __register_sysctl_table(set, path, files); - if (!header) { - kfree(ctl_table_arg); - goto out; - } - - /* Remember if we need to free the file table */ - header->ctl_table_arg = ctl_table_arg; - **subheader = header; - (*subheader)++; - } - - /* Recurse into the subdirectories. */ - list_for_each_table_entry(entry, table) { - char *child_pos; - - if (!entry->child) - continue; - - err = -ENAMETOOLONG; - child_pos = append_path(path, pos, entry->procname); - if (!child_pos) - goto out; - - err = register_leaf_sysctl_tables(path, child_pos, subheader, - set, entry->child); - pos[0] = '\0'; - if (err) - goto out; - } - err = 0; -out: - /* On failure our caller will unregister all registered subheaders */ - return err; -} - -/** - * register_sysctl_table - register a sysctl table hierarchy - * @table: the top-level table structure - * - * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. - * We are slowly deprecating this call so avoid its use. - */ -struct ctl_table_header *register_sysctl_table(struct ctl_table *table) -{ - struct ctl_table *ctl_table_arg = table; - int nr_subheaders = count_subheaders(table); - struct ctl_table_header *header = NULL, **subheaders, **subheader; - char *new_path, *pos; - - pos = new_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!new_path) - return NULL; - - pos[0] = '\0'; - while (table->procname && table->child && !table[1].procname) { - pos = append_path(new_path, pos, table->procname); - if (!pos) - goto out; - table = table->child; - } - if (nr_subheaders == 1) { - header = __register_sysctl_table(&sysctl_table_root.default_set, new_path, table); - if (header) - header->ctl_table_arg = ctl_table_arg; - } else { - header = kzalloc(sizeof(*header) + - sizeof(*subheaders)*nr_subheaders, GFP_KERNEL); - if (!header) - goto out; - - subheaders = (struct ctl_table_header **) (header + 1); - subheader = subheaders; - header->ctl_table_arg = ctl_table_arg; - - if (register_leaf_sysctl_tables(new_path, pos, &subheader, - &sysctl_table_root.default_set, table)) - goto err_register_leaves; - } - -out: - kfree(new_path); - return header; - -err_register_leaves: - while (subheader > subheaders) { - struct ctl_table_header *subh = *(--subheader); - struct ctl_table *table = subh->ctl_table_arg; - unregister_sysctl_table(subh); - kfree(table); - } - kfree(header); - header = NULL; - goto out; -} -EXPORT_SYMBOL(register_sysctl_table); - -int __register_sysctl_base(struct ctl_table *base_table) -{ - struct ctl_table_header *hdr; - - hdr = register_sysctl_table(base_table); - kmemleak_not_leak(hdr); - return 0; -} - static void put_links(struct ctl_table_header *header) { struct ctl_table_set *root_set = &sysctl_table_root.default_set; @@ -1700,35 +1511,18 @@ static void drop_sysctl_table(struct ctl_table_header *header) /** * unregister_sysctl_table - unregister a sysctl table hierarchy - * @header: the header returned from register_sysctl_table + * @header: the header returned from register_sysctl or __register_sysctl_table * * Unregisters the sysctl table and all children. proc entries may not * actually be removed until they are no longer used by anyone. */ void unregister_sysctl_table(struct ctl_table_header * header) { - int nr_subheaders; might_sleep(); if (header == NULL) return; - nr_subheaders = count_subheaders(header->ctl_table_arg); - if (unlikely(nr_subheaders > 1)) { - struct ctl_table_header **subheaders; - int i; - - subheaders = (struct ctl_table_header **)(header + 1); - for (i = nr_subheaders -1; i >= 0; i--) { - struct ctl_table_header *subh = subheaders[i]; - struct ctl_table *table = subh->ctl_table_arg; - unregister_sysctl_table(subh); - kfree(table); - } - kfree(header); - return; - } - spin_lock(&sysctl_lock); drop_sysctl_table(header); spin_unlock(&sysctl_lock); diff --git a/fs/sysctls.c b/fs/sysctls.c index c701273c9432..76a0aee8c229 100644 --- a/fs/sysctls.c +++ b/fs/sysctls.c @@ -29,11 +29,10 @@ static struct ctl_table fs_shared_sysctls[] = { { } }; -DECLARE_SYSCTL_BASE(fs, fs_shared_sysctls); - static int __init init_fs_sysctls(void) { - return register_sysctl_base(fs); + register_sysctl_init("fs", fs_shared_sysctls); + return 0; } early_initcall(init_fs_sysctls); diff --git a/include/linux/key.h b/include/linux/key.h index 8dc7f7c3088b..938d7ecfb495 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -490,9 +490,6 @@ do { \ rcu_assign_pointer((KEY)->payload.rcu_data0, (PAYLOAD)); \ } while (0) -#ifdef CONFIG_SYSCTL -extern struct ctl_table key_sysctls[]; -#endif /* * the userspace interface */ diff --git a/include/linux/parport.h b/include/linux/parport.h index a0bc9e0267b7..243c82d7f852 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -180,6 +180,8 @@ struct ieee1284_info { struct semaphore irq; }; +#define PARPORT_NAME_MAX_LEN 15 + /* A parallel port */ struct parport { unsigned long base; /* base address */ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3d08277959af..59d451f455bf 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -89,7 +89,7 @@ int proc_do_static_key(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); /* - * Register a set of sysctl names by calling register_sysctl_table + * Register a set of sysctl names by calling register_sysctl * with an initialised array of struct ctl_table's. An entry with * NULL procname terminates the table. table->de will be * set up by the registration and need not be initialised in advance. @@ -137,7 +137,17 @@ struct ctl_table { void *data; int maxlen; umode_t mode; - struct ctl_table *child; /* Deprecated */ + /** + * enum type - Enumeration to differentiate between ctl target types + * @SYSCTL_TABLE_TYPE_DEFAULT: ctl target with no special considerations + * @SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY: Used to identify a permanently + * empty directory target to serve + * as mount point. + */ + enum { + SYSCTL_TABLE_TYPE_DEFAULT, + SYSCTL_TABLE_TYPE_PERMANENTLY_EMPTY + } type; proc_handler *proc_handler; /* Callback for text formatting */ struct ctl_table_poll *poll; void *extra1; @@ -197,20 +207,6 @@ struct ctl_path { #ifdef CONFIG_SYSCTL -#define DECLARE_SYSCTL_BASE(_name, _table) \ -static struct ctl_table _name##_base_table[] = { \ - { \ - .procname = #_name, \ - .mode = 0555, \ - .child = _table, \ - }, \ - { }, \ -} - -extern int __register_sysctl_base(struct ctl_table *base_table); - -#define register_sysctl_base(_name) __register_sysctl_base(_name##_base_table) - void proc_sys_poll_notify(struct ctl_table_poll *poll); extern void setup_sysctl_set(struct ctl_table_set *p, @@ -222,7 +218,6 @@ struct ctl_table_header *__register_sysctl_table( struct ctl_table_set *set, const char *path, struct ctl_table *table); struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table); -struct ctl_table_header *register_sysctl_table(struct ctl_table * table); void unregister_sysctl_table(struct ctl_table_header * table); extern int sysctl_init_bases(void); @@ -244,24 +239,10 @@ extern int unaligned_enabled; extern int unaligned_dump_stack; extern int no_unaligned_warning; -extern struct ctl_table sysctl_mount_point[]; +#define SYSCTL_PERM_EMPTY_DIR (1 << 0) #else /* CONFIG_SYSCTL */ -#define DECLARE_SYSCTL_BASE(_name, _table) - -static inline int __register_sysctl_base(struct ctl_table *base_table) -{ - return 0; -} - -#define register_sysctl_base(table) __register_sysctl_base(table) - -static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table) -{ - return NULL; -} - static inline void register_sysctl_init(const char *path, struct ctl_table *table) { } diff --git a/include/linux/umh.h b/include/linux/umh.h index 5d1f6129b847..daa6a7048c11 100644 --- a/include/linux/umh.h +++ b/include/linux/umh.h @@ -42,8 +42,6 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp, extern int call_usermodehelper_exec(struct subprocess_info *info, int wait); -extern struct ctl_table usermodehelper_table[]; - enum umh_disable_depth { UMH_ENABLED = 0, UMH_FREEZING, diff --git a/kernel/signal.c b/kernel/signal.c index 2547fa73bde5..b5370fe5c198 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -45,6 +45,7 @@ #include <linux/posix-timers.h> #include <linux/cgroup.h> #include <linux/audit.h> +#include <linux/sysctl.h> #define CREATE_TRACE_POINTS #include <trace/events/signal.h> @@ -4773,6 +4774,28 @@ static inline void siginfo_buildtime_checks(void) #endif } +#if defined(CONFIG_SYSCTL) +static struct ctl_table signal_debug_table[] = { +#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE + { + .procname = "exception-trace", + .data = &show_unhandled_signals, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, +#endif + { } +}; + +static int __init init_signal_sysctls(void) +{ + register_sysctl_init("debug", signal_debug_table); + return 0; +} +early_initcall(init_signal_sysctls); +#endif /* CONFIG_SYSCTL */ + void __init signals_init(void) { siginfo_buildtime_checks(); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index a57de67f032f..354a2d294f52 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1783,11 +1783,6 @@ static struct ctl_table kern_table[] = { .proc_handler = sysctl_max_threads, }, { - .procname = "usermodehelper", - .mode = 0555, - .child = usermodehelper_table, - }, - { .procname = "overflowuid", .data = &overflowuid, .maxlen = sizeof(int), @@ -1962,13 +1957,6 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #endif -#ifdef CONFIG_KEYS - { - .procname = "keys", - .mode = 0555, - .child = key_sysctls, - }, -#endif #ifdef CONFIG_PERF_EVENTS /* * User-space scripts rely on the existence of this file @@ -2264,34 +2252,10 @@ static struct ctl_table vm_table[] = { { } }; -static struct ctl_table debug_table[] = { -#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE - { - .procname = "exception-trace", - .data = &show_unhandled_signals, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, -#endif - { } -}; - -static struct ctl_table dev_table[] = { - { } -}; - -DECLARE_SYSCTL_BASE(kernel, kern_table); -DECLARE_SYSCTL_BASE(vm, vm_table); -DECLARE_SYSCTL_BASE(debug, debug_table); -DECLARE_SYSCTL_BASE(dev, dev_table); - int __init sysctl_init_bases(void) { - register_sysctl_base(kernel); - register_sysctl_base(vm); - register_sysctl_base(debug); - register_sysctl_base(dev); + register_sysctl_init("kernel", kern_table); + register_sysctl_init("vm", vm_table); return 0; } diff --git a/kernel/umh.c b/kernel/umh.c index 60aa9e764a38..41088c5c39fd 100644 --- a/kernel/umh.c +++ b/kernel/umh.c @@ -544,7 +544,8 @@ static int proc_cap_handler(struct ctl_table *table, int write, return 0; } -struct ctl_table usermodehelper_table[] = { +#if defined(CONFIG_SYSCTL) +static struct ctl_table usermodehelper_table[] = { { .procname = "bset", .data = &usermodehelper_bset, @@ -561,3 +562,11 @@ struct ctl_table usermodehelper_table[] = { }, { } }; + +static int __init init_umh_sysctls(void) +{ + register_sysctl_init("kernel/usermodehelper", usermodehelper_table); + return 0; +} +early_initcall(init_umh_sysctls); +#endif /* CONFIG_SYSCTL */ diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index e2a816d85ea2..8036aa91a1cb 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -30,6 +30,13 @@ static int i_zero; static int i_one_hundred = 100; static int match_int_ok = 1; + +static struct { + struct ctl_table_header *test_h_setup_node; + struct ctl_table_header *test_h_mnt; + struct ctl_table_header *test_h_mnterror; +} sysctl_test_headers; + struct test_sysctl_data { int int_0001; int int_0002; @@ -126,9 +133,7 @@ static struct ctl_table test_table[] = { { } }; -static struct ctl_table_header *test_sysctl_header; - -static int __init test_sysctl_init(void) +static void test_sysctl_calc_match_int_ok(void) { int i; @@ -153,24 +158,96 @@ static int __init test_sysctl_init(void) 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; - test_sysctl_header = register_sysctl("debug/test_sysctl", test_table); - if (!test_sysctl_header) { + sysctl_test_headers.test_h_setup_node = register_sysctl("debug/test_sysctl", test_table); + if (!sysctl_test_headers.test_h_setup_node) { kfree(test_data.bitmap_0001); return -ENOMEM; } + return 0; } + +/* Used to test that unregister actually removes the directory */ +static 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) +{ + sysctl_test_headers.test_h_mnt + = register_sysctl_mount_point("debug/test_sysctl/mnt"); + if (!sysctl_test_headers.test_h_mnt) + return -ENOMEM; + + sysctl_test_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 int __init test_sysctl_init(void) +{ + int err; + + err = test_sysctl_setup_node_tests(); + if (err) + goto out; + + err = test_sysctl_run_unregister_nested(); + if (err) + goto out; + + err = test_sysctl_run_register_mount_point(); + +out: + return err; +} module_init(test_sysctl_init); static void __exit test_sysctl_exit(void) { kfree(test_data.bitmap_0001); - if (test_sysctl_header) - unregister_sysctl_table(test_sysctl_header); + if (sysctl_test_headers.test_h_setup_node) + unregister_sysctl_table(sysctl_test_headers.test_h_setup_node); + if (sysctl_test_headers.test_h_mnt) + unregister_sysctl_table(sysctl_test_headers.test_h_mnt); + if (sysctl_test_headers.test_h_mnterror) + unregister_sysctl_table(sysctl_test_headers.test_h_mnterror); } module_exit(test_sysctl_exit); diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index edc9a629d79e..4f163e0bf6a4 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -146,16 +146,6 @@ curtable && /\.procname[\t ]*=[\t ]*".+"/ { children[curtable][curentry] = child } -/register_sysctl_table\(.*\)/ { - match($0, /register_sysctl_table\(([^)]+)\)/, tables) - if (debug) print "Registering table " tables[1] - if (children[tables[1]][table]) { - for (entry in entries[children[tables[1]][table]]) { - printentry(entry) - } - } -} - END { for (entry in documented) { if (!seen[entry]) { diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index b46b651b3c4c..b72b82bb20c6 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -68,3 +68,10 @@ struct ctl_table key_sysctls[] = { #endif { } }; + +static int __init init_security_keys_sysctls(void) +{ + register_sysctl_init("kernel/keys", key_sysctls); + return 0; +} +early_initcall(init_security_keys_sysctls); diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index bfc54b422f25..444b2befda82 100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -14,23 +14,27 @@ TEST_FILE=$(mktemp) # This represents # -# TEST_ID:TEST_COUNT:ENABLED:TARGET +# TEST_ID:TEST_COUNT:ENABLED:TARGET:SKIP_NO_TARGET # # TEST_ID: is the test id number # TEST_COUNT: number of times we should run the test # ENABLED: 1 if enabled, 0 otherwise # TARGET: test target file required on the test_sysctl module +# SKIP_NO_TARGET: 1 skip if TARGET not there +# 0 run eventhough TARGET not there # # Once these are enabled please leave them as-is. Write your own test, # we have tons of space. -ALL_TESTS="0001:1:1:int_0001" -ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001" -ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002" -ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001" -ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003" -ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001" -ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int" -ALL_TESTS="$ALL_TESTS 0008:1:1:match_int" +ALL_TESTS="0001:1:1:int_0001:1" +ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001:1" +ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002:1" +ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001:1" +ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003:1" +ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001:1" +ALL_TESTS="$ALL_TESTS 0007:1:1:boot_int:1" +ALL_TESTS="$ALL_TESTS 0008:1:1:match_int:1" +ALL_TESTS="$ALL_TESTS 0009:1:1:unregister_error:0" +ALL_TESTS="$ALL_TESTS 0010:1:1:mnt/mnt_error:0" function allow_user_defaults() { @@ -613,7 +617,6 @@ target_exists() TEST_ID="$2" if [ ! -f ${TARGET} ] ; then - echo "Target for test $TEST_ID: $TARGET not exist, skipping test ..." return 0 fi return 1 @@ -730,7 +733,7 @@ sysctl_test_0005() sysctl_test_0006() { - TARGET="${SYSCTL}/bitmap_0001" + TARGET="${SYSCTL}/$(get_test_target 0006)" reset_vals ORIG="" run_bitmaptest @@ -738,7 +741,7 @@ sysctl_test_0006() sysctl_test_0007() { - TARGET="${SYSCTL}/boot_int" + TARGET="${SYSCTL}/$(get_test_target 0007)" if [ ! -f $TARGET ]; then echo "Skipping test for $TARGET as it is not present ..." return $ksft_skip @@ -778,7 +781,7 @@ sysctl_test_0007() sysctl_test_0008() { - TARGET="${SYSCTL}/match_int" + TARGET="${SYSCTL}/$(get_test_target 0008)" if [ ! -f $TARGET ]; then echo "Skipping test for $TARGET as it is not present ..." return $ksft_skip @@ -797,6 +800,34 @@ sysctl_test_0008() return 0 } +sysctl_test_0009() +{ + TARGET="${SYSCTL}/$(get_test_target 0009)" + echo -n "Testing if $TARGET unregistered correctly ..." + if [ -d $TARGET ]; then + echo "TEST FAILED" + rc=1 + test_rc + fi + + echo "ok" + return 0 +} + +sysctl_test_0010() +{ + TARGET="${SYSCTL}/$(get_test_target 0010)" + echo -n "Testing that $TARGET was not created ..." + if [ -d $TARGET ]; then + echo "TEST FAILED" + rc=1 + test_rc + fi + + echo "ok" + return 0 +} + list_tests() { echo "Test ID list:" @@ -813,6 +844,8 @@ list_tests() echo "0006 x $(get_test_count 0006) - tests proc_do_large_bitmap()" echo "0007 x $(get_test_count 0007) - tests setting sysctl from kernel boot param" echo "0008 x $(get_test_count 0008) - tests sysctl macro values match" + echo "0009 x $(get_test_count 0009) - tests sysct unregister" + echo "0010 x $(get_test_count 0010) - tests sysct mount point" } usage() @@ -857,38 +890,65 @@ function test_num() usage fi } +function remove_leading_zeros() +{ + echo $1 | sed 's/^0*//' +} function get_test_count() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $2}' } function get_test_enabled() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $3}' } function get_test_target() { test_num $1 - TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') echo ${TEST_DATA} | awk -F":" '{print $4}' } +function get_test_skip_no_target() +{ + test_num $1 + awk_field=$(remove_leading_zeros $1) + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$awk_field'}') + echo ${TEST_DATA} | awk -F":" '{print $5}' +} + +function skip_test() +{ + TEST_ID=$1 + TEST_TARGET=$2 + if target_exists $TEST_TARGET $TEST_ID; then + TEST_SKIP=$(get_test_skip_no_target $TEST_ID) + if [[ $TEST_SKIP -eq "1" ]]; then + echo "Target for test $TEST_ID: $TEST_TARGET not exist, skipping test ..." + return 0 + fi + fi + return 1 +} + function run_all_tests() { for i in $ALL_TESTS ; do - TEST_ID=${i%:*:*:*} + TEST_ID=${i%:*:*:*:*} ENABLED=$(get_test_enabled $TEST_ID) TEST_COUNT=$(get_test_count $TEST_ID) TEST_TARGET=$(get_test_target $TEST_ID) - if target_exists $TEST_TARGET $TEST_ID; then - continue - fi + if [[ $ENABLED -eq "1" ]]; then test_case $TEST_ID $TEST_COUNT $TEST_TARGET fi @@ -923,18 +983,19 @@ function watch_case() function test_case() { + TEST_ID=$1 NUM_TESTS=$2 + TARGET=$3 - i=0 - - if target_exists $3 $1; then - continue + if skip_test $TEST_ID $TARGET; then + return fi + i=0 while [ $i -lt $NUM_TESTS ]; do - test_num $1 - watch_log $i ${TEST_NAME}_test_$1 noclear - RUN_TEST=${TEST_NAME}_test_$1 + test_num $TEST_ID + watch_log $i ${TEST_NAME}_test_${TEST_ID} noclear + RUN_TEST=${TEST_NAME}_test_${TEST_ID} $RUN_TEST let i=$i+1 done |