/* * Copyright (c) 2022-2023, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include namespace Kernel { ErrorOr> MountFile::create(FileSystemInitializer const& file_system_initializer, int flags) { // NOTE: We should not open a MountFile if someone wants to either remount or bindmount. // There's a check for this in the fsopen syscall entry handler, but here we just assert // to ensure this never happens. VERIFY(!(flags & MS_BIND)); VERIFY(!(flags & MS_REMOUNT)); auto mount_specific_data_buffer = TRY(KBuffer::try_create_with_size("Mount Specific Data"sv, PAGE_SIZE, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) MountFile(file_system_initializer, flags, move(mount_specific_data_buffer)))); } MountFile::MountFile(FileSystemInitializer const& file_system_initializer, int flags, NonnullOwnPtr mount_specific_data) : m_flags(flags) , m_file_system_initializer(file_system_initializer) { m_mount_specific_data.with_exclusive([&](auto& our_mount_specific_data) { our_mount_specific_data = move(mount_specific_data); memset(our_mount_specific_data->data(), 0, our_mount_specific_data->size()); }); } MountFile::~MountFile() = default; ErrorOr MountFile::ioctl(OpenFileDescription&, unsigned request, Userspace arg) { return m_mount_specific_data.with_exclusive([&](auto& our_mount_specific_data) -> ErrorOr { switch (request) { case MOUNT_IOCTL_SET_MOUNT_SPECIFIC_FLAG: { auto user_mount_specific_data = static_ptr_cast(arg); auto mount_specific_data = TRY(copy_typed_from_user(user_mount_specific_data)); if ((mount_specific_data.value_type == MountSpecificFlag::ValueType::SignedInteger || mount_specific_data.value_type == MountSpecificFlag::ValueType::UnsignedInteger) && mount_specific_data.value_length != 8) return EDOM; if (mount_specific_data.key_string_length > MOUNT_SPECIFIC_FLAG_KEY_STRING_MAX_LENGTH) return ENAMETOOLONG; if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_length == 0) return EINVAL; if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_addr == nullptr) return EFAULT; // NOTE: We put these limits in place because we assume that don't need to handle huge // amounts of bytes when trying to handle a mount fs-specific flag. // Anything larger than these constants (which could be changed if needed) is deemed to // potentially cause OOM condition, and cannot represent any reasonable and "honest" data // from userspace. if (mount_specific_data.value_type != MountSpecificFlag::ValueType::ASCIIString && mount_specific_data.value_length > MOUNT_SPECIFIC_FLAG_NON_ASCII_STRING_TYPE_MAX_LENGTH) return E2BIG; if (mount_specific_data.value_type == MountSpecificFlag::ValueType::ASCIIString && mount_specific_data.value_length > MOUNT_SPECIFIC_FLAG_ASCII_STRING_TYPE_MAX_LENGTH) return E2BIG; // NOTE: We enforce that the passed argument will be either i64 or u64, so it will always be // exactly 8 bytes. We do that to simplify handling of integers as well as to ensure ABI correctness // in all possible cases. auto key_string = TRY(try_copy_kstring_from_user(reinterpret_cast(mount_specific_data.key_string_addr), static_cast(mount_specific_data.key_string_length))); switch (mount_specific_data.value_type) { // NOTE: This is actually considered as simply boolean flag. case MountSpecificFlag::ValueType::Boolean: { VERIFY(m_file_system_initializer.handle_mount_boolean_flag); Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); auto value_integer = TRY(copy_typed_from_user(user_value_addr)); if (value_integer != 0 && value_integer != 1) return EDOM; bool value = (value_integer == 1) ? true : false; TRY(m_file_system_initializer.handle_mount_boolean_flag(our_mount_specific_data->bytes(), key_string->view(), value)); return {}; } case MountSpecificFlag::ValueType::UnsignedInteger: { VERIFY(m_file_system_initializer.handle_mount_unsigned_integer_flag); Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); auto value_integer = TRY(copy_typed_from_user(user_value_addr)); TRY(m_file_system_initializer.handle_mount_unsigned_integer_flag(our_mount_specific_data->bytes(), key_string->view(), value_integer)); return {}; } case MountSpecificFlag::ValueType::SignedInteger: { VERIFY(m_file_system_initializer.handle_mount_signed_integer_flag); Userspace user_value_addr(reinterpret_cast(mount_specific_data.value_addr)); auto value_integer = TRY(copy_typed_from_user(user_value_addr)); TRY(m_file_system_initializer.handle_mount_signed_integer_flag(our_mount_specific_data->bytes(), key_string->view(), value_integer)); return {}; } case MountSpecificFlag::ValueType::ASCIIString: { VERIFY(m_file_system_initializer.handle_mount_ascii_string_flag); auto value_string = TRY(try_copy_kstring_from_user(reinterpret_cast(mount_specific_data.value_addr), static_cast(mount_specific_data.value_length))); TRY(m_file_system_initializer.handle_mount_ascii_string_flag(our_mount_specific_data->bytes(), key_string->view(), value_string->view())); return {}; } default: return EINVAL; } } default: return EINVAL; } }); } ErrorOr> MountFile::pseudo_path(OpenFileDescription const&) const { return KString::try_create(":mount-file:"sv); } }