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