GNU Linux-libre 6.8.7-gnu
[releases.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 "../io.h"
20 #include "debug-internal.h"
21
22 #define _STR(x) #x
23 #define STR(x) _STR(x)
24
25 #ifndef SYSFS_MAGIC
26 #define SYSFS_MAGIC            0x62656572
27 #endif
28
29 #ifndef PROC_SUPER_MAGIC
30 #define PROC_SUPER_MAGIC       0x9fa0
31 #endif
32
33 #ifndef DEBUGFS_MAGIC
34 #define DEBUGFS_MAGIC          0x64626720
35 #endif
36
37 #ifndef TRACEFS_MAGIC
38 #define TRACEFS_MAGIC          0x74726163
39 #endif
40
41 #ifndef HUGETLBFS_MAGIC
42 #define HUGETLBFS_MAGIC        0x958458f6
43 #endif
44
45 #ifndef BPF_FS_MAGIC
46 #define BPF_FS_MAGIC           0xcafe4a11
47 #endif
48
49 static const char * const sysfs__known_mountpoints[] = {
50         "/sys",
51         0,
52 };
53
54 static const char * const procfs__known_mountpoints[] = {
55         "/proc",
56         0,
57 };
58
59 #ifndef DEBUGFS_DEFAULT_PATH
60 #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
61 #endif
62
63 static const char * const debugfs__known_mountpoints[] = {
64         DEBUGFS_DEFAULT_PATH,
65         "/debug",
66         0,
67 };
68
69
70 #ifndef TRACEFS_DEFAULT_PATH
71 #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
72 #endif
73
74 static const char * const tracefs__known_mountpoints[] = {
75         TRACEFS_DEFAULT_PATH,
76         "/sys/kernel/debug/tracing",
77         "/tracing",
78         "/trace",
79         0,
80 };
81
82 static const char * const hugetlbfs__known_mountpoints[] = {
83         0,
84 };
85
86 static const char * const bpf_fs__known_mountpoints[] = {
87         "/sys/fs/bpf",
88         0,
89 };
90
91 struct 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_MAGIC
100 #define TRACEFS_MAGIC 0x74726163
101 #endif
102
103 static void fs__init_once(struct fs *fs);
104 static const char *fs__mountpoint(const struct fs *fs);
105 static const char *fs__mount(struct fs *fs);
106
107 #define FS(lower_name, fs_name, upper_name)             \
108 static 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                                                         \
115 static void lower_name##_init_once(void)                \
116 {                                                       \
117         struct fs *fs = &fs__##lower_name;              \
118                                                         \
119         fs__init_once(fs);                              \
120 }                                                       \
121                                                         \
122 const 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                                                         \
131 const 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                                                         \
142 bool lower_name##__configured(void)                     \
143 {                                                       \
144         return lower_name##__mountpoint() != NULL;      \
145 }
146
147 FS(sysfs, sysfs, SYSFS);
148 FS(procfs, procfs, PROC_SUPER);
149 FS(debugfs, debugfs, DEBUGFS);
150 FS(tracefs, tracefs, TRACEFS);
151 FS(hugetlbfs, hugetlbfs, HUGETLBFS);
152 FS(bpf_fs, bpf, BPF_FS);
153
154 static 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
177 static 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
189 static 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
207 static 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 (for
218  * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst
219  * for SYSFS_PATH.
220  */
221 static 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
242 static 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
253 static const char *fs__mountpoint(const struct fs *fs)
254 {
255         return fs->path;
256 }
257
258 static 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
270 static 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         }
288 out:
289         pthread_mutex_unlock(&fs->mount_mutex);
290         return mountpoint;
291 }
292
293 int 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
310 static 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  */
333 int 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 the
341  * base automatically (see man strtoull).
342  */
343 int filename__read_ull(const char *filename, unsigned long long *value)
344 {
345         return filename__read_ull_base(filename, value, 0);
346 }
347
348 int 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         } else
364                 err = 0;
365         close(io.fd);
366         return err;
367 }
368
369 int 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
385 int 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
398 static 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
412 int sysfs__read_xll(const char *entry, unsigned long long *value)
413 {
414         return sysfs__read_ull_base(entry, value, 16);
415 }
416
417 int sysfs__read_ull(const char *entry, unsigned long long *value)
418 {
419         return sysfs__read_ull_base(entry, value, 0);
420 }
421
422 int 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
435 int 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
448 int 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 }
483 int 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
496 int 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 }