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