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/sysfs-rules.txt
 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
 318/*
 319 * Parses @value out of @filename with strtoull.
 320 * By using 0 for base, the strtoull detects the
 321 * base automatically (see man strtoull).
 322 */
 323int filename__read_ull(const char *filename, unsigned long long *value)
 324{
 325        char line[64];
 326        int fd = open(filename, O_RDONLY), err = -1;
 327
 328        if (fd < 0)
 329                return -1;
 330
 331        if (read(fd, line, sizeof(line)) > 0) {
 332                *value = strtoull(line, NULL, 0);
 333                if (*value != ULLONG_MAX)
 334                        err = 0;
 335        }
 336
 337        close(fd);
 338        return err;
 339}
 340
 341#define STRERR_BUFSIZE  128     /* For the buffer size of strerror_r */
 342
 343int filename__read_str(const char *filename, char **buf, size_t *sizep)
 344{
 345        size_t size = 0, alloc_size = 0;
 346        void *bf = NULL, *nbf;
 347        int fd, n, err = 0;
 348        char sbuf[STRERR_BUFSIZE];
 349
 350        fd = open(filename, O_RDONLY);
 351        if (fd < 0)
 352                return -errno;
 353
 354        do {
 355                if (size == alloc_size) {
 356                        alloc_size += BUFSIZ;
 357                        nbf = realloc(bf, alloc_size);
 358                        if (!nbf) {
 359                                err = -ENOMEM;
 360                                break;
 361                        }
 362
 363                        bf = nbf;
 364                }
 365
 366                n = read(fd, bf + size, alloc_size - size);
 367                if (n < 0) {
 368                        if (size) {
 369                                pr_warning("read failed %d: %s\n", errno,
 370                                         strerror_r(errno, sbuf, sizeof(sbuf)));
 371                                err = 0;
 372                        } else
 373                                err = -errno;
 374
 375                        break;
 376                }
 377
 378                size += n;
 379        } while (n > 0);
 380
 381        if (!err) {
 382                *sizep = size;
 383                *buf   = bf;
 384        } else
 385                free(bf);
 386
 387        close(fd);
 388        return err;
 389}
 390
 391int filename__write_int(const char *filename, int value)
 392{
 393        int fd = open(filename, O_WRONLY), err = -1;
 394        char buf[64];
 395
 396        if (fd < 0)
 397                return err;
 398
 399        sprintf(buf, "%d", value);
 400        if (write(fd, buf, sizeof(buf)) == sizeof(buf))
 401                err = 0;
 402
 403        close(fd);
 404        return err;
 405}
 406
 407int procfs__read_str(const char *entry, char **buf, size_t *sizep)
 408{
 409        char path[PATH_MAX];
 410        const char *procfs = procfs__mountpoint();
 411
 412        if (!procfs)
 413                return -1;
 414
 415        snprintf(path, sizeof(path), "%s/%s", procfs, entry);
 416
 417        return filename__read_str(path, buf, sizep);
 418}
 419
 420int sysfs__read_ull(const char *entry, unsigned long long *value)
 421{
 422        char path[PATH_MAX];
 423        const char *sysfs = sysfs__mountpoint();
 424
 425        if (!sysfs)
 426                return -1;
 427
 428        snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
 429
 430        return filename__read_ull(path, value);
 431}
 432
 433int sysfs__read_int(const char *entry, int *value)
 434{
 435        char path[PATH_MAX];
 436        const char *sysfs = sysfs__mountpoint();
 437
 438        if (!sysfs)
 439                return -1;
 440
 441        snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
 442
 443        return filename__read_int(path, value);
 444}
 445
 446int sysfs__read_str(const char *entry, char **buf, size_t *sizep)
 447{
 448        char path[PATH_MAX];
 449        const char *sysfs = sysfs__mountpoint();
 450
 451        if (!sysfs)
 452                return -1;
 453
 454        snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
 455
 456        return filename__read_str(path, buf, sizep);
 457}
 458
 459int sysfs__read_bool(const char *entry, bool *value)
 460{
 461        char *buf;
 462        size_t size;
 463        int ret;
 464
 465        ret = sysfs__read_str(entry, &buf, &size);
 466        if (ret < 0)
 467                return ret;
 468
 469        switch (buf[0]) {
 470        case '1':
 471        case 'y':
 472        case 'Y':
 473                *value = true;
 474                break;
 475        case '0':
 476        case 'n':
 477        case 'N':
 478                *value = false;
 479                break;
 480        default:
 481                ret = -1;
 482        }
 483
 484        free(buf);
 485
 486        return ret;
 487}
 488int sysctl__read_int(const char *sysctl, int *value)
 489{
 490        char path[PATH_MAX];
 491        const char *procfs = procfs__mountpoint();
 492
 493        if (!procfs)
 494                return -1;
 495
 496        snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl);
 497
 498        return filename__read_int(path, value);
 499}
 500
 501int sysfs__write_int(const char *entry, int value)
 502{
 503        char path[PATH_MAX];
 504        const char *sysfs = sysfs__mountpoint();
 505
 506        if (!sysfs)
 507                return -1;
 508
 509        if (snprintf(path, sizeof(path), "%s/%s", sysfs, entry) >= PATH_MAX)
 510                return -1;
 511
 512        return filename__write_int(path, value);
 513}
 514