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