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        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}
 559