xref: /linux/tools/lib/api/fs/fs.c (revision f8846a1a)
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 
fs__read_mounts(struct fs * fs)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 
fs__valid_mount(const char * fs,long magic)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 
fs__check_mounts(struct fs * fs)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 
mem_toupper(char * f,size_t len)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  */
fs__env_override(struct fs * fs)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 
fs__init_once(struct fs * fs)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 
fs__mountpoint(const struct fs * fs)253 static const char *fs__mountpoint(const struct fs *fs)
254 {
255 	return fs->path;
256 }
257 
mount_overload(struct fs * fs)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 
fs__mount(struct fs * fs)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 
filename__read_int(const char * filename,int * value)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 
filename__read_ull_base(const char * filename,unsigned long long * value,int base)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  */
filename__read_xll(const char * filename,unsigned long long * value)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  */
filename__read_ull(const char * filename,unsigned long long * value)343 int filename__read_ull(const char *filename, unsigned long long *value)
344 {
345 	return filename__read_ull_base(filename, value, 0);
346 }
347 
filename__read_str(const char * filename,char ** buf,size_t * sizep)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 
filename__write_int(const char * filename,int value)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 
procfs__read_str(const char * entry,char ** buf,size_t * sizep)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 
sysfs__read_ull_base(const char * entry,unsigned long long * value,int base)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 
sysfs__read_xll(const char * entry,unsigned long long * value)412 int sysfs__read_xll(const char *entry, unsigned long long *value)
413 {
414 	return sysfs__read_ull_base(entry, value, 16);
415 }
416 
sysfs__read_ull(const char * entry,unsigned long long * value)417 int sysfs__read_ull(const char *entry, unsigned long long *value)
418 {
419 	return sysfs__read_ull_base(entry, value, 0);
420 }
421 
sysfs__read_int(const char * entry,int * value)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 
sysfs__read_str(const char * entry,char ** buf,size_t * sizep)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 
sysfs__read_bool(const char * entry,bool * value)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 }
sysctl__read_int(const char * sysctl,int * value)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 
sysfs__write_int(const char * entry,int value)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 }
509