@@ -1602,35 +1602,39 @@ LTP library can be instructed to save and restore value of specified
field 'save_restore'. It is a NULL-terminated array of struct
'tst_path_val' where each tst_path_val.path represents a file, whose
value is saved at the beginning and restored at the end of the test.
-If non-NULL value is passed it is written to the respective file at
-the beginning of the test. Only the first line of a specified file
-is saved and restored.
+If non-NULL string is passed in tst_path_val.val, it is written
+to the respective file at the beginning of the test. Only the first line
+of a specified file is saved and restored.
-Pathnames can be optionally prefixed to specify how strictly (during
-'store') are handled errors:
+By default, the test will end with TCONF if the file is read-only or
+does not exist. If the optional write of new value fails, the test will end
+with 'TBROK'. This behavior can be changed using tst_path_val.flags:
-* (no prefix) - test ends with 'TCONF', if file doesn't exist
-* '?' - test prints info message and continues,
- if file doesn't exist or open/read fails
-* '!' - test ends with 'TBROK', if file doesn't exist
+* 'TST_SR_TBROK_MISSING' – End test with 'TBROK' if the file does not exist
+* 'TST_SR_TCONF_MISSING' – End test with 'TCONF' if the file does not exist
+* 'TST_SR_SKIP_MISSING' – Continue without saving the file if it does not exist
+* 'TST_SR_TBROK_RO' – End test with 'TBROK' if the file is read-only
+* 'TST_SR_TCONF_RO' – End test with 'TCONF' if the file is read-only
+* 'TST_SR_SKIP_RO' – Continue without saving the file if it is read-only
+* 'TST_SR_IGNORE_ERR' – Ignore errors when writing new value into the file
+
+Common flag combinations also have shortcuts:
+
+* 'TST_SR_TCONF' – Equivalent to 'TST_SR_TCONF_MISSING | TST_SR_TCONF_RO'
+* 'TST_SR_TBROK' – Equivalent to 'TST_SR_TBROK_MISSING | TST_SR_TBROK_RO'
+* 'TST_SR_SKIP' – Equivalent to 'TST_SR_SKIP_MISSING | TST_SR_SKIP_RO'
'restore' is always strict and will TWARN if it encounters any error.
[source,c]
-------------------------------------------------------------------------------
-static void setup(void)
-{
- FILE_PRINTF("/proc/sys/kernel/core_pattern", "/mypath");
- SAFE_TRY_FILE_PRINTF("/proc/sys/user/max_user_namespaces", "%d", 10);
-}
-
static struct tst_test test = {
...
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"/proc/sys/kernel/core_pattern", NULL},
- {"?/proc/sys/user/max_user_namespaces", NULL},
- {"!/sys/kernel/mm/ksm/run", "1"},
+ {"/proc/sys/kernel/core_pattern", NULL, TST_SR_TCONF},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
+ {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
{}
},
};
@@ -5,14 +5,26 @@
#ifndef TST_SYS_CONF_H__
#define TST_SYS_CONF_H__
+#define TST_SR_TCONF_MISSING 0x0
+#define TST_SR_TBROK_MISSING 0x1
+#define TST_SR_SKIP_MISSING 0x2
+#define TST_SR_TCONF_RO 0x0
+#define TST_SR_TBROK_RO 0x4
+#define TST_SR_SKIP_RO 0x8
+#define TST_SR_IGNORE_ERR 0x10
+
+#define TST_SR_TCONF (TST_SR_TCONF_MISSING | TST_SR_TCONF_RO)
+#define TST_SR_TBROK (TST_SR_TBROK_MISSING | TST_SR_TBROK_RO)
+#define TST_SR_SKIP (TST_SR_SKIP_MISSING | TST_SR_SKIP_RO)
+
struct tst_path_val {
const char *path;
const char *val;
+ unsigned int flags;
};
-int tst_sys_conf_save_str(const char *path, const char *value);
-int tst_sys_conf_save(const char *path);
-void tst_sys_conf_set(const char *path, const char *value);
+void tst_sys_conf_save_str(const char *path, const char *value);
+int tst_sys_conf_save(const struct tst_path_val *conf);
void tst_sys_conf_restore(int verbose);
void tst_sys_conf_dump(void);
@@ -20,6 +20,14 @@ struct tst_sys_conf {
static struct tst_sys_conf *save_restore_data;
+static void print_error(int info_only, const char *err, const char *path)
+{
+ if (info_only)
+ tst_res(TINFO | TERRNO, err, path);
+ else
+ tst_brk(TBROK | TERRNO, err, path);
+}
+
void tst_sys_conf_dump(void)
{
struct tst_sys_conf *i;
@@ -28,7 +36,7 @@ void tst_sys_conf_dump(void)
tst_res(TINFO, "%s = %s", i->path, i->value);
}
-int tst_sys_conf_save_str(const char *path, const char *value)
+void tst_sys_conf_save_str(const char *path, const char *value)
{
struct tst_sys_conf *n = SAFE_MALLOC(sizeof(*n));
@@ -40,45 +48,45 @@ int tst_sys_conf_save_str(const char *path, const char *value)
n->next = save_restore_data;
save_restore_data = n;
-
- return 0;
}
-int tst_sys_conf_save(const char *path)
+int tst_sys_conf_save(const struct tst_path_val *conf)
{
char line[PATH_MAX];
+ int ttype, iret;
FILE *fp;
void *ret;
- char flag;
- if (!path)
+ if (!conf || !conf->path)
tst_brk(TBROK, "path is empty");
- flag = path[0];
- if (flag == '?' || flag == '!')
- path++;
-
- if (access(path, F_OK) != 0) {
- switch (flag) {
- case '?':
- tst_res(TINFO, "Path not found: '%s'", path);
- break;
- case '!':
- tst_brk(TBROK|TERRNO, "Path not found: '%s'", path);
- break;
- default:
- tst_brk(TCONF|TERRNO, "Path not found: '%s'", path);
+ if (access(conf->path, F_OK) != 0) {
+ if (conf->flags & TST_SR_SKIP_MISSING) {
+ tst_res(TINFO | TERRNO, "Path not found: %s",
+ conf->path);
+ return 1;
}
- return 1;
+
+ ttype = (conf->flags & TST_SR_TBROK_MISSING) ? TBROK : TCONF;
+ tst_brk(ttype | TERRNO, "Path not found: %s", conf->path);
}
- fp = fopen(path, "r");
- if (fp == NULL) {
- if (flag == '?')
+ if (access(conf->path, W_OK) != 0) {
+ if (conf->flags & TST_SR_SKIP_RO) {
+ tst_res(TINFO | TERRNO, "Path is not writable: %s",
+ conf->path);
return 1;
+ }
- tst_brk(TBROK | TERRNO, "Failed to open FILE '%s' for reading",
- path);
+ ttype = (conf->flags & TST_SR_TBROK_RO) ? TBROK : TCONF;
+ tst_brk(ttype | TERRNO, "Path is not writable: %s", conf->path);
+ }
+
+ fp = fopen(conf->path, "r");
+
+ if (fp == NULL) {
+ print_error(conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to open '%s' for reading", conf->path);
return 1;
}
@@ -86,24 +94,41 @@ int tst_sys_conf_save(const char *path)
fclose(fp);
if (ret == NULL) {
- if (flag == '?')
+ if (conf->flags & TST_SR_IGNORE_ERR)
return 1;
tst_brk(TBROK | TERRNO, "Failed to read anything from '%s'",
- path);
+ conf->path);
}
- return tst_sys_conf_save_str(path, line);
-}
+ tst_sys_conf_save_str(conf->path, line);
-void tst_sys_conf_set(const char *path, const char *value)
-{
- char flag = path[0];
- if (flag == '?' || flag == '!')
- path++;
+ if (!conf->val)
+ return 0;
+
+ fp = fopen(conf->path, "w");
+
+ if (fp == NULL) {
+ print_error(conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to open '%s' for writing", conf->path);
+ return 0;
+ }
+
+ iret = fputs(conf->val, fp);
- if (value)
- SAFE_FILE_PRINTF(path, "%s", value);
+ if (iret < 0) {
+ print_error(conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to write into '%s'", conf->path);
+ }
+
+ iret = fclose(fp);
+
+ if (iret < 0) {
+ print_error(conf->flags & TST_SR_IGNORE_ERR,
+ "Failed to close '%s'", conf->path);
+ }
+
+ return 0;
}
void tst_sys_conf_restore(int verbose)
@@ -1203,8 +1203,7 @@ static void do_setup(int argc, char *argv[])
const struct tst_path_val *pvl = tst_test->save_restore;
while (pvl->path) {
- if (!tst_sys_conf_save(pvl->path))
- tst_sys_conf_set(pvl->path, pvl->val);
+ tst_sys_conf_save(pvl);
pvl++;
}
}
@@ -269,7 +269,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -135,7 +135,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -217,7 +217,8 @@ static struct tst_test test = {
.cleanup = tst_kvm_cleanup,
.needs_root = 1,
.save_restore = (const struct tst_path_val[]) {
- {"?/sys/module/kvm/parameters/tdp_mmu", "0"},
+ {"/sys/module/kvm/parameters/tdp_mmu", "0",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.supported_archs = (const char *const []) {
@@ -131,7 +131,7 @@ static struct tst_test test = {
.needs_hugetlbfs = 1,
.forks_child = 1,
.save_restore = (const struct tst_path_val[]) {
- {PATH_OC_HPAGES, NULL},
+ {PATH_OC_HPAGES, NULL, TST_SR_TCONF},
{}
},
.tcnt = 2,
@@ -80,10 +80,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -101,10 +101,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -83,10 +83,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -103,10 +103,12 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"?/sys/kernel/mm/ksm/merge_across_nodes", "1"},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -89,7 +89,7 @@ static struct tst_test test = {
.test_all = test_ksm,
.min_kver = "2.6.32",
.save_restore = (const struct tst_path_val[]) {
- {"!/sys/kernel/mm/ksm/run", "1"},
+ {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -137,10 +137,11 @@ static struct tst_test test = {
},
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"?/sys/kernel/mm/ksm/max_page_sharing", NULL},
- {"!/sys/kernel/mm/ksm/run", NULL},
- {"!/sys/kernel/mm/ksm/sleep_millisecs", NULL},
- {"/sys/kernel/mm/ksm/merge_across_nodes", NULL},
+ {"/sys/kernel/mm/ksm/max_page_sharing", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK},
+ {"/sys/kernel/mm/ksm/merge_across_nodes", NULL, TST_SR_TCONF},
{}
},
.needs_kconfigs = (const char *const[]){
@@ -220,9 +220,12 @@ static struct tst_test test = {
.forks_child = 1,
.cleanup = cleanup,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/kernel/keys/gc_delay", "1"},
- {"?/proc/sys/kernel/keys/maxkeys", "200"},
- {"?/proc/sys/kernel/keys/maxbytes", "20000"},
+ {"/proc/sys/kernel/keys/gc_delay", "1",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/proc/sys/kernel/keys/maxkeys", "200",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
+ {"/proc/sys/kernel/keys/maxbytes", "20000",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.bufs = (struct tst_buffers []) {
@@ -110,7 +110,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -113,7 +113,7 @@ static struct tst_test test = {
.max_runtime = 600,
.test_all = check,
.save_restore = (const struct tst_path_val[]) {
- {"!/proc/sys/kernel/pid_max", PID_MAX_STR},
+ {"/proc/sys/kernel/pid_max", PID_MAX_STR, TST_SR_TBROK},
{}
},
.tags = (const struct tst_tag[]) {
@@ -76,7 +76,7 @@ static struct tst_test test = {
.cleanup = cleanup,
.test_all = verify_msgget,
.save_restore = (const struct tst_path_val[]){
- {"/proc/sys/kernel/msgmni", NULL},
+ {"/proc/sys/kernel/msgmni", NULL, TST_SR_TCONF},
{}
}
};
@@ -239,7 +239,8 @@ static struct tst_test test = {
.needs_tmpdir = 1,
.needs_root = 1,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/vm/swappiness", NULL},
+ {"/proc/sys/vm/swappiness", NULL,
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
.needs_cgroup_ctrls = (const char *const []){ "memory", NULL },
@@ -213,7 +213,7 @@ static struct tst_test test = {
.needs_root = 1,
.forks_child = 1,
.save_restore = (const struct tst_path_val[]) {
- {CORE_PATTERN, NULL},
+ {CORE_PATTERN, NULL, TST_SR_TCONF},
{}
},
};
@@ -327,7 +327,8 @@ static struct tst_test test = {
.test_all = run,
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/kernel/numa_balancing", "0"},
+ {"/proc/sys/kernel/numa_balancing", "0",
+ TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
{}
},
};
@@ -218,7 +218,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -102,7 +102,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -130,7 +130,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -143,7 +143,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -159,7 +159,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -124,7 +124,7 @@ static struct tst_test test = {
NULL
},
.save_restore = (const struct tst_path_val[]) {
- {"?/proc/sys/user/max_user_namespaces", NULL},
+ {"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{}
},
.tags = (const struct tst_tag[]) {
@@ -58,7 +58,7 @@ static void run(unsigned int n)
static struct tst_test test = {
.test = run,
.save_restore = (const struct tst_path_val[]) {
- {"!/proc/sys/kernel/printk", NULL},
+ {"/proc/sys/kernel/printk", NULL, TST_SR_TBROK},
{}
},
.needs_root = 1,
Tests using the .save_restore functionality currently cannot run without root privileges at all because the test will write into the path at least at the end and trigger error, even when the config paths are flagged as optional. Introduce new tst_path_val field for flags and replace path prefix flags with bit flags. Also introduce new flags to control handling of read/write errors and read-only sysfiles and rewrite save_restore implementation accordingly. Signed-off-by: Martin Doucha <mdoucha@suse.cz> --- Changes since v1: - TST_SR_IF_ACCESS => TST_SR_COND_ACCESS - TST_SR_IGNORE_RO => TST_SR_SKIP_RO - TST_SR_IGNORE_MISSING => TST_SR_SKIP_MISSING (to match the IGNORE_RO change) Changes since v2: - Fixed save_restore example in C Test API docs Changes since v3: - Renamed flags to match tst_brk() constants - Added TERRNO to tst_res(TINFO) messages in tst_sys_conf.c - Added save_restore flags to newly added hugemmap08 I've removed Jan Stancek's ack because the changes are rather significant. Please review again. I'll send a follow-up patchset to replace setup() code which requires root privileges without good reason after this patch gets merged. Here I've kept test changes to the minimum needed to maintain current save_restore behavior with the new flags system. The only change in behavior is the use of read-only handling flags where it's clear that the change is desired. Though a few tests should get closer attention during review: - all KSM tests - add_key05 - migrate_pages02 doc/c-test-api.txt | 40 ++++---- include/tst_sys_conf.h | 18 +++- lib/tst_sys_conf.c | 99 ++++++++++++------- lib/tst_test.c | 3 +- testcases/cve/icmp_rate_limit01.c | 2 +- testcases/kernel/containers/userns/userns08.c | 2 +- testcases/kernel/kvm/kvm_pagefault01.c | 3 +- .../kernel/mem/hugetlb/hugemmap/hugemmap08.c | 2 +- testcases/kernel/mem/ksm/ksm01.c | 10 +- testcases/kernel/mem/ksm/ksm02.c | 10 +- testcases/kernel/mem/ksm/ksm03.c | 10 +- testcases/kernel/mem/ksm/ksm04.c | 10 +- testcases/kernel/mem/ksm/ksm05.c | 2 +- testcases/kernel/mem/ksm/ksm06.c | 9 +- testcases/kernel/syscalls/add_key/add_key05.c | 9 +- testcases/kernel/syscalls/bind/bind06.c | 2 +- testcases/kernel/syscalls/fork/fork13.c | 2 +- .../kernel/syscalls/ipc/msgget/msgget03.c | 2 +- testcases/kernel/syscalls/madvise/madvise06.c | 3 +- testcases/kernel/syscalls/madvise/madvise08.c | 2 +- .../syscalls/migrate_pages/migrate_pages02.c | 3 +- testcases/kernel/syscalls/sendto/sendto03.c | 2 +- .../kernel/syscalls/setsockopt/setsockopt05.c | 2 +- .../kernel/syscalls/setsockopt/setsockopt06.c | 2 +- .../kernel/syscalls/setsockopt/setsockopt07.c | 2 +- .../kernel/syscalls/setsockopt/setsockopt08.c | 2 +- .../kernel/syscalls/setsockopt/setsockopt09.c | 2 +- testcases/kernel/syscalls/syslog/syslog11.c | 2 +- 28 files changed, 156 insertions(+), 101 deletions(-)