]> Git Repo - linux.git/blob - tools/lib/api/fs/fs.c
x86/alternative: Make custom return thunk unconditional
[linux.git] / tools / lib / api / fs / fs.c
1 // SPDX-License-Identifier: GPL-2.0
2 #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 "debug-internal.h"
20
21 #define _STR(x) #x
22 #define STR(x) _STR(x)
23
24 #ifndef SYSFS_MAGIC
25 #define SYSFS_MAGIC            0x62656572
26 #endif
27
28 #ifndef PROC_SUPER_MAGIC
29 #define PROC_SUPER_MAGIC       0x9fa0
30 #endif
31
32 #ifndef DEBUGFS_MAGIC
33 #define DEBUGFS_MAGIC          0x64626720
34 #endif
35
36 #ifndef TRACEFS_MAGIC
37 #define TRACEFS_MAGIC          0x74726163
38 #endif
39
40 #ifndef HUGETLBFS_MAGIC
41 #define HUGETLBFS_MAGIC        0x958458f6
42 #endif
43
44 #ifndef BPF_FS_MAGIC
45 #define BPF_FS_MAGIC           0xcafe4a11
46 #endif
47
48 static const char * const sysfs__known_mountpoints[] = {
49         "/sys",
50         0,
51 };
52
53 static const char * const procfs__known_mountpoints[] = {
54         "/proc",
55         0,
56 };
57
58 #ifndef DEBUGFS_DEFAULT_PATH
59 #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
60 #endif
61
62 static const char * const debugfs__known_mountpoints[] = {
63         DEBUGFS_DEFAULT_PATH,
64         "/debug",
65         0,
66 };
67
68
69 #ifndef TRACEFS_DEFAULT_PATH
70 #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
71 #endif
72
73 static const char * const tracefs__known_mountpoints[] = {
74         TRACEFS_DEFAULT_PATH,
75         "/sys/kernel/debug/tracing",
76         "/tracing",
77         "/trace",
78         0,
79 };
80
81 static const char * const hugetlbfs__known_mountpoints[] = {
82         0,
83 };
84
85 static const char * const bpf_fs__known_mountpoints[] = {
86         "/sys/fs/bpf",
87         0,
88 };
89
90 struct fs {
91         const char *             const name;
92         const char * const *     const mounts;
93         char                    *path;
94         pthread_mutex_t          mount_mutex;
95         const long               magic;
96 };
97
98 #ifndef TRACEFS_MAGIC
99 #define TRACEFS_MAGIC 0x74726163
100 #endif
101
102 static void fs__init_once(struct fs *fs);
103 static const char *fs__mountpoint(const struct fs *fs);
104 static const char *fs__mount(struct fs *fs);
105
106 #define FS(lower_name, fs_name, upper_name)             \
107 static struct fs fs__##lower_name = {                   \
108         .name = #fs_name,                               \
109         .mounts = lower_name##__known_mountpoints,      \
110         .magic = upper_name##_MAGIC,                    \
111         .mount_mutex = PTHREAD_MUTEX_INITIALIZER,       \
112 };                                                      \
113                                                         \
114 static void lower_name##_init_once(void)                \
115 {                                                       \
116         struct fs *fs = &fs__##lower_name;              \
117                                                         \
118         fs__init_once(fs);                              \
119 }                                                       \
120                                                         \
121 const char *lower_name##__mountpoint(void)              \
122 {                                                       \
123         static pthread_once_t init_once = PTHREAD_ONCE_INIT;    \
124         struct fs *fs = &fs__##lower_name;              \
125                                                         \
126         pthread_once(&init_once, lower_name##_init_once);       \
127         return fs__mountpoint(fs);                      \
128 }                                                       \
129                                                         \
130 const char *lower_name##__mount(void)                   \
131 {                                                       \
132         const char *mountpoint = lower_name##__mountpoint();    \
133         struct fs *fs = &fs__##lower_name;              \
134                                                         \
135         if (mountpoint)                                 \
136                 return mountpoint;                      \
137                                                         \
138         return fs__mount(fs);                           \
139 }                                                       \
140                                                         \
141 bool lower_name##__configured(void)                     \
142 {                                                       \
143         return lower_name##__mountpoint() != NULL;      \
144 }
145
146 FS(sysfs, sysfs, SYSFS);
147 FS(procfs, procfs, PROC_SUPER);
148 FS(debugfs, debugfs, DEBUGFS);
149 FS(tracefs, tracefs, TRACEFS);
150 FS(hugetlbfs, hugetlbfs, HUGETLBFS);
151 FS(bpf_fs, bpf, BPF_FS);
152
153 static bool fs__read_mounts(struct fs *fs)
154 {
155         char type[100];
156         FILE *fp;
157         char path[PATH_MAX + 1];
158
159         fp = fopen("/proc/mounts", "r");
160         if (fp == NULL)
161                 return false;
162
163         while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
164                       path, type) == 2) {
165
166                 if (strcmp(type, fs->name) == 0) {
167                         fs->path = strdup(path);
168                         fclose(fp);
169                         return fs->path != NULL;
170                 }
171         }
172         fclose(fp);
173         return false;
174 }
175
176 static int fs__valid_mount(const char *fs, long magic)
177 {
178         struct statfs st_fs;
179
180         if (statfs(fs, &st_fs) < 0)
181                 return -ENOENT;
182         else if ((long)st_fs.f_type != magic)
183                 return -ENOENT;
184
185         return 0;
186 }
187
188 static bool fs__check_mounts(struct fs *fs)
189 {
190         const char * const *ptr;
191
192         ptr = fs->mounts;
193         while (*ptr) {
194                 if (fs__valid_mount(*ptr, fs->magic) == 0) {
195                         fs->path = strdup(*ptr);
196                         if (!fs->path)
197                                 return false;
198                         return true;
199                 }
200                 ptr++;
201         }
202
203         return false;
204 }
205
206 static void mem_toupper(char *f, size_t len)
207 {
208         while (len) {
209                 *f = toupper(*f);
210                 f++;
211                 len--;
212         }
213 }
214
215 /*
216  * Check for "NAME_PATH" environment variable to override fs location (for
217  * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst
218  * for SYSFS_PATH.
219  */
220 static bool fs__env_override(struct fs *fs)
221 {
222         char *override_path;
223         size_t name_len = strlen(fs->name);
224         /* name + "_PATH" + '\0' */
225         char upper_name[name_len + 5 + 1];
226
227         memcpy(upper_name, fs->name, name_len);
228         mem_toupper(upper_name, name_len);
229         strcpy(&upper_name[name_len], "_PATH");
230
231         override_path = getenv(upper_name);
232         if (!override_path)
233                 return false;
234
235         fs->path = strdup(override_path);
236         if (!fs->path)
237                 return false;
238         return true;
239 }
240
241 static void fs__init_once(struct fs *fs)
242 {
243         if (!fs__env_override(fs) &&
244             !fs__check_mounts(fs) &&
245             !fs__read_mounts(fs)) {
246                 assert(!fs->path);
247         } else {
248                 assert(fs->path);
249         }
250 }
251
252 static const char *fs__mountpoint(const struct fs *fs)
253 {
254         return fs->path;
255 }
256
257 static const char *mount_overload(struct fs *fs)
258 {
259         size_t name_len = strlen(fs->name);
260         /* "PERF_" + name + "_ENVIRONMENT" + '\0' */
261         char upper_name[5 + name_len + 12 + 1];
262
263         snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name);
264         mem_toupper(upper_name, name_len);
265
266         return getenv(upper_name) ?: *fs->mounts;
267 }
268
269 static const char *fs__mount(struct fs *fs)
270 {
271         const char *mountpoint;
272
273         pthread_mutex_lock(&fs->mount_mutex);
274
275         /* Check if path found inside the mutex to avoid races with other callers of mount. */
276         mountpoint = fs__mountpoint(fs);
277         if (mountpoint)
278                 goto out;
279
280         mountpoint = mount_overload(fs);
281
282         if (mount(NULL, mountpoint, fs->name, 0, NULL) == 0 &&
283             fs__valid_mount(mountpoint, fs->magic) == 0) {
284                 fs->path = strdup(mountpoint);
285                 mountpoint = fs->path;
286         }
287 out:
288         pthread_mutex_unlock(&fs->mount_mutex);
289         return mountpoint;
290 }
291
292 int filename__read_int(const char *filename, int *value)
293 {
294         char line[64];
295         int fd = open(filename, O_RDONLY), err = -1;
296
297         if (fd < 0)
298                 return -1;
299
300         if (read(fd, line, sizeof(line)) > 0) {
301                 *value = atoi(line);
302                 err = 0;
303         }
304
305         close(fd);
306         return err;
307 }
308
309 static int filename__read_ull_base(const char *filename,
310                                    unsigned long long *value, int base)
311 {
312         char line[64];
313         int fd = open(filename, O_RDONLY), err = -1;
314
315         if (fd < 0)
316                 return -1;
317
318         if (read(fd, line, sizeof(line)) > 0) {
319                 *value = strtoull(line, NULL, base);
320                 if (*value != ULLONG_MAX)
321                         err = 0;
322         }
323
324         close(fd);
325         return err;
326 }
327
328 /*
329  * Parses @value out of @filename with strtoull.
330  * By using 16 for base to treat the number as hex.
331  */
332 int filename__read_xll(const char *filename, unsigned long long *value)
333 {
334         return filename__read_ull_base(filename, value, 16);
335 }
336
337 /*
338  * Parses @value out of @filename with strtoull.
339  * By using 0 for base, the strtoull detects the
340  * base automatically (see man strtoull).
341  */
342 int filename__read_ull(const char *filename, unsigned long long *value)
343 {
344         return filename__read_ull_base(filename, value, 0);
345 }
346
347 #define STRERR_BUFSIZE  128     /* For the buffer size of strerror_r */
348
349 int filename__read_str(const char *filename, char **buf, size_t *sizep)
350 {
351         size_t size = 0, alloc_size = 0;
352         void *bf = NULL, *nbf;
353         int fd, n, err = 0;
354         char sbuf[STRERR_BUFSIZE];
355
356         fd = open(filename, O_RDONLY);
357         if (fd < 0)
358                 return -errno;
359
360         do {
361                 if (size == alloc_size) {
362                         alloc_size += BUFSIZ;
363                         nbf = realloc(bf, alloc_size);
364                         if (!nbf) {
365                                 err = -ENOMEM;
366                                 break;
367                         }
368
369                         bf = nbf;
370                 }
371
372                 n = read(fd, bf + size, alloc_size - size);
373                 if (n < 0) {
374                         if (size) {
375                                 pr_warn("read failed %d: %s\n", errno,
376                                         strerror_r(errno, sbuf, sizeof(sbuf)));
377                                 err = 0;
378                         } else
379                                 err = -errno;
380
381                         break;
382                 }
383
384                 size += n;
385         } while (n > 0);
386
387         if (!err) {
388                 *sizep = size;
389                 *buf   = bf;
390         } else
391                 free(bf);
392
393         close(fd);
394         return err;
395 }
396
397 int filename__write_int(const char *filename, int value)
398 {
399         int fd = open(filename, O_WRONLY), err = -1;
400         char buf[64];
401
402         if (fd < 0)
403                 return err;
404
405         sprintf(buf, "%d", value);
406         if (write(fd, buf, sizeof(buf)) == sizeof(buf))
407                 err = 0;
408
409         close(fd);
410         return err;
411 }
412
413 int procfs__read_str(const char *entry, char **buf, size_t *sizep)
414 {
415         char path[PATH_MAX];
416         const char *procfs = procfs__mountpoint();
417
418         if (!procfs)
419                 return -1;
420
421         snprintf(path, sizeof(path), "%s/%s", procfs, entry);
422
423         return filename__read_str(path, buf, sizep);
424 }
425
426 static int sysfs__read_ull_base(const char *entry,
427                                 unsigned long long *value, int base)
428 {
429         char path[PATH_MAX];
430         const char *sysfs = sysfs__mountpoint();
431
432         if (!sysfs)
433                 return -1;
434
435         snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
436
437         return filename__read_ull_base(path, value, base);
438 }
439
440 int sysfs__read_xll(const char *entry, unsigned long long *value)
441 {
442         return sysfs__read_ull_base(entry, value, 16);
443 }
444
445 int sysfs__read_ull(const char *entry, unsigned long long *value)
446 {
447         return sysfs__read_ull_base(entry, value, 0);
448 }
449
450 int sysfs__read_int(const char *entry, int *value)
451 {
452         char path[PATH_MAX];
453         const char *sysfs = sysfs__mountpoint();
454
455         if (!sysfs)
456                 return -1;
457
458         snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
459
460         return filename__read_int(path, value);
461 }
462
463 int sysfs__read_str(const char *entry, char **buf, size_t *sizep)
464 {
465         char path[PATH_MAX];
466         const char *sysfs = sysfs__mountpoint();
467
468         if (!sysfs)
469                 return -1;
470
471         snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
472
473         return filename__read_str(path, buf, sizep);
474 }
475
476 int sysfs__read_bool(const char *entry, bool *value)
477 {
478         char *buf;
479         size_t size;
480         int ret;
481
482         ret = sysfs__read_str(entry, &buf, &size);
483         if (ret < 0)
484                 return ret;
485
486         switch (buf[0]) {
487         case '1':
488         case 'y':
489         case 'Y':
490                 *value = true;
491                 break;
492         case '0':
493         case 'n':
494         case 'N':
495                 *value = false;
496                 break;
497         default:
498                 ret = -1;
499         }
500
501         free(buf);
502
503         return ret;
504 }
505 int sysctl__read_int(const char *sysctl, int *value)
506 {
507         char path[PATH_MAX];
508         const char *procfs = procfs__mountpoint();
509
510         if (!procfs)
511                 return -1;
512
513         snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl);
514
515         return filename__read_int(path, value);
516 }
517
518 int sysfs__write_int(const char *entry, int value)
519 {
520         char path[PATH_MAX];
521         const char *sysfs = sysfs__mountpoint();
522
523         if (!sysfs)
524                 return -1;
525
526         if (snprintf(path, sizeof(path), "%s/%s", sysfs, entry) >= PATH_MAX)
527                 return -1;
528
529         return filename__write_int(path, value);
530 }
This page took 0.060137 seconds and 4 git commands to generate.