xref: /openbmc/linux/tools/lib/api/fs/fs.c (revision 97d5f2e9)
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 
fs__read_mounts(struct fs * fs)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 
fs__valid_mount(const char * fs,long magic)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 
fs__check_mounts(struct fs * fs)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 
mem_toupper(char * f,size_t len)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  */
fs__env_override(struct fs * fs)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 
fs__init_once(struct fs * fs)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 
fs__mountpoint(const struct fs * fs)252 static const char *fs__mountpoint(const struct fs *fs)
253 {
254 	return fs->path;
255 }
256 
mount_overload(struct fs * fs)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 
fs__mount(struct fs * fs)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 
filename__read_int(const char * filename,int * value)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 
filename__read_ull_base(const char * filename,unsigned long long * value,int base)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  */
filename__read_xll(const char * filename,unsigned long long * value)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  */
filename__read_ull(const char * filename,unsigned long long * value)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 
filename__read_str(const char * filename,char ** buf,size_t * sizep)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 
filename__write_int(const char * filename,int value)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 
procfs__read_str(const char * entry,char ** buf,size_t * sizep)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 
sysfs__read_ull_base(const char * entry,unsigned long long * value,int base)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 
sysfs__read_xll(const char * entry,unsigned long long * value)440 int sysfs__read_xll(const char *entry, unsigned long long *value)
441 {
442 	return sysfs__read_ull_base(entry, value, 16);
443 }
444 
sysfs__read_ull(const char * entry,unsigned long long * value)445 int sysfs__read_ull(const char *entry, unsigned long long *value)
446 {
447 	return sysfs__read_ull_base(entry, value, 0);
448 }
449 
sysfs__read_int(const char * entry,int * value)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 
sysfs__read_str(const char * entry,char ** buf,size_t * sizep)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 
sysfs__read_bool(const char * entry,bool * value)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 }
sysctl__read_int(const char * sysctl,int * value)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 
sysfs__write_int(const char * entry,int value)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 }
531