509 lines · c
1// SPDX-License-Identifier: GPL-2.02#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) #x23#define STR(x) _STR(x)24 25#ifndef SYSFS_MAGIC26#define SYSFS_MAGIC 0x6265657227#endif28 29#ifndef PROC_SUPER_MAGIC30#define PROC_SUPER_MAGIC 0x9fa031#endif32 33#ifndef DEBUGFS_MAGIC34#define DEBUGFS_MAGIC 0x6462672035#endif36 37#ifndef TRACEFS_MAGIC38#define TRACEFS_MAGIC 0x7472616339#endif40 41#ifndef HUGETLBFS_MAGIC42#define HUGETLBFS_MAGIC 0x958458f643#endif44 45#ifndef BPF_FS_MAGIC46#define BPF_FS_MAGIC 0xcafe4a1147#endif48 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_PATH60#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"61#endif62 63static const char * const debugfs__known_mountpoints[] = {64 DEBUGFS_DEFAULT_PATH,65 "/debug",66 0,67};68 69 70#ifndef TRACEFS_DEFAULT_PATH71#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"72#endif73 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_MAGIC100#define TRACEFS_MAGIC 0x74726163101#endif102 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 (for218 * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst219 * 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 the341 * 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 } else364 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