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