Serenity Operating System
at master 233 lines 7.2 kB view raw
1/* 2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/JsonArray.h> 8#include <AK/JsonObject.h> 9#include <AK/JsonValue.h> 10#include <LibCore/ArgsParser.h> 11#include <LibCore/DirIterator.h> 12#include <LibCore/File.h> 13#include <LibCore/System.h> 14#include <LibMain/Main.h> 15#include <fcntl.h> 16#include <stdio.h> 17#include <string.h> 18#include <unistd.h> 19 20static int parse_options(StringView options) 21{ 22 int flags = 0; 23 Vector<StringView> parts = options.split_view(','); 24 for (auto& part : parts) { 25 if (part == "defaults") 26 continue; 27 else if (part == "nodev") 28 flags |= MS_NODEV; 29 else if (part == "noexec") 30 flags |= MS_NOEXEC; 31 else if (part == "nosuid") 32 flags |= MS_NOSUID; 33 else if (part == "bind") 34 flags |= MS_BIND; 35 else if (part == "ro") 36 flags |= MS_RDONLY; 37 else if (part == "remount") 38 flags |= MS_REMOUNT; 39 else if (part == "wxallowed") 40 flags |= MS_WXALLOWED; 41 else if (part == "axallowed") 42 flags |= MS_AXALLOWED; 43 else if (part == "noregular") 44 flags |= MS_NOREGULAR; 45 else 46 warnln("Ignoring invalid option: {}", part); 47 } 48 return flags; 49} 50 51static bool is_source_none(StringView source) 52{ 53 return source == "none"sv; 54} 55 56static ErrorOr<int> get_source_fd(StringView source) 57{ 58 if (is_source_none(source)) 59 return -1; 60 auto fd_or_error = Core::System::open(source, O_RDWR); 61 if (fd_or_error.is_error()) 62 fd_or_error = Core::System::open(source, O_RDONLY); 63 return fd_or_error; 64} 65 66static bool mount_by_line(DeprecatedString const& line) 67{ 68 // Skip comments and blank lines. 69 if (line.is_empty() || line.starts_with('#')) 70 return true; 71 72 Vector<DeprecatedString> parts = line.split('\t'); 73 if (parts.size() < 3) { 74 warnln("Invalid fstab entry: {}", line); 75 return false; 76 } 77 78 auto mountpoint = parts[1]; 79 auto fstype = parts[2]; 80 int flags = parts.size() >= 4 ? parse_options(parts[3]) : 0; 81 82 if (mountpoint == "/") { 83 dbgln("Skipping mounting root"); 84 return true; 85 } 86 87 auto filename = parts[0]; 88 89 auto fd_or_error = get_source_fd(filename); 90 if (fd_or_error.is_error()) { 91 outln("{}", fd_or_error.release_error()); 92 return false; 93 } 94 auto const fd = fd_or_error.release_value(); 95 96 dbgln("Mounting {} ({}) on {}", filename, fstype, mountpoint); 97 98 auto error_or_void = Core::System::mount(fd, mountpoint, fstype, flags); 99 if (error_or_void.is_error()) { 100 warnln("Failed to mount {} (FD: {}) ({}) on {}: {}", filename, fd, fstype, mountpoint, error_or_void.error()); 101 return false; 102 } 103 104 return true; 105} 106 107static ErrorOr<void> mount_all() 108{ 109 // Mount all filesystems listed in /etc/fstab. 110 dbgln("Mounting all filesystems..."); 111 Array<u8, PAGE_SIZE> buffer; 112 113 bool all_ok = true; 114 auto process_fstab_entries = [&](StringView path) -> ErrorOr<void> { 115 auto file_unbuffered = TRY(Core::File::open(path, Core::File::OpenMode::Read)); 116 auto file = TRY(Core::BufferedFile::create(move(file_unbuffered))); 117 118 while (TRY(file->can_read_line())) { 119 auto line = TRY(file->read_line(buffer)); 120 121 if (!mount_by_line(line)) 122 all_ok = false; 123 } 124 return {}; 125 }; 126 127 if (auto result = process_fstab_entries("/etc/fstab"sv); result.is_error()) 128 dbgln("Failed to read '/etc/fstab': {}", result.error()); 129 130 auto fstab_directory_iterator = Core::DirIterator("/etc/fstab.d", Core::DirIterator::SkipDots); 131 132 if (fstab_directory_iterator.has_error() && fstab_directory_iterator.error().code() != ENOENT) { 133 dbgln("Failed to open /etc/fstab.d: {}", fstab_directory_iterator.error()); 134 } else if (!fstab_directory_iterator.has_error()) { 135 while (fstab_directory_iterator.has_next()) { 136 auto path = fstab_directory_iterator.next_full_path(); 137 if (auto result = process_fstab_entries(path); result.is_error()) 138 dbgln("Failed to read '{}': {}", path, result.error()); 139 } 140 } 141 142 if (all_ok) 143 return {}; 144 145 return Error::from_string_literal("One or more errors occurred. Please verify earlier output."); 146} 147 148static ErrorOr<void> print_mounts() 149{ 150 // Output info about currently mounted filesystems. 151 auto df = TRY(Core::File::open("/sys/kernel/df"sv, Core::File::OpenMode::Read)); 152 153 auto content = TRY(df->read_until_eof()); 154 auto json = TRY(JsonValue::from_string(content)); 155 156 json.as_array().for_each([](auto& value) { 157 auto& fs_object = value.as_object(); 158 auto class_name = fs_object.get_deprecated_string("class_name"sv).value_or({}); 159 auto mount_point = fs_object.get_deprecated_string("mount_point"sv).value_or({}); 160 auto source = fs_object.get_deprecated_string("source"sv).value_or("none"); 161 auto readonly = fs_object.get_bool("readonly"sv).value_or(false); 162 auto mount_flags = fs_object.get_u32("mount_flags"sv).value_or(0); 163 164 out("{} on {} type {} (", source, mount_point, class_name); 165 166 if (readonly || mount_flags & MS_RDONLY) 167 out("ro"); 168 else 169 out("rw"); 170 171 if (mount_flags & MS_NODEV) 172 out(",nodev"); 173 if (mount_flags & MS_NOREGULAR) 174 out(",noregular"); 175 if (mount_flags & MS_NOEXEC) 176 out(",noexec"); 177 if (mount_flags & MS_NOSUID) 178 out(",nosuid"); 179 if (mount_flags & MS_BIND) 180 out(",bind"); 181 if (mount_flags & MS_WXALLOWED) 182 out(",wxallowed"); 183 if (mount_flags & MS_AXALLOWED) 184 out(",axallowed"); 185 186 outln(")"); 187 }); 188 189 return {}; 190} 191 192ErrorOr<int> serenity_main(Main::Arguments arguments) 193{ 194 StringView source; 195 StringView mountpoint; 196 StringView fs_type; 197 StringView options; 198 bool should_mount_all = false; 199 200 Core::ArgsParser args_parser; 201 args_parser.add_positional_argument(source, "Source path", "source", Core::ArgsParser::Required::No); 202 args_parser.add_positional_argument(mountpoint, "Mount point", "mountpoint", Core::ArgsParser::Required::No); 203 args_parser.add_option(fs_type, "File system type", nullptr, 't', "fstype"); 204 args_parser.add_option(options, "Mount options", nullptr, 'o', "options"); 205 args_parser.add_option(should_mount_all, "Mount all file systems listed in /etc/fstab and /etc/fstab.d/*", nullptr, 'a'); 206 args_parser.parse(arguments); 207 208 if (should_mount_all) { 209 TRY(mount_all()); 210 return 0; 211 } 212 213 if (source.is_empty() && mountpoint.is_empty()) { 214 TRY(print_mounts()); 215 return 0; 216 } 217 218 if (!source.is_empty() && !mountpoint.is_empty()) { 219 if (fs_type.is_empty()) 220 fs_type = "ext2"sv; 221 int flags = !options.is_empty() ? parse_options(options) : 0; 222 223 int const fd = TRY(get_source_fd(source)); 224 225 TRY(Core::System::mount(fd, mountpoint, fs_type, flags)); 226 227 return 0; 228 } 229 230 args_parser.print_usage(stderr, arguments.strings[0]); 231 232 return 1; 233}