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