1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "analyze.h"
4 #include "analyze-filesystems.h"
5 #include "fileio.h"
6 #include "filesystems.h"
7 #include "set.h"
8 #include "strv.h"
9 #include "terminal-util.h"
10
load_available_kernel_filesystems(Set ** ret)11 static int load_available_kernel_filesystems(Set **ret) {
12 _cleanup_set_free_ Set *filesystems = NULL;
13 _cleanup_free_ char *t = NULL;
14 int r;
15
16 assert(ret);
17
18 /* Let's read the available filesystems */
19
20 r = read_virtual_file("/proc/filesystems", SIZE_MAX, &t, NULL);
21 if (r < 0)
22 return r;
23
24 for (int i = 0;;) {
25 _cleanup_free_ char *line = NULL;
26 const char *p;
27
28 r = string_extract_line(t, i++, &line);
29 if (r < 0)
30 return log_oom();
31 if (r == 0)
32 break;
33
34 if (!line)
35 line = t;
36
37 p = strchr(line, '\t');
38 if (!p)
39 continue;
40
41 p += strspn(p, WHITESPACE);
42
43 r = set_put_strdup(&filesystems, p);
44 if (r < 0)
45 return log_error_errno(r, "Failed to add filesystem to list: %m");
46 }
47
48 *ret = TAKE_PTR(filesystems);
49 return 0;
50 }
51
filesystem_set_remove(Set * s,const FilesystemSet * set)52 static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
53 const char *filesystem;
54
55 NULSTR_FOREACH(filesystem, set->value) {
56 if (filesystem[0] == '@')
57 continue;
58
59 free(set_remove(s, filesystem));
60 }
61 }
62
dump_filesystem_set(const FilesystemSet * set)63 static void dump_filesystem_set(const FilesystemSet *set) {
64 const char *filesystem;
65 int r;
66
67 if (!set)
68 return;
69
70 printf("%s%s%s\n"
71 " # %s\n",
72 ansi_highlight(),
73 set->name,
74 ansi_normal(),
75 set->help);
76
77 NULSTR_FOREACH(filesystem, set->value) {
78 const statfs_f_type_t *magic;
79
80 if (filesystem[0] == '@') {
81 printf(" %s%s%s\n", ansi_underline(), filesystem, ansi_normal());
82 continue;
83 }
84
85 r = fs_type_from_string(filesystem, &magic);
86 assert_se(r >= 0);
87
88 printf(" %s", filesystem);
89
90 for (size_t i = 0; magic[i] != 0; i++) {
91 const char *primary;
92 if (i == 0)
93 printf(" %s(magic: ", ansi_grey());
94 else
95 printf(", ");
96
97 printf("0x%llx", (unsigned long long) magic[i]);
98
99 primary = fs_type_to_string(magic[i]);
100 if (primary && !streq(primary, filesystem))
101 printf("[%s]", primary);
102
103 if (magic[i+1] == 0)
104 printf(")%s", ansi_normal());
105 }
106
107 printf("\n");
108 }
109 }
110
verb_filesystems(int argc,char * argv[],void * userdata)111 int verb_filesystems(int argc, char *argv[], void *userdata) {
112 bool first = true;
113
114 #if ! HAVE_LIBBPF
115 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with libbpf support, sorry.");
116 #endif
117
118 pager_open(arg_pager_flags);
119
120 if (strv_isempty(strv_skip(argv, 1))) {
121 _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
122 const char *fs;
123 int k;
124
125 NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value)
126 if (set_put_strdup(&known, fs) < 0)
127 return log_oom();
128
129 k = load_available_kernel_filesystems(&kernel);
130
131 for (FilesystemGroups i = 0; i < _FILESYSTEM_SET_MAX; i++) {
132 const FilesystemSet *set = filesystem_sets + i;
133 if (!first)
134 puts("");
135
136 dump_filesystem_set(set);
137 filesystem_set_remove(kernel, set);
138 if (i != FILESYSTEM_SET_KNOWN)
139 filesystem_set_remove(known, set);
140 first = false;
141 }
142
143 if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
144 return 0;
145
146 if (!set_isempty(known)) {
147 _cleanup_free_ char **l = NULL;
148
149 printf("\n"
150 "# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n",
151 ansi_highlight(), ansi_normal());
152
153 l = set_get_strv(known);
154 if (!l)
155 return log_oom();
156
157 strv_sort(l);
158
159 STRV_FOREACH(filesystem, l) {
160 const statfs_f_type_t *magic;
161 bool is_primary = false;
162
163 assert_se(fs_type_from_string(*filesystem, &magic) >= 0);
164
165 for (size_t i = 0; magic[i] != 0; i++) {
166 const char *primary;
167
168 primary = fs_type_to_string(magic[i]);
169 assert(primary);
170
171 if (streq(primary, *filesystem))
172 is_primary = true;
173 }
174
175 if (!is_primary) {
176 log_debug("Skipping ungrouped file system '%s', because it's an alias for another one.", *filesystem);
177 continue;
178 }
179
180 printf("# %s\n", *filesystem);
181 }
182 }
183
184 if (k < 0) {
185 fputc('\n', stdout);
186 fflush(stdout);
187 log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m");
188 } else if (!set_isempty(kernel)) {
189 _cleanup_free_ char **l = NULL;
190
191 printf("\n"
192 "# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n",
193 ansi_highlight(), ansi_normal());
194
195 l = set_get_strv(kernel);
196 if (!l)
197 return log_oom();
198
199 strv_sort(l);
200
201 STRV_FOREACH(filesystem, l)
202 printf("# %s\n", *filesystem);
203 }
204 } else
205 STRV_FOREACH(name, strv_skip(argv, 1)) {
206 const FilesystemSet *set;
207
208 if (!first)
209 puts("");
210
211 set = filesystem_set_find(*name);
212 if (!set) {
213 /* make sure the error appears below normal output */
214 fflush(stdout);
215
216 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
217 "Filesystem set \"%s\" not found.", *name);
218 }
219
220 dump_filesystem_set(set);
221 first = false;
222 }
223
224 return 0;
225 }
226