xref: /kernel/linux/linux-6.6/tools/lib/api/fs/fs.c (revision 62306a36)
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
48static const char * const sysfs__known_mountpoints[] = {
49	"/sys",
50	0,
51};
52
53static 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
62static 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
73static const char * const tracefs__known_mountpoints[] = {
74	TRACEFS_DEFAULT_PATH,
75	"/sys/kernel/debug/tracing",
76	"/tracing",
77	"/trace",
78	0,
79};
80
81static const char * const hugetlbfs__known_mountpoints[] = {
82	0,
83};
84
85static const char * const bpf_fs__known_mountpoints[] = {
86	"/sys/fs/bpf",
87	0,
88};
89
90struct 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
102static void fs__init_once(struct fs *fs);
103static const char *fs__mountpoint(const struct fs *fs);
104static const char *fs__mount(struct fs *fs);
105
106#define FS(lower_name, fs_name, upper_name)		\
107static 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							\
114static void lower_name##_init_once(void)		\
115{							\
116	struct fs *fs = &fs__##lower_name;		\
117							\
118	fs__init_once(fs);				\
119}							\
120							\
121const 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							\
130const 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							\
141bool lower_name##__configured(void)			\
142{							\
143	return lower_name##__mountpoint() != NULL;	\
144}
145
146FS(sysfs, sysfs, SYSFS);
147FS(procfs, procfs, PROC_SUPER);
148FS(debugfs, debugfs, DEBUGFS);
149FS(tracefs, tracefs, TRACEFS);
150FS(hugetlbfs, hugetlbfs, HUGETLBFS);
151FS(bpf_fs, bpf, BPF_FS);
152
153static 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
176static 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
188static 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
206static 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 */
220static 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
241static 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
252static const char *fs__mountpoint(const struct fs *fs)
253{
254	return fs->path;
255}
256
257static 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
269static 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	}
287out:
288	pthread_mutex_unlock(&fs->mount_mutex);
289	return mountpoint;
290}
291
292int 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
309static 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 */
332int 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 */
342int 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
349int 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
397int 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
413int 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
426static 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
440int sysfs__read_xll(const char *entry, unsigned long long *value)
441{
442	return sysfs__read_ull_base(entry, value, 16);
443}
444
445int sysfs__read_ull(const char *entry, unsigned long long *value)
446{
447	return sysfs__read_ull_base(entry, value, 0);
448}
449
450int 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
463int 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
476int 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}
505int 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
518int 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}
531