| /* 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; |
| } |