1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2017 Cyril Hrubis <chrubis@suse.cz>
4 * Copyright (c) Linux Test Project, 2018-2023
5 */
6
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <sys/mount.h>
11 #include <sys/wait.h>
12 #include <sys/quota.h>
13
14 #define TST_NO_DEFAULT_MAIN
15 #include "tst_test.h"
16 #include "tst_fs.h"
17
18 /*
19 * NOTE: new filesystem should be also added to
20 * lib/newlib_tests/shell/tst_{all_filesystems_skip,skip_filesystems}.sh
21 */
22 static const char *const fs_type_whitelist[] = {
23 "ext2",
24 "ext3",
25 "ext4",
26 "xfs",
27 "btrfs",
28 "bcachefs",
29 "vfat",
30 "exfat",
31 "ntfs",
32 "tmpfs",
33 NULL
34 };
35
36 static const char *const fs_type_fuse_blacklist[] = {
37 "bcachefs",
38 NULL,
39 };
40
41 static const char *fs_types[ARRAY_SIZE(fs_type_whitelist)];
42
has_mkfs(const char * fs_type)43 static int has_mkfs(const char *fs_type)
44 {
45 char buf[128];
46 int ret;
47
48 if (strstr(fs_type, "tmpfs")) {
49 tst_res(TINFO, "mkfs is not needed for tmpfs");
50 return 1;
51 }
52
53 sprintf(buf, "mkfs.%s >/dev/null 2>&1", fs_type);
54
55 ret = tst_system(buf);
56
57 if (WEXITSTATUS(ret) == 127) {
58 tst_res(TINFO, "mkfs.%s does not exist", fs_type);
59 return 0;
60 }
61
62 tst_res(TINFO, "mkfs.%s does exist", fs_type);
63 return 1;
64 }
65
tst_fs_in_skiplist(const char * fs_type,const char * const * skiplist)66 int tst_fs_in_skiplist(const char *fs_type, const char *const *skiplist)
67 {
68 unsigned int i;
69
70 if (!skiplist)
71 return 0;
72
73 for (i = 0; skiplist[i]; i++) {
74 if (!strcmp(fs_type, skiplist[i]))
75 return 1;
76 }
77
78 return 0;
79 }
80
has_kernel_support(const char * fs_type)81 static enum tst_fs_impl has_kernel_support(const char *fs_type)
82 {
83 static int fuse_supported = -1;
84 const char *tmpdir = tst_get_tmpdir_root();
85 char buf[128];
86 char template[PATH_MAX];
87 int ret;
88
89 snprintf(template, sizeof(template), "%s/mountXXXXXX", tmpdir);
90 if (!mkdtemp(template))
91 tst_brk(TBROK | TERRNO, "mkdtemp(%s) failed", template);
92
93 ret = mount("/dev/zero", template, fs_type, 0, NULL);
94 if ((ret && errno != ENODEV) || !ret) {
95 if (!ret)
96 tst_umount(template);
97 tst_res(TINFO, "Kernel supports %s", fs_type);
98 SAFE_RMDIR(template);
99 return TST_FS_KERNEL;
100 }
101
102 SAFE_RMDIR(template);
103
104 if (tst_fs_in_skiplist(fs_type, fs_type_fuse_blacklist)) {
105 tst_res(TINFO, "Skipping %s because of FUSE blacklist", fs_type);
106 return TST_FS_UNSUPPORTED;
107 }
108
109 /* Is FUSE supported by kernel? */
110 if (fuse_supported == -1) {
111 ret = open("/dev/fuse", O_RDWR);
112 if (ret < 0) {
113 fuse_supported = 0;
114 } else {
115 fuse_supported = 1;
116 SAFE_CLOSE(ret);
117 }
118 }
119
120 if (!fuse_supported)
121 return TST_FS_UNSUPPORTED;
122
123 /* Is FUSE implementation installed? */
124 sprintf(buf, "mount.%s >/dev/null 2>&1", fs_type);
125
126 ret = tst_system(buf);
127 if (WEXITSTATUS(ret) == 127) {
128 tst_res(TINFO, "Filesystem %s is not supported", fs_type);
129 return TST_FS_UNSUPPORTED;
130 }
131
132 tst_res(TINFO, "FUSE does support %s", fs_type);
133 return TST_FS_FUSE;
134 }
135
tst_fs_is_supported(const char * fs_type)136 enum tst_fs_impl tst_fs_is_supported(const char *fs_type)
137 {
138 enum tst_fs_impl ret;
139
140 ret = has_kernel_support(fs_type);
141 if (!ret)
142 return TST_FS_UNSUPPORTED;
143
144 if (has_mkfs(fs_type))
145 return ret;
146
147 return TST_FS_UNSUPPORTED;
148 }
149
tst_get_supported_fs_types(const char * const * skiplist)150 const char **tst_get_supported_fs_types(const char *const *skiplist)
151 {
152 unsigned int i, j = 0;
153 int skip_fuse;
154 enum tst_fs_impl sup;
155 const char *only_fs;
156
157 skip_fuse = tst_fs_in_skiplist("fuse", skiplist);
158 only_fs = getenv("LTP_SINGLE_FS_TYPE");
159
160 if (only_fs) {
161 tst_res(TINFO, "WARNING: testing only %s", only_fs);
162 if (tst_fs_is_supported(only_fs))
163 fs_types[0] = only_fs;
164 return fs_types;
165 }
166
167 for (i = 0; fs_type_whitelist[i]; i++) {
168 if (tst_fs_in_skiplist(fs_type_whitelist[i], skiplist)) {
169 tst_res(TINFO, "Skipping %s as requested by the test",
170 fs_type_whitelist[i]);
171 continue;
172 }
173
174 sup = tst_fs_is_supported(fs_type_whitelist[i]);
175
176 if (skip_fuse && sup == TST_FS_FUSE) {
177 tst_res(TINFO,
178 "Skipping FUSE based %s as requested by the test",
179 fs_type_whitelist[i]);
180 continue;
181 }
182
183 if (sup)
184 fs_types[j++] = fs_type_whitelist[i];
185 }
186
187 return fs_types;
188 }
189