Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tests: Use scandirat for shell script finding

Avoid filename appending buffers by using openat, faccessat and
scandirat more widely. Turn the script's path back to a file name
using readlink from /proc/<pid>/fd/<fd>.

Read the script's description using api/io.h to avoid fdopen
conversions. Whilst reading perform additional sanity checks on the
script's contents.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@arm.com>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Bill Wendling <morbo@google.com>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Yang Jihong <yangjihong1@huawei.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Athira Jajeev <atrajeev@linux.vnet.ibm.com>
Cc: llvm@lists.linux.dev
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20240221034155.1500118-7-irogers@google.com

authored by

Ian Rogers and committed by
Namhyung Kim
f3295f5b d5bcade9

+95 -71
+8 -12
tools/perf/tests/builtin-test.c
··· 300 300 } 301 301 302 302 struct shell_test { 303 - const char *dir; 304 303 const char *file; 305 304 }; 306 305 307 306 static int shell_test__run(struct test_suite *test, int subdir __maybe_unused) 308 307 { 309 308 int err; 310 - char script[PATH_MAX]; 311 309 struct shell_test *st = test->priv; 310 + char *cmd = NULL; 312 311 313 - path__join(script, sizeof(script) - 3, st->dir, st->file); 314 - 315 - if (verbose > 0) 316 - strncat(script, " -v", sizeof(script) - strlen(script) - 1); 317 - 318 - err = system(script); 312 + if (asprintf(&cmd, "%s%s", st->file, verbose ? " -v" : "") < 0) 313 + return TEST_FAIL; 314 + err = system(cmd); 315 + free(cmd); 319 316 if (!err) 320 317 return TEST_OK; 321 318 ··· 328 331 files = list_script_files(); 329 332 if (!files) 330 333 return 0; 331 - for (file = files; file->dir; file++) { 334 + for (file = files; file->file; file++) { 332 335 int curr = i++; 333 336 struct test_case test_cases[] = { 334 337 { ··· 342 345 .test_cases = test_cases, 343 346 .priv = &st, 344 347 }; 345 - st.dir = file->dir; 348 + st.file = file->file; 346 349 347 350 if (test_suite.desc == NULL || 348 351 !perf_test__matches(test_suite.desc, curr, argc, argv)) 349 352 continue; 350 353 351 - st.file = file->file; 352 354 pr_info("%3d: %-*s:", i, width, test_suite.desc); 353 355 354 356 if (intlist__find(skiplist, i)) { ··· 451 455 files = list_script_files(); 452 456 if (!files) 453 457 return 0; 454 - for (file = files; file->dir; file++) { 458 + for (file = files; file->file; file++) { 455 459 int curr = i++; 456 460 struct test_suite t = { 457 461 .desc = file->desc
+87 -58
tools/perf/tests/tests-scripts.c
··· 14 14 #include <subcmd/parse-options.h> 15 15 #include <sys/wait.h> 16 16 #include <sys/stat.h> 17 + #include <api/io.h> 17 18 #include "builtin.h" 18 19 #include "tests-scripts.h" 19 20 #include "color.h" ··· 25 24 #include "symbol.h" 26 25 #include "tests.h" 27 26 #include "util/rlimit.h" 27 + #include "util/util.h" 28 28 29 29 30 30 /* ··· 37 35 static struct script_file *files = NULL; 38 36 static int files_max_width = 0; 39 37 40 - static const char *shell_tests__dir(char *path, size_t size) 38 + static int shell_tests__dir_fd(void) 41 39 { 42 - const char *devel_dirs[] = { "./tools/perf/tests", "./tests", }; 43 - char *exec_path; 44 - unsigned int i; 40 + char path[PATH_MAX], *exec_path; 41 + static const char * const devel_dirs[] = { "./tools/perf/tests/shell", "./tests/shell", }; 45 42 46 - for (i = 0; i < ARRAY_SIZE(devel_dirs); ++i) { 47 - struct stat st; 43 + for (size_t i = 0; i < ARRAY_SIZE(devel_dirs); ++i) { 44 + int fd = open(devel_dirs[i], O_PATH); 48 45 49 - if (!lstat(devel_dirs[i], &st)) { 50 - scnprintf(path, size, "%s/shell", devel_dirs[i]); 51 - if (!lstat(devel_dirs[i], &st)) 52 - return path; 53 - } 46 + if (fd >= 0) 47 + return fd; 54 48 } 55 49 56 50 /* Then installed path. */ 57 51 exec_path = get_argv_exec_path(); 58 - scnprintf(path, size, "%s/tests/shell", exec_path); 52 + scnprintf(path, sizeof(path), "%s/tests/shell", exec_path); 59 53 free(exec_path); 60 - return path; 54 + return open(path, O_PATH); 61 55 } 62 56 63 - static const char *shell_test__description(char *description, size_t size, 64 - const char *path, const char *name) 57 + static char *shell_test__description(int dir_fd, const char *name) 65 58 { 66 - FILE *fp; 67 - char filename[PATH_MAX]; 68 - int ch; 59 + struct io io; 60 + char buf[128], desc[256]; 61 + int ch, pos = 0; 69 62 70 - path__join(filename, sizeof(filename), path, name); 71 - fp = fopen(filename, "r"); 72 - if (!fp) 63 + io__init(&io, openat(dir_fd, name, O_RDONLY), buf, sizeof(buf)); 64 + if (io.fd < 0) 73 65 return NULL; 74 66 75 67 /* Skip first line - should be #!/bin/sh Shebang */ 68 + if (io__get_char(&io) != '#') 69 + goto err_out; 70 + if (io__get_char(&io) != '!') 71 + goto err_out; 76 72 do { 77 - ch = fgetc(fp); 78 - } while (ch != EOF && ch != '\n'); 73 + ch = io__get_char(&io); 74 + if (ch < 0) 75 + goto err_out; 76 + } while (ch != '\n'); 79 77 80 - description = fgets(description, size, fp); 81 - fclose(fp); 82 - 83 - /* Assume first char on line is omment everything after that desc */ 84 - return description ? strim(description + 1) : NULL; 78 + do { 79 + ch = io__get_char(&io); 80 + if (ch < 0) 81 + goto err_out; 82 + } while (ch == '#' || isspace(ch)); 83 + while (ch > 0 && ch != '\n') { 84 + desc[pos++] = ch; 85 + if (pos >= (int)sizeof(desc) - 1) 86 + break; 87 + ch = io__get_char(&io); 88 + } 89 + while (pos > 0 && isspace(desc[--pos])) 90 + ; 91 + desc[++pos] = '\0'; 92 + close(io.fd); 93 + return strdup(desc); 94 + err_out: 95 + close(io.fd); 96 + return NULL; 85 97 } 86 98 87 99 /* Is this full file path a shell script */ 88 - static bool is_shell_script(const char *path) 100 + static bool is_shell_script(int dir_fd, const char *path) 89 101 { 90 102 const char *ext; 91 103 ··· 107 91 if (!ext) 108 92 return false; 109 93 if (!strcmp(ext, ".sh")) { /* Has .sh extension */ 110 - if (access(path, R_OK | X_OK) == 0) /* Is executable */ 94 + if (faccessat(dir_fd, path, R_OK | X_OK, 0) == 0) /* Is executable */ 111 95 return true; 112 96 } 113 97 return false; 114 98 } 115 99 116 100 /* Is this file in this dir a shell script (for test purposes) */ 117 - static bool is_test_script(const char *path, const char *name) 101 + static bool is_test_script(int dir_fd, const char *name) 118 102 { 119 - char filename[PATH_MAX]; 120 - 121 - path__join(filename, sizeof(filename), path, name); 122 - if (!is_shell_script(filename)) return false; 123 - return true; 103 + return is_shell_script(dir_fd, name); 124 104 } 125 105 126 106 /* Duplicate a string and fall over and die if we run out of memory */ ··· 132 120 return newstr; 133 121 } 134 122 135 - static void append_script(const char *dir, const char *file, const char *desc) 123 + static void append_script(int dir_fd, const char *name, char *desc) 136 124 { 125 + char filename[PATH_MAX], link[128]; 137 126 struct script_file *files_tmp; 138 - size_t files_num_tmp; 127 + size_t files_num_tmp, len; 139 128 int width; 140 129 130 + snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd); 131 + len = readlink(link, filename, sizeof(filename)); 132 + if (len < 0) { 133 + pr_err("Failed to readlink %s", link); 134 + return; 135 + } 136 + filename[len++] = '/'; 137 + strcpy(&filename[len], name); 141 138 files_num_tmp = files_num + 1; 142 139 if (files_num_tmp >= SIZE_MAX) { 143 140 pr_err("Too many script files\n"); ··· 163 142 /* Add file to end and NULL terminate the struct array */ 164 143 files = files_tmp; 165 144 files_num = files_num_tmp; 166 - files[files_num - 1].dir = strdup_check(dir); 167 - files[files_num - 1].file = strdup_check(file); 168 - files[files_num - 1].desc = strdup_check(desc); 169 - files[files_num].dir = NULL; 145 + files[files_num - 1].file = strdup_check(filename); 146 + files[files_num - 1].desc = desc; 170 147 files[files_num].file = NULL; 171 148 files[files_num].desc = NULL; 172 149 ··· 173 154 files_max_width = width; 174 155 } 175 156 176 - static void append_scripts_in_dir(const char *path) 157 + static void append_scripts_in_dir(int dir_fd) 177 158 { 178 159 struct dirent **entlist; 179 160 struct dirent *ent; 180 161 int n_dirs, i; 181 - char filename[PATH_MAX]; 182 162 183 163 /* List files, sorted by alpha */ 184 - n_dirs = scandir(path, &entlist, NULL, alphasort); 164 + n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort); 185 165 if (n_dirs == -1) 186 166 return; 187 167 for (i = 0; i < n_dirs && (ent = entlist[i]); i++) { 168 + int fd; 169 + 188 170 if (ent->d_name[0] == '.') 189 171 continue; /* Skip hidden files */ 190 - if (is_test_script(path, ent->d_name)) { /* It's a test */ 191 - char bf[256]; 192 - const char *desc = shell_test__description 193 - (bf, sizeof(bf), path, ent->d_name); 172 + if (is_test_script(dir_fd, ent->d_name)) { /* It's a test */ 173 + char *desc = shell_test__description(dir_fd, ent->d_name); 194 174 195 175 if (desc) /* It has a desc line - valid script */ 196 - append_script(path, ent->d_name, desc); 197 - } else if (is_directory(path, ent)) { /* Scan the subdir */ 198 - path__join(filename, sizeof(filename), 199 - path, ent->d_name); 200 - append_scripts_in_dir(filename); 176 + append_script(dir_fd, ent->d_name, desc); 177 + continue; 201 178 } 179 + if (ent->d_type != DT_DIR) { 180 + struct stat st; 181 + 182 + if (ent->d_type != DT_UNKNOWN) 183 + continue; 184 + fstatat(dir_fd, ent->d_name, &st, 0); 185 + if (!S_ISDIR(st.st_mode)) 186 + continue; 187 + } 188 + fd = openat(dir_fd, ent->d_name, O_PATH); 189 + append_scripts_in_dir(fd); 202 190 } 203 191 for (i = 0; i < n_dirs; i++) /* Clean up */ 204 192 zfree(&entlist[i]); ··· 214 188 215 189 const struct script_file *list_script_files(void) 216 190 { 217 - char path_dir[PATH_MAX]; 218 - const char *path; 191 + int dir_fd; 219 192 220 193 if (files) 221 194 return files; /* Singleton - we already know our list */ 222 195 223 - path = shell_tests__dir(path_dir, sizeof(path_dir)); /* Walk dir */ 224 - append_scripts_in_dir(path); 196 + dir_fd = shell_tests__dir_fd(); /* Walk dir */ 197 + if (dir_fd < 0) 198 + return NULL; 199 + 200 + append_scripts_in_dir(dir_fd); 201 + close(dir_fd); 225 202 226 203 return files; 227 204 }
-1
tools/perf/tests/tests-scripts.h
··· 3 3 #define TESTS_SCRIPTS_H 4 4 5 5 struct script_file { 6 - char *dir; 7 6 char *file; 8 7 char *desc; 9 8 };