/* * Copyright (c) 2019-2020, Sergey Bugaev * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include static int parse_options(StringView options) { int flags = 0; Vector parts = options.split_view(','); for (auto& part : parts) { if (part == "defaults") continue; else if (part == "nodev") flags |= MS_NODEV; else if (part == "noexec") flags |= MS_NOEXEC; else if (part == "nosuid") flags |= MS_NOSUID; else if (part == "bind") flags |= MS_BIND; else if (part == "ro") flags |= MS_RDONLY; else if (part == "remount") flags |= MS_REMOUNT; else if (part == "wxallowed") flags |= MS_WXALLOWED; else if (part == "axallowed") flags |= MS_AXALLOWED; else warnln("Ignoring invalid option: {}", part); } return flags; } static bool is_source_none(StringView source) { return source == "none"sv; } static int get_source_fd(StringView source) { if (is_source_none(source)) return -1; auto fd_or_error = Core::System::open(source, O_RDWR); if (fd_or_error.is_error()) fd_or_error = Core::System::open(source, O_RDONLY); if (fd_or_error.is_error()) { int saved_errno = errno; auto message = String::formatted("Failed to open: {}\n", source); errno = saved_errno; perror(message.characters()); } return fd_or_error.release_value(); } static bool mount_by_line(String const& line) { // Skip comments and blank lines. if (line.is_empty() || line.starts_with('#')) return true; Vector parts = line.split('\t'); if (parts.size() < 3) { warnln("Invalid fstab entry: {}", line); return false; } auto mountpoint = parts[1]; auto fstype = parts[2]; int flags = parts.size() >= 4 ? parse_options(parts[3]) : 0; if (mountpoint == "/") { dbgln("Skipping mounting root"); return true; } auto filename = parts[0]; int fd = get_source_fd(filename); dbgln("Mounting {} ({}) on {}", filename, fstype, mountpoint); auto error_or_void = Core::System::mount(fd, mountpoint, fstype, flags); if (error_or_void.is_error()) { warnln("Failed to mount {} (FD: {}) ({}) on {}: {}", filename, fd, fstype, mountpoint, strerror(errno)); return false; } return true; } static ErrorOr mount_all() { // Mount all filesystems listed in /etc/fstab. dbgln("Mounting all filesystems..."); auto fstab = TRY(Core::File::open("/etc/fstab", Core::OpenMode::ReadOnly)); bool all_ok = true; while (fstab->can_read_line()) { auto line = fstab->read_line(); if (!mount_by_line(line)) all_ok = false; } auto fstab_directory_iterator = Core::DirIterator("/etc/fstab.d", Core::DirIterator::SkipDots); if (fstab_directory_iterator.has_error() && fstab_directory_iterator.error() != ENOENT) { dbgln("Failed to open /etc/fstab.d: {}", fstab_directory_iterator.error_string()); } else if (!fstab_directory_iterator.has_error()) { while (fstab_directory_iterator.has_next()) { auto path = fstab_directory_iterator.next_full_path(); auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly); if (file_or_error.is_error()) { dbgln("Failed to open '{}': {}", path, file_or_error.error()); continue; } auto file = file_or_error.release_value(); while (file->can_read_line()) { auto line = file->read_line(); if (!mount_by_line(line)) all_ok = false; } } } if (all_ok) return {}; else return Error::from_string_literal("One or more errors occurred. Please verify earlier output."); } static ErrorOr print_mounts() { // Output info about currently mounted filesystems. auto df = TRY(Core::File::open("/proc/df", Core::OpenMode::ReadOnly)); auto content = df->read_all(); auto json = TRY(JsonValue::from_string(content)); json.as_array().for_each([](auto& value) { auto& fs_object = value.as_object(); auto class_name = fs_object.get("class_name"sv).to_string(); auto mount_point = fs_object.get("mount_point"sv).to_string(); auto source = fs_object.get("source"sv).as_string_or("none"); auto readonly = fs_object.get("readonly"sv).to_bool(); auto mount_flags = fs_object.get("mount_flags"sv).to_int(); out("{} on {} type {} (", source, mount_point, class_name); if (readonly || mount_flags & MS_RDONLY) out("ro"); else out("rw"); if (mount_flags & MS_NODEV) out(",nodev"); if (mount_flags & MS_NOEXEC) out(",noexec"); if (mount_flags & MS_NOSUID) out(",nosuid"); if (mount_flags & MS_BIND) out(",bind"); if (mount_flags & MS_WXALLOWED) out(",wxallowed"); if (mount_flags & MS_AXALLOWED) out(",axallowed"); outln(")"); }); return {}; } ErrorOr serenity_main(Main::Arguments arguments) { StringView source; StringView mountpoint; StringView fs_type; StringView options; bool should_mount_all = false; Core::ArgsParser args_parser; args_parser.add_positional_argument(source, "Source path", "source", Core::ArgsParser::Required::No); args_parser.add_positional_argument(mountpoint, "Mount point", "mountpoint", Core::ArgsParser::Required::No); args_parser.add_option(fs_type, "File system type", nullptr, 't', "fstype"); args_parser.add_option(options, "Mount options", nullptr, 'o', "options"); args_parser.add_option(should_mount_all, "Mount all file systems listed in /etc/fstab and /etc/fstab.d/*", nullptr, 'a'); args_parser.parse(arguments); if (should_mount_all) { TRY(mount_all()); return 0; } if (source.is_empty() && mountpoint.is_empty()) { TRY(print_mounts()); return 0; } if (!source.is_empty() && !mountpoint.is_empty()) { if (fs_type.is_empty()) fs_type = "ext2"sv; int flags = !options.is_empty() ? parse_options(options) : 0; int fd = get_source_fd(source); TRY(Core::System::mount(fd, mountpoint, fs_type, flags)); return 0; } args_parser.print_usage(stderr, arguments.argv[0]); return 1; }