blob: 582e04eac9132866368b806df11ca05bbbd5041c [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "analyze.h"
#include "analyze-filesystems.h"
#include "fileio.h"
#include "filesystems.h"
#include "set.h"
#include "strv.h"
#include "terminal-util.h"
static int load_available_kernel_filesystems(Set **ret) {
_cleanup_set_free_ Set *filesystems = NULL;
_cleanup_free_ char *t = NULL;
int r;
assert(ret);
/* Let's read the available filesystems */
r = read_virtual_file("/proc/filesystems", SIZE_MAX, &t, NULL);
if (r < 0)
return r;
for (int i = 0;;) {
_cleanup_free_ char *line = NULL;
const char *p;
r = string_extract_line(t, i++, &line);
if (r < 0)
return log_oom();
if (r == 0)
break;
if (!line)
line = t;
p = strchr(line, '\t');
if (!p)
continue;
p += strspn(p, WHITESPACE);
r = set_put_strdup(&filesystems, p);
if (r < 0)
return log_error_errno(r, "Failed to add filesystem to list: %m");
}
*ret = TAKE_PTR(filesystems);
return 0;
}
static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
NULSTR_FOREACH(filesystem, set->value) {
if (filesystem[0] == '@')
continue;
free(set_remove(s, filesystem));
}
}
static void dump_filesystem_set(const FilesystemSet *set) {
int r;
if (!set)
return;
printf("%s%s%s\n"
" # %s\n",
ansi_highlight(),
set->name,
ansi_normal(),
set->help);
NULSTR_FOREACH(filesystem, set->value) {
const statfs_f_type_t *magic;
if (filesystem[0] == '@') {
printf(" %s%s%s\n", ansi_underline(), filesystem, ansi_normal());
continue;
}
r = fs_type_from_string(filesystem, &magic);
assert_se(r >= 0);
printf(" %s", filesystem);
for (size_t i = 0; magic[i] != 0; i++) {
const char *primary;
if (i == 0)
printf(" %s(magic: ", ansi_grey());
else
printf(", ");
printf("0x%llx", (unsigned long long) magic[i]);
primary = fs_type_to_string(magic[i]);
if (primary && !streq(primary, filesystem))
printf("[%s]", primary);
if (magic[i+1] == 0)
printf(")%s", ansi_normal());
}
printf("\n");
}
}
int verb_filesystems(int argc, char *argv[], void *userdata) {
bool first = true;
#if ! HAVE_LIBBPF
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with libbpf support, sorry.");
#endif
pager_open(arg_pager_flags);
if (strv_isempty(strv_skip(argv, 1))) {
_cleanup_set_free_ Set *kernel = NULL, *known = NULL;
int k;
NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value)
if (set_put_strdup(&known, fs) < 0)
return log_oom();
k = load_available_kernel_filesystems(&kernel);
for (FilesystemGroups i = 0; i < _FILESYSTEM_SET_MAX; i++) {
const FilesystemSet *set = filesystem_sets + i;
if (!first)
puts("");
dump_filesystem_set(set);
filesystem_set_remove(kernel, set);
if (i != FILESYSTEM_SET_KNOWN)
filesystem_set_remove(known, set);
first = false;
}
if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
return 0;
if (!set_isempty(known)) {
_cleanup_free_ char **l = NULL;
printf("\n"
"# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n",
ansi_highlight(), ansi_normal());
l = set_get_strv(known);
if (!l)
return log_oom();
strv_sort(l);
STRV_FOREACH(filesystem, l) {
const statfs_f_type_t *magic;
bool is_primary = false;
assert_se(fs_type_from_string(*filesystem, &magic) >= 0);
for (size_t i = 0; magic[i] != 0; i++) {
const char *primary;
primary = fs_type_to_string(magic[i]);
assert(primary);
if (streq(primary, *filesystem))
is_primary = true;
}
if (!is_primary) {
log_debug("Skipping ungrouped file system '%s', because it's an alias for another one.", *filesystem);
continue;
}
printf("# %s\n", *filesystem);
}
}
if (k < 0) {
fputc('\n', stdout);
fflush(stdout);
log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m");
} else if (!set_isempty(kernel)) {
_cleanup_free_ char **l = NULL;
printf("\n"
"# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n",
ansi_highlight(), ansi_normal());
l = set_get_strv(kernel);
if (!l)
return log_oom();
strv_sort(l);
STRV_FOREACH(filesystem, l)
printf("# %s\n", *filesystem);
}
} else
STRV_FOREACH(name, strv_skip(argv, 1)) {
const FilesystemSet *set;
if (!first)
puts("");
set = filesystem_set_find(*name);
if (!set) {
/* make sure the error appears below normal output */
fflush(stdout);
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"Filesystem set \"%s\" not found.", *name);
}
dump_filesystem_set(set);
first = false;
}
return EXIT_SUCCESS;
}