mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 11:58:12 +00:00
Kernel/Devices: Introduce the LoopDevice device
This device is a block device that allows a user to effectively treat an Inode as a block device. The static construction method is given an OpenFileDescription reference but validates that: - The description has a valid custody (so it's not some arbitrary file). Failing this requirement will yield EINVAL. - The description custody points to an Inode which is a regular file, as we only support (seekable) regular files. Failing this requirement will yield ENOTSUP. LoopDevice can be used to mount a regular file on the filesystem like other supported types of (physical) block devices.
This commit is contained in:
parent
a9d240c647
commit
5dcf03ad9a
12 changed files with 254 additions and 4 deletions
131
Kernel/Devices/Loop/LoopDevice.cpp
Normal file
131
Kernel/Devices/Loop/LoopDevice.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Singleton.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/Loop/LoopDevice.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <Kernel/Tasks/WorkQueue.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static Singleton<SpinlockProtected<LoopDevice::List, LockRank::None>> s_all_instances;
|
||||
static Atomic<u64> s_loop_device_id { 0 };
|
||||
|
||||
SpinlockProtected<LoopDevice::List, LockRank::None>& LoopDevice::all_instances()
|
||||
{
|
||||
return s_all_instances;
|
||||
}
|
||||
|
||||
void LoopDevice::remove(Badge<DeviceControlDevice>)
|
||||
{
|
||||
LoopDevice::all_instances().with([&](auto&) {
|
||||
m_list_node.remove();
|
||||
});
|
||||
}
|
||||
|
||||
bool LoopDevice::unref() const
|
||||
{
|
||||
bool did_hit_zero = LoopDevice::all_instances().with([&](auto&) {
|
||||
if (deref_base())
|
||||
return false;
|
||||
const_cast<LoopDevice&>(*this).revoke_weak_ptrs();
|
||||
return true;
|
||||
});
|
||||
if (did_hit_zero) {
|
||||
const_cast<LoopDevice&>(*this).will_be_destroyed();
|
||||
delete this;
|
||||
}
|
||||
return did_hit_zero;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<LoopDevice>> LoopDevice::create_with_file_description(OpenFileDescription& description)
|
||||
{
|
||||
auto custody = description.custody();
|
||||
if (!custody)
|
||||
return Error::from_errno(EINVAL);
|
||||
|
||||
// NOTE: We only support regular inode files, because anything else
|
||||
// just doesn't make sense (could be non-seekable files or char devices)
|
||||
if (!custody->inode().metadata().is_regular_file())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
|
||||
// NOTE: We could technically allow the user to create a loop device from a file
|
||||
// on SysFS, ProcFS, etc, but the added value from allowing this is non-existent
|
||||
// because there's simply no good reason to ever do this kind of operation.
|
||||
//
|
||||
// If you need more justification, some filesystems (like ProcFS, SysFS, etc) don't
|
||||
// support keeping Inode objects and instead keep re-creating them - this has serious
|
||||
// consequences on the integrity of loop devices, as we rely on the backing Inode to
|
||||
// be consistent while the LoopDevice is alive.
|
||||
if (!custody->inode().fs().supports_backing_loop_devices())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
|
||||
return TRY(LoopDevice::all_instances().with([custody](auto& all_instances_list) -> ErrorOr<NonnullRefPtr<LoopDevice>> {
|
||||
NonnullRefPtr<LoopDevice> device = *TRY(DeviceManagement::try_create_device<LoopDevice>(*custody, s_loop_device_id.fetch_add(1)));
|
||||
all_instances_list.append(*device);
|
||||
return device;
|
||||
}));
|
||||
}
|
||||
|
||||
void LoopDevice::start_request(AsyncBlockDeviceRequest& request)
|
||||
{
|
||||
auto work_item_creation_result = g_io_work->try_queue([this, &request]() {
|
||||
if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Read) {
|
||||
auto result = m_backing_custody->inode().read_bytes(request.block_index() * request.block_size(), request.buffer_size(), request.buffer(), nullptr);
|
||||
if (result.is_error())
|
||||
request.complete(AsyncDeviceRequest::Failure);
|
||||
else
|
||||
request.complete(AsyncDeviceRequest::Success);
|
||||
return;
|
||||
} else if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
|
||||
auto result = m_backing_custody->inode().write_bytes(request.block_index() * request.block_size(), request.buffer_size(), request.buffer(), nullptr);
|
||||
if (result.is_error())
|
||||
request.complete(AsyncDeviceRequest::Failure);
|
||||
else
|
||||
request.complete(AsyncDeviceRequest::Success);
|
||||
return;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
});
|
||||
if (work_item_creation_result.is_error())
|
||||
request.complete(AsyncDeviceRequest::OutOfMemory);
|
||||
}
|
||||
|
||||
bool LoopDevice::can_read(OpenFileDescription const&, u64) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoopDevice::can_write(OpenFileDescription const&, u64) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<size_t> LoopDevice::read(OpenFileDescription& description, u64 offset, UserOrKernelBuffer& buffer, size_t size)
|
||||
{
|
||||
return m_backing_custody->inode().read_bytes(offset, size, buffer, &description);
|
||||
}
|
||||
|
||||
ErrorOr<size_t> LoopDevice::write(OpenFileDescription& description, u64 offset, UserOrKernelBuffer const& buffer, size_t size)
|
||||
{
|
||||
return m_backing_custody->inode().write_bytes(offset, size, buffer, &description);
|
||||
}
|
||||
|
||||
// FIXME: Allow passing different block sizes to the constructor
|
||||
LoopDevice::LoopDevice(NonnullRefPtr<Custody> backing_custody, unsigned index)
|
||||
: BlockDevice(20, index, 512)
|
||||
, m_backing_custody(backing_custody)
|
||||
, m_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> LoopDevice::ioctl(OpenFileDescription&, unsigned, Userspace<void*>)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue