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 49static const char * const sysfs__known_mountpoints[] = { 50 "/sys", 51 0, 52}; 53 54static 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 63static 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 74static const char * const tracefs__known_mountpoints[] = { 75 TRACEFS_DEFAULT_PATH, 76 "/sys/kernel/debug/tracing", 77 "/tracing", 78 "/trace", 79 0, 80}; 81 82static const char * const hugetlbfs__known_mountpoints[] = { 83 0, 84}; 85 86static const char * const bpf_fs__known_mountpoints[] = { 87 "/sys/fs/bpf", 88 0, 89}; 90 91struct 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 103static void fs__init_once(struct fs *fs); 104static const char *fs__mountpoint(const struct fs *fs); 105static const char *fs__mount(struct fs *fs); 106 107#define FS(lower_name, fs_name, upper_name) \ 108static 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 \ 115static void lower_name##_init_once(void) \ 116{ \ 117 struct fs *fs = &fs__##lower_name; \ 118 \ 119 fs__init_once(fs); \ 120} \ 121 \ 122const 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 \ 131const 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 \ 142bool lower_name##__configured(void) \ 143{ \ 144 return lower_name##__mountpoint() != NULL; \ 145} 146 147FS(sysfs, sysfs, SYSFS); 148FS(procfs, procfs, PROC_SUPER); 149FS(debugfs, debugfs, DEBUGFS); 150FS(tracefs, tracefs, TRACEFS); 151FS(hugetlbfs, hugetlbfs, HUGETLBFS); 152FS(bpf_fs, bpf, BPF_FS); 153 154static 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 177static 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 189static 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 207static 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 */ 221static 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 242static 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 253static const char *fs__mountpoint(const struct fs *fs) 254{ 255 return fs->path; 256} 257 258static 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 270static 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 } 288out: 289 pthread_mutex_unlock(&fs->mount_mutex); 290 return mountpoint; 291} 292 293int 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 310static 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 */ 333int 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 */ 343int filename__read_ull(const char *filename, unsigned long long *value) 344{ 345 return filename__read_ull_base(filename, value, 0); 346} 347 348int 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 369int 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 385int 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 398static 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 412int sysfs__read_xll(const char *entry, unsigned long long *value) 413{ 414 return sysfs__read_ull_base(entry, value, 16); 415} 416 417int sysfs__read_ull(const char *entry, unsigned long long *value) 418{ 419 return sysfs__read_ull_base(entry, value, 0); 420} 421 422int 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 435int 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 448int 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 '0': 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} 483int 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 496int 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