1 // SPDX-License-Identifier: GPL-2.0 2 #include <assert.h> 3 #include <ctype.h> 4 #include <errno.h> 5 #include <limits.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/vfs.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include <fcntl.h> 14 #include <pthread.h> 15 #include <unistd.h> 16 #include <sys/mount.h> 17 18 #include "fs.h" 19 #include "../io.h" 20 #include "debug-internal.h" 21 22 #define _STR(x) #x 23 #define STR(x) _STR(x) 24 25 #ifndef SYSFS_MAGIC 26 #define SYSFS_MAGIC 0x62656572 27 #endif 28 29 #ifndef PROC_SUPER_MAGIC 30 #define PROC_SUPER_MAGIC 0x9fa0 31 #endif 32 33 #ifndef DEBUGFS_MAGIC 34 #define DEBUGFS_MAGIC 0x64626720 35 #endif 36 37 #ifndef TRACEFS_MAGIC 38 #define TRACEFS_MAGIC 0x74726163 39 #endif 40 41 #ifndef HUGETLBFS_MAGIC 42 #define HUGETLBFS_MAGIC 0x958458f6 43 #endif 44 45 #ifndef BPF_FS_MAGIC 46 #define BPF_FS_MAGIC 0xcafe4a11 47 #endif 48 49 static const char * const sysfs__known_mountpoints[] = { 50 "/sys", 51 0, 52 }; 53 54 static const char * const procfs__known_mountpoints[] = { 55 "/proc", 56 0, 57 }; 58 59 #ifndef DEBUGFS_DEFAULT_PATH 60 #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug" 61 #endif 62 63 static const char * const debugfs__known_mountpoints[] = { 64 DEBUGFS_DEFAULT_PATH, 65 "/debug", 66 0, 67 }; 68 69 70 #ifndef TRACEFS_DEFAULT_PATH 71 #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing" 72 #endif 73 74 static const char * const tracefs__known_mountpoints[] = { 75 TRACEFS_DEFAULT_PATH, 76 "/sys/kernel/debug/tracing", 77 "/tracing", 78 "/trace", 79 0, 80 }; 81 82 static const char * const hugetlbfs__known_mountpoints[] = { 83 0, 84 }; 85 86 static const char * const bpf_fs__known_mountpoints[] = { 87 "/sys/fs/bpf", 88 0, 89 }; 90 91 struct fs { 92 const char * const name; 93 const char * const * const mounts; 94 char *path; 95 pthread_mutex_t mount_mutex; 96 const long magic; 97 }; 98 99 #ifndef TRACEFS_MAGIC 100 #define TRACEFS_MAGIC 0x74726163 101 #endif 102 103 static void fs__init_once(struct fs *fs); 104 static const char *fs__mountpoint(const struct fs *fs); 105 static const char *fs__mount(struct fs *fs); 106 107 #define FS(lower_name, fs_name, upper_name) \ 108 static struct fs fs__##lower_name = { \ 109 .name = #fs_name, \ 110 .mounts = lower_name##__known_mountpoints, \ 111 .magic = upper_name##_MAGIC, \ 112 .mount_mutex = PTHREAD_MUTEX_INITIALIZER, \ 113 }; \ 114 \ 115 static void lower_name##_init_once(void) \ 116 { \ 117 struct fs *fs = &fs__##lower_name; \ 118 \ 119 fs__init_once(fs); \ 120 } \ 121 \ 122 const char *lower_name##__mountpoint(void) \ 123 { \ 124 static pthread_once_t init_once = PTHREAD_ONCE_INIT; \ 125 struct fs *fs = &fs__##lower_name; \ 126 \ 127 pthread_once(&init_once, lower_name##_init_once); \ 128 return fs__mountpoint(fs); \ 129 } \ 130 \ 131 const char *lower_name##__mount(void) \ 132 { \ 133 const char *mountpoint = lower_name##__mountpoint(); \ 134 struct fs *fs = &fs__##lower_name; \ 135 \ 136 if (mountpoint) \ 137 return mountpoint; \ 138 \ 139 return fs__mount(fs); \ 140 } \ 141 \ 142 bool lower_name##__configured(void) \ 143 { \ 144 return lower_name##__mountpoint() != NULL; \ 145 } 146 147 FS(sysfs, sysfs, SYSFS); 148 FS(procfs, procfs, PROC_SUPER); 149 FS(debugfs, debugfs, DEBUGFS); 150 FS(tracefs, tracefs, TRACEFS); 151 FS(hugetlbfs, hugetlbfs, HUGETLBFS); 152 FS(bpf_fs, bpf, BPF_FS); 153 154 static bool fs__read_mounts(struct fs *fs) 155 { 156 char type[100]; 157 FILE *fp; 158 char path[PATH_MAX + 1]; 159 160 fp = fopen("/proc/mounts", "r"); 161 if (fp == NULL) 162 return false; 163 164 while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", 165 path, type) == 2) { 166 167 if (strcmp(type, fs->name) == 0) { 168 fs->path = strdup(path); 169 fclose(fp); 170 return fs->path != NULL; 171 } 172 } 173 fclose(fp); 174 return false; 175 } 176 177 static int fs__valid_mount(const char *fs, long magic) 178 { 179 struct statfs st_fs; 180 181 if (statfs(fs, &st_fs) < 0) 182 return -ENOENT; 183 else if ((long)st_fs.f_type != magic) 184 return -ENOENT; 185 186 return 0; 187 } 188 189 static bool fs__check_mounts(struct fs *fs) 190 { 191 const char * const *ptr; 192 193 ptr = fs->mounts; 194 while (*ptr) { 195 if (fs__valid_mount(*ptr, fs->magic) == 0) { 196 fs->path = strdup(*ptr); 197 if (!fs->path) 198 return false; 199 return true; 200 } 201 ptr++; 202 } 203 204 return false; 205 } 206 207 static void mem_toupper(char *f, size_t len) 208 { 209 while (len) { 210 *f = toupper(*f); 211 f++; 212 len--; 213 } 214 } 215 216 /* 217 * Check for "NAME_PATH" environment variable to override fs location (for 218 * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst 219 * for SYSFS_PATH. 220 */ 221 static bool fs__env_override(struct fs *fs) 222 { 223 char *override_path; 224 size_t name_len = strlen(fs->name); 225 /* name + "_PATH" + '\0' */ 226 char upper_name[name_len + 5 + 1]; 227 228 memcpy(upper_name, fs->name, name_len); 229 mem_toupper(upper_name, name_len); 230 strcpy(&upper_name[name_len], "_PATH"); 231 232 override_path = getenv(upper_name); 233 if (!override_path) 234 return false; 235 236 fs->path = strdup(override_path); 237 if (!fs->path) 238 return false; 239 return true; 240 } 241 242 static void fs__init_once(struct fs *fs) 243 { 244 if (!fs__env_override(fs) && 245 !fs__check_mounts(fs) && 246 !fs__read_mounts(fs)) { 247 assert(!fs->path); 248 } else { 249 assert(fs->path); 250 } 251 } 252 253 static const char *fs__mountpoint(const struct fs *fs) 254 { 255 return fs->path; 256 } 257 258 static const char *mount_overload(struct fs *fs) 259 { 260 size_t name_len = strlen(fs->name); 261 /* "PERF_" + name + "_ENVIRONMENT" + '\0' */ 262 char upper_name[5 + name_len + 12 + 1]; 263 264 snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name); 265 mem_toupper(upper_name, name_len); 266 267 return getenv(upper_name) ?: *fs->mounts; 268 } 269 270 static const char *fs__mount(struct fs *fs) 271 { 272 const char *mountpoint; 273 274 pthread_mutex_lock(&fs->mount_mutex); 275 276 /* Check if path found inside the mutex to avoid races with other callers of mount. */ 277 mountpoint = fs__mountpoint(fs); 278 if (mountpoint) 279 goto out; 280 281 mountpoint = mount_overload(fs); 282 283 if (mount(NULL, mountpoint, fs->name, 0, NULL) == 0 && 284 fs__valid_mount(mountpoint, fs->magic) == 0) { 285 fs->path = strdup(mountpoint); 286 mountpoint = fs->path; 287 } 288 out: 289 pthread_mutex_unlock(&fs->mount_mutex); 290 return mountpoint; 291 } 292 293 int filename__read_int(const char *filename, int *value) 294 { 295 char line[64]; 296 int fd = open(filename, O_RDONLY), err = -1; 297 298 if (fd < 0) 299 return -1; 300 301 if (read(fd, line, sizeof(line)) > 0) { 302 *value = atoi(line); 303 err = 0; 304 } 305 306 close(fd); 307 return err; 308 } 309 310 static int filename__read_ull_base(const char *filename, 311 unsigned long long *value, int base) 312 { 313 char line[64]; 314 int fd = open(filename, O_RDONLY), err = -1; 315 316 if (fd < 0) 317 return -1; 318 319 if (read(fd, line, sizeof(line)) > 0) { 320 *value = strtoull(line, NULL, base); 321 if (*value != ULLONG_MAX) 322 err = 0; 323 } 324 325 close(fd); 326 return err; 327 } 328 329 /* 330 * Parses @value out of @filename with strtoull. 331 * By using 16 for base to treat the number as hex. 332 */ 333 int filename__read_xll(const char *filename, unsigned long long *value) 334 { 335 return filename__read_ull_base(filename, value, 16); 336 } 337 338 /* 339 * Parses @value out of @filename with strtoull. 340 * By using 0 for base, the strtoull detects the 341 * base automatically (see man strtoull). 342 */ 343 int filename__read_ull(const char *filename, unsigned long long *value) 344 { 345 return filename__read_ull_base(filename, value, 0); 346 } 347 348 int filename__read_str(const char *filename, char **buf, size_t *sizep) 349 { 350 struct io io; 351 char bf[128]; 352 int err; 353 354 io.fd = open(filename, O_RDONLY); 355 if (io.fd < 0) 356 return -errno; 357 io__init(&io, io.fd, bf, sizeof(bf)); 358 *buf = NULL; 359 err = io__getdelim(&io, buf, sizep, /*delim=*/-1); 360 if (err < 0) { 361 free(*buf); 362 *buf = NULL; 363 } else 364 err = 0; 365 close(io.fd); 366 return err; 367 } 368 369 int filename__write_int(const char *filename, int value) 370 { 371 int fd = open(filename, O_WRONLY), err = -1; 372 char buf[64]; 373 374 if (fd < 0) 375 return err; 376 377 sprintf(buf, "%d", value); 378 if (write(fd, buf, sizeof(buf)) == sizeof(buf)) 379 err = 0; 380 381 close(fd); 382 return err; 383 } 384 385 int procfs__read_str(const char *entry, char **buf, size_t *sizep) 386 { 387 char path[PATH_MAX]; 388 const char *procfs = procfs__mountpoint(); 389 390 if (!procfs) 391 return -1; 392 393 snprintf(path, sizeof(path), "%s/%s", procfs, entry); 394 395 return filename__read_str(path, buf, sizep); 396 } 397 398 static int sysfs__read_ull_base(const char *entry, 399 unsigned long long *value, int base) 400 { 401 char path[PATH_MAX]; 402 const char *sysfs = sysfs__mountpoint(); 403 404 if (!sysfs) 405 return -1; 406 407 snprintf(path, sizeof(path), "%s/%s", sysfs, entry); 408 409 return filename__read_ull_base(path, value, base); 410 } 411 412 int sysfs__read_xll(const char *entry, unsigned long long *value) 413 { 414 return sysfs__read_ull_base(entry, value, 16); 415 } 416 417 int sysfs__read_ull(const char *entry, unsigned long long *value) 418 { 419 return sysfs__read_ull_base(entry, value, 0); 420 } 421 422 int sysfs__read_int(const char *entry, int *value) 423 { 424 char path[PATH_MAX]; 425 const char *sysfs = sysfs__mountpoint(); 426 427 if (!sysfs) 428 return -1; 429 430 snprintf(path, sizeof(path), "%s/%s", sysfs, entry); 431 432 return filename__read_int(path, value); 433 } 434 435 int sysfs__read_str(const char *entry, char **buf, size_t *sizep) 436 { 437 char path[PATH_MAX]; 438 const char *sysfs = sysfs__mountpoint(); 439 440 if (!sysfs) 441 return -1; 442 443 snprintf(path, sizeof(path), "%s/%s", sysfs, entry); 444 445 return filename__read_str(path, buf, sizep); 446 } 447 448 int sysfs__read_bool(const char *entry, bool *value) 449 { 450 struct io io; 451 char bf[16]; 452 int ret = 0; 453 char path[PATH_MAX]; 454 const char *sysfs = sysfs__mountpoint(); 455 456 if (!sysfs) 457 return -1; 458 459 snprintf(path, sizeof(path), "%s/%s", sysfs, entry); 460 io.fd = open(path, O_RDONLY); 461 if (io.fd < 0) 462 return -errno; 463 464 io__init(&io, io.fd, bf, sizeof(bf)); 465 switch (io__get_char(&io)) { 466 case '1': 467 case 'y': 468 case 'Y': 469 *value = true; 470 break; 471 case '': 472 case 'n': 473 case 'N': 474 *value = false; 475 break; 476 default: 477 ret = -1; 478 } 479 close(io.fd); 480 481 return ret; 482 } 483 int sysctl__read_int(const char *sysctl, int *value) 484 { 485 char path[PATH_MAX]; 486 const char *procfs = procfs__mountpoint(); 487 488 if (!procfs) 489 return -1; 490 491 snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl); 492 493 return filename__read_int(path, value); 494 } 495 496 int sysfs__write_int(const char *entry, int value) 497 { 498 char path[PATH_MAX]; 499 const char *sysfs = sysfs__mountpoint(); 500 501 if (!sysfs) 502 return -1; 503 504 if (snprintf(path, sizeof(path), "%s/%s", sysfs, entry) >= PATH_MAX) 505 return -1; 506 507 return filename__write_int(path, value); 508 } 509
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.