mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
Kernel: Add FUSE support
This adds both the fuse device (used for communication between the kernel and the filesystem) and filesystem implementation itself.
This commit is contained in:
parent
f923016e0b
commit
a08d1637e2
Notes:
sideshowbarker
2024-07-17 05:05:51 +09:00
Author: https://github.com/implicitfield Commit: https://github.com/SerenityOS/serenity/commit/a08d1637e2 Pull-request: https://github.com/SerenityOS/serenity/pull/23098 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/supercomputer7
15 changed files with 1235 additions and 0 deletions
|
@ -21,6 +21,7 @@
|
||||||
#include <Kernel/Bus/VirtIO/Transport/PCIe/Detect.h>
|
#include <Kernel/Bus/VirtIO/Transport/PCIe/Detect.h>
|
||||||
#include <Kernel/Devices/Audio/Management.h>
|
#include <Kernel/Devices/Audio/Management.h>
|
||||||
#include <Kernel/Devices/DeviceManagement.h>
|
#include <Kernel/Devices/DeviceManagement.h>
|
||||||
|
#include <Kernel/Devices/FUSEDevice.h>
|
||||||
#include <Kernel/Devices/GPU/Console/BootFramebufferConsole.h>
|
#include <Kernel/Devices/GPU/Console/BootFramebufferConsole.h>
|
||||||
#include <Kernel/Devices/GPU/Management.h>
|
#include <Kernel/Devices/GPU/Management.h>
|
||||||
#include <Kernel/Devices/Generic/DeviceControlDevice.h>
|
#include <Kernel/Devices/Generic/DeviceControlDevice.h>
|
||||||
|
@ -442,6 +443,7 @@ void init_stage2(void*)
|
||||||
(void)MemoryDevice::must_create().leak_ref();
|
(void)MemoryDevice::must_create().leak_ref();
|
||||||
(void)ZeroDevice::must_create().leak_ref();
|
(void)ZeroDevice::must_create().leak_ref();
|
||||||
(void)FullDevice::must_create().leak_ref();
|
(void)FullDevice::must_create().leak_ref();
|
||||||
|
(void)FUSEDevice::must_create().leak_ref();
|
||||||
(void)RandomDevice::must_create().leak_ref();
|
(void)RandomDevice::must_create().leak_ref();
|
||||||
(void)SelfTTYDevice::must_create().leak_ref();
|
(void)SelfTTYDevice::must_create().leak_ref();
|
||||||
PTYMultiplexer::initialize();
|
PTYMultiplexer::initialize();
|
||||||
|
|
|
@ -66,6 +66,7 @@ set(KERNEL_SOURCES
|
||||||
Devices/CharacterDevice.cpp
|
Devices/CharacterDevice.cpp
|
||||||
Devices/Device.cpp
|
Devices/Device.cpp
|
||||||
Devices/DeviceManagement.cpp
|
Devices/DeviceManagement.cpp
|
||||||
|
Devices/FUSEDevice.cpp
|
||||||
Devices/PCISerialDevice.cpp
|
Devices/PCISerialDevice.cpp
|
||||||
Devices/SerialDevice.cpp
|
Devices/SerialDevice.cpp
|
||||||
Devices/HID/AllMiceDevice.cpp
|
Devices/HID/AllMiceDevice.cpp
|
||||||
|
@ -149,6 +150,8 @@ set(KERNEL_SOURCES
|
||||||
FileSystem/File.cpp
|
FileSystem/File.cpp
|
||||||
FileSystem/FileBackedFileSystem.cpp
|
FileSystem/FileBackedFileSystem.cpp
|
||||||
FileSystem/FileSystem.cpp
|
FileSystem/FileSystem.cpp
|
||||||
|
FileSystem/FUSE/FileSystem.cpp
|
||||||
|
FileSystem/FUSE/Inode.cpp
|
||||||
FileSystem/Inode.cpp
|
FileSystem/Inode.cpp
|
||||||
FileSystem/InodeFile.cpp
|
FileSystem/InodeFile.cpp
|
||||||
FileSystem/InodeMetadata.cpp
|
FileSystem/InodeMetadata.cpp
|
||||||
|
|
|
@ -87,6 +87,10 @@
|
||||||
#cmakedefine01 FORK_DEBUG
|
#cmakedefine01 FORK_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FUSE_DEBUG
|
||||||
|
#cmakedefine01 FUSE_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FUTEX_DEBUG
|
#ifndef FUTEX_DEBUG
|
||||||
#cmakedefine01 FUTEX_DEBUG
|
#cmakedefine01 FUTEX_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
198
Kernel/Devices/FUSEDevice.cpp
Normal file
198
Kernel/Devices/FUSEDevice.cpp
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <Kernel/Devices/DeviceManagement.h>
|
||||||
|
#include <Kernel/Devices/FUSEDevice.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/Definitions.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/FUSEConnection.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT NonnullLockRefPtr<FUSEDevice> FUSEDevice::must_create()
|
||||||
|
{
|
||||||
|
return MUST(DeviceManagement::try_create_device<FUSEDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT FUSEDevice::FUSEDevice()
|
||||||
|
: CharacterDevice(10, 229)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UNMAP_AFTER_INIT FUSEDevice::~FUSEDevice() = default;
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEDevice::initialize_instance(OpenFileDescription const& fd)
|
||||||
|
{
|
||||||
|
return m_instances.with([&](auto& instances) -> ErrorOr<void> {
|
||||||
|
for (auto const& instance : instances)
|
||||||
|
VERIFY(instance.fd != &fd);
|
||||||
|
|
||||||
|
TRY(instances.try_append({
|
||||||
|
&fd,
|
||||||
|
TRY(KBuffer::try_create_with_size("FUSE: Pending request buffer"sv, 0x21000)),
|
||||||
|
TRY(KBuffer::try_create_with_size("FUSE: Response buffer"sv, 0x21000)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FUSEDevice::can_read(OpenFileDescription const& fd, u64) const
|
||||||
|
{
|
||||||
|
return m_instances.with([&](auto& instances) {
|
||||||
|
Optional<size_t> instance_index = {};
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
if (instances[i].fd == &fd) {
|
||||||
|
instance_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!instance_index.has_value()) {
|
||||||
|
VERIFY(instances.is_empty());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& instance = instances[instance_index.value()];
|
||||||
|
return instance.buffer_ready || instance.drop_request;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FUSEDevice::can_write(OpenFileDescription const&, u64) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<size_t> FUSEDevice::read(OpenFileDescription& fd, u64, UserOrKernelBuffer& buffer, size_t size)
|
||||||
|
{
|
||||||
|
return m_instances.with([&](auto& instances) -> ErrorOr<size_t> {
|
||||||
|
Optional<size_t> instance_index = {};
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
if (instances[i].fd == &fd) {
|
||||||
|
instance_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance_index.has_value())
|
||||||
|
return Error::from_errno(ENODEV);
|
||||||
|
|
||||||
|
auto& instance = instances[instance_index.value()];
|
||||||
|
|
||||||
|
if (instance.drop_request) {
|
||||||
|
instance.drop_request = false;
|
||||||
|
|
||||||
|
instances.remove(instance_index.value());
|
||||||
|
return Error::from_errno(ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0x21000)
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
if (!instance.buffer_ready)
|
||||||
|
return Error::from_errno(ENOENT);
|
||||||
|
|
||||||
|
TRY(buffer.write(instance.pending_request->bytes()));
|
||||||
|
instance.buffer_ready = false;
|
||||||
|
return instance.pending_request->size();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<size_t> FUSEDevice::write(OpenFileDescription& description, u64, UserOrKernelBuffer const& buffer, size_t size)
|
||||||
|
{
|
||||||
|
return m_instances.with([&](auto& instances) -> ErrorOr<size_t> {
|
||||||
|
Optional<size_t> instance_index = {};
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
if (instances[i].fd == &description) {
|
||||||
|
instance_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance_index.has_value())
|
||||||
|
return Error::from_errno(ENODEV);
|
||||||
|
|
||||||
|
auto& instance = instances[instance_index.value()];
|
||||||
|
|
||||||
|
if (instance.expecting_header) {
|
||||||
|
memset(instance.response->data(), 0, instance.response->size());
|
||||||
|
|
||||||
|
fuse_out_header header;
|
||||||
|
TRY(buffer.read(&header, 0, sizeof(fuse_out_header)));
|
||||||
|
|
||||||
|
dbgln_if(FUSE_DEBUG, "header: length: {}, error: {}, unique: {}", header.len, header.error, header.unique);
|
||||||
|
memcpy(instance.response->data(), &header, sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
if (header.len > sizeof(fuse_out_header))
|
||||||
|
instance.expecting_header = false;
|
||||||
|
else
|
||||||
|
instance.response_ready = true;
|
||||||
|
} else {
|
||||||
|
fuse_out_header* existing_header = bit_cast<fuse_out_header*>(instance.response->data());
|
||||||
|
|
||||||
|
instance.expecting_header = true;
|
||||||
|
if (existing_header->len > instance.response->size())
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
instance.response_ready = true;
|
||||||
|
u64 length = existing_header->len - sizeof(fuse_out_header);
|
||||||
|
dbgln_if(FUSE_DEBUG, "request: response length: {}", length);
|
||||||
|
TRY(buffer.read(instance.response->data() + sizeof(fuse_out_header), 0, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullOwnPtr<KBuffer>> FUSEDevice::send_request_and_wait_for_a_reply(OpenFileDescription const& description, Bytes bytes)
|
||||||
|
{
|
||||||
|
return m_instances.with([&](auto& instances) -> ErrorOr<NonnullOwnPtr<KBuffer>> {
|
||||||
|
Optional<size_t> instance_index = {};
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
if (instances[i].fd == &description) {
|
||||||
|
instance_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY(instance_index.has_value());
|
||||||
|
auto& instance = instances[instance_index.value()];
|
||||||
|
|
||||||
|
VERIFY(!instance.drop_request);
|
||||||
|
VERIFY(bytes.size() <= 0x21000);
|
||||||
|
|
||||||
|
memset(instance.pending_request->data(), 0, instance.pending_request->size());
|
||||||
|
memcpy(instance.pending_request->data(), bytes.data(), bytes.size());
|
||||||
|
instance.buffer_ready = true;
|
||||||
|
evaluate_block_conditions();
|
||||||
|
|
||||||
|
while (!instance.response_ready)
|
||||||
|
(void)Thread::current()->sleep(Duration::from_microseconds(100));
|
||||||
|
|
||||||
|
auto result = KBuffer::try_create_with_bytes("FUSEDevice: Response"sv, instance.response->bytes());
|
||||||
|
instance.response_ready = false;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FUSEDevice::shutdown_for_description(OpenFileDescription const& description)
|
||||||
|
{
|
||||||
|
m_instances.with([&](auto& instances) {
|
||||||
|
Optional<size_t> instance_index = {};
|
||||||
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
|
if (instances[i].fd == &description) {
|
||||||
|
instance_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VERIFY(instance_index.has_value());
|
||||||
|
instances[instance_index.value()].drop_request = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
evaluate_block_conditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
Kernel/Devices/FUSEDevice.h
Normal file
53
Kernel/Devices/FUSEDevice.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Atomic.h>
|
||||||
|
#include <Kernel/Devices/CharacterDevice.h>
|
||||||
|
#include <Kernel/Locking/Mutex.h>
|
||||||
|
#include <Kernel/Locking/SpinlockProtected.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
struct FUSEInstance {
|
||||||
|
OpenFileDescription const* fd = nullptr;
|
||||||
|
NonnullOwnPtr<KBuffer> pending_request;
|
||||||
|
NonnullOwnPtr<KBuffer> response;
|
||||||
|
bool drop_request = false;
|
||||||
|
bool buffer_ready = false;
|
||||||
|
bool response_ready = false;
|
||||||
|
bool expecting_header = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FUSEDevice final : public CharacterDevice {
|
||||||
|
friend class DeviceManagement;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static NonnullLockRefPtr<FUSEDevice> must_create();
|
||||||
|
virtual ~FUSEDevice() override;
|
||||||
|
|
||||||
|
ErrorOr<void> initialize_instance(OpenFileDescription const&);
|
||||||
|
ErrorOr<NonnullOwnPtr<KBuffer>> send_request_and_wait_for_a_reply(OpenFileDescription const&, Bytes);
|
||||||
|
void shutdown_for_description(OpenFileDescription const&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FUSEDevice();
|
||||||
|
|
||||||
|
// ^Device
|
||||||
|
virtual bool is_openable_by_jailed_processes() const override { return false; }
|
||||||
|
|
||||||
|
// ^CharacterDevice
|
||||||
|
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
|
||||||
|
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override;
|
||||||
|
virtual bool can_read(OpenFileDescription const&, u64) const override;
|
||||||
|
virtual bool can_write(OpenFileDescription const&, u64) const override;
|
||||||
|
virtual StringView class_name() const override { return "FUSEDevice"sv; }
|
||||||
|
|
||||||
|
SpinlockProtected<Vector<FUSEInstance>, LockRank::None> m_instances;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
308
Kernel/FileSystem/FUSE/Definitions.h
Normal file
308
Kernel/FileSystem/FUSE/Definitions.h
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
#define FUSE_KERNEL_VERSION 7
|
||||||
|
#define FUSE_KERNEL_MINOR_VERSION 39
|
||||||
|
|
||||||
|
struct fuse_attr {
|
||||||
|
uint64_t ino;
|
||||||
|
uint64_t size;
|
||||||
|
uint64_t blocks;
|
||||||
|
uint64_t atime;
|
||||||
|
uint64_t mtime;
|
||||||
|
uint64_t ctime;
|
||||||
|
uint32_t atimensec;
|
||||||
|
uint32_t mtimensec;
|
||||||
|
uint32_t ctimensec;
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t nlink;
|
||||||
|
uint32_t uid;
|
||||||
|
uint32_t gid;
|
||||||
|
uint32_t rdev;
|
||||||
|
uint32_t blksize;
|
||||||
|
uint32_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bitmasks for fuse_setattr_in.valid
|
||||||
|
#define FATTR_MODE (1 << 0)
|
||||||
|
#define FATTR_UID (1 << 1)
|
||||||
|
#define FATTR_GID (1 << 2)
|
||||||
|
#define FATTR_SIZE (1 << 3)
|
||||||
|
#define FATTR_ATIME (1 << 4)
|
||||||
|
#define FATTR_MTIME (1 << 5)
|
||||||
|
#define FATTR_FH (1 << 6)
|
||||||
|
#define FATTR_ATIME_NOW (1 << 7)
|
||||||
|
#define FATTR_MTIME_NOW (1 << 8)
|
||||||
|
#define FATTR_LOCKOWNER (1 << 9)
|
||||||
|
#define FATTR_CTIME (1 << 10)
|
||||||
|
#define FATTR_KILL_SUIDGID (1 << 11)
|
||||||
|
|
||||||
|
enum class FUSEOpcode {
|
||||||
|
FUSE_LOOKUP = 1,
|
||||||
|
FUSE_FORGET = 2, // no reply
|
||||||
|
FUSE_GETATTR = 3,
|
||||||
|
FUSE_SETATTR = 4,
|
||||||
|
FUSE_READLINK = 5,
|
||||||
|
FUSE_SYMLINK = 6,
|
||||||
|
FUSE_MKNOD = 8,
|
||||||
|
FUSE_MKDIR = 9,
|
||||||
|
FUSE_UNLINK = 10,
|
||||||
|
FUSE_RMDIR = 11,
|
||||||
|
FUSE_RENAME = 12,
|
||||||
|
FUSE_LINK = 13,
|
||||||
|
FUSE_OPEN = 14,
|
||||||
|
FUSE_READ = 15,
|
||||||
|
FUSE_WRITE = 16,
|
||||||
|
FUSE_STATFS = 17,
|
||||||
|
FUSE_RELEASE = 18,
|
||||||
|
FUSE_FSYNC = 20,
|
||||||
|
FUSE_SETXATTR = 21,
|
||||||
|
FUSE_GETXATTR = 22,
|
||||||
|
FUSE_LISTXATTR = 23,
|
||||||
|
FUSE_REMOVEXATTR = 24,
|
||||||
|
FUSE_FLUSH = 25,
|
||||||
|
FUSE_INIT = 26,
|
||||||
|
FUSE_OPENDIR = 27,
|
||||||
|
FUSE_READDIR = 28,
|
||||||
|
FUSE_RELEASEDIR = 29,
|
||||||
|
FUSE_FSYNCDIR = 30,
|
||||||
|
FUSE_GETLK = 31,
|
||||||
|
FUSE_SETLK = 32,
|
||||||
|
FUSE_SETLKW = 33,
|
||||||
|
FUSE_ACCESS = 34,
|
||||||
|
FUSE_CREATE = 35,
|
||||||
|
FUSE_INTERRUPT = 36,
|
||||||
|
FUSE_BMAP = 37,
|
||||||
|
FUSE_DESTROY = 38,
|
||||||
|
FUSE_IOCTL = 39,
|
||||||
|
FUSE_POLL = 40,
|
||||||
|
FUSE_NOTIFY_REPLY = 41,
|
||||||
|
FUSE_BATCH_FORGET = 42,
|
||||||
|
FUSE_FALLOCATE = 43,
|
||||||
|
FUSE_READDIRPLUS = 44,
|
||||||
|
FUSE_RENAME2 = 45,
|
||||||
|
FUSE_LSEEK = 46,
|
||||||
|
FUSE_COPY_FILE_RANGE = 47,
|
||||||
|
FUSE_SETUPMAPPING = 48,
|
||||||
|
FUSE_REMOVEMAPPING = 49,
|
||||||
|
FUSE_SYNCFS = 50,
|
||||||
|
FUSE_TMPFILE = 51,
|
||||||
|
FUSE_STATX = 52,
|
||||||
|
|
||||||
|
// CUSE specific operations
|
||||||
|
CUSE_INIT = 4096,
|
||||||
|
|
||||||
|
// Reserved opcodes: helpful to detect structure endian-ness
|
||||||
|
CUSE_INIT_BSWAP_RESERVED = 1048576, // CUSE_INIT << 8
|
||||||
|
FUSE_INIT_BSWAP_RESERVED = 436207616, // FUSE_INIT << 24
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_entry_out {
|
||||||
|
uint64_t nodeid; /* Inode ID */
|
||||||
|
uint64_t generation; /* Inode generation: nodeid:gen must
|
||||||
|
be unique for the fs's lifetime */
|
||||||
|
uint64_t entry_valid; /* Cache timeout for the name */
|
||||||
|
uint64_t attr_valid; /* Cache timeout for the attributes */
|
||||||
|
uint32_t entry_valid_nsec;
|
||||||
|
uint32_t attr_valid_nsec;
|
||||||
|
struct fuse_attr attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_getattr_in {
|
||||||
|
uint32_t getattr_flags;
|
||||||
|
uint32_t dummy;
|
||||||
|
uint64_t fh;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_attr_out {
|
||||||
|
uint64_t attr_valid; /* Cache timeout for the attributes */
|
||||||
|
uint32_t attr_valid_nsec;
|
||||||
|
uint32_t dummy;
|
||||||
|
struct fuse_attr attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_mknod_in {
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t rdev;
|
||||||
|
uint32_t umask;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_mkdir_in {
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t umask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_rename_in {
|
||||||
|
uint64_t newdir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_link_in {
|
||||||
|
uint64_t oldnodeid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_setattr_in {
|
||||||
|
uint32_t valid;
|
||||||
|
uint32_t padding;
|
||||||
|
uint64_t fh;
|
||||||
|
uint64_t size;
|
||||||
|
uint64_t lock_owner;
|
||||||
|
uint64_t atime;
|
||||||
|
uint64_t mtime;
|
||||||
|
uint64_t ctime;
|
||||||
|
uint32_t atimensec;
|
||||||
|
uint32_t mtimensec;
|
||||||
|
uint32_t ctimensec;
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t unused4;
|
||||||
|
uint32_t uid;
|
||||||
|
uint32_t gid;
|
||||||
|
uint32_t unused5;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_open_in {
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_create_in {
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t mode;
|
||||||
|
uint32_t umask;
|
||||||
|
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_open_out {
|
||||||
|
uint64_t fh;
|
||||||
|
uint32_t open_flags;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_release_in {
|
||||||
|
uint64_t fh;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t release_flags;
|
||||||
|
uint64_t lock_owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_flush_in {
|
||||||
|
uint64_t fh;
|
||||||
|
uint32_t unused;
|
||||||
|
uint32_t padding;
|
||||||
|
uint64_t lock_owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_read_in {
|
||||||
|
uint64_t fh;
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t read_flags;
|
||||||
|
uint64_t lock_owner;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_write_in {
|
||||||
|
uint64_t fh;
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t write_flags;
|
||||||
|
uint64_t lock_owner;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_write_out {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_access_in {
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_init_in {
|
||||||
|
uint32_t major;
|
||||||
|
uint32_t minor;
|
||||||
|
uint32_t max_readahead;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t flags2;
|
||||||
|
uint32_t unused[11];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_init_out {
|
||||||
|
uint32_t major;
|
||||||
|
uint32_t minor;
|
||||||
|
uint32_t max_readahead;
|
||||||
|
uint32_t flags;
|
||||||
|
uint16_t max_background;
|
||||||
|
uint16_t congestion_threshold;
|
||||||
|
uint32_t max_write;
|
||||||
|
uint32_t time_gran;
|
||||||
|
uint16_t max_pages;
|
||||||
|
uint16_t map_alignment;
|
||||||
|
uint32_t flags2;
|
||||||
|
uint32_t unused[7];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_in_header {
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t opcode;
|
||||||
|
uint64_t unique;
|
||||||
|
uint64_t nodeid;
|
||||||
|
uint32_t uid;
|
||||||
|
uint32_t gid;
|
||||||
|
uint32_t pid;
|
||||||
|
uint16_t total_extlen; /* length of extensions in 8byte units */
|
||||||
|
uint16_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_out_header {
|
||||||
|
uint32_t len;
|
||||||
|
int32_t error;
|
||||||
|
uint64_t unique;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_dirent {
|
||||||
|
uint64_t ino;
|
||||||
|
uint64_t off;
|
||||||
|
uint32_t namelen;
|
||||||
|
uint32_t type;
|
||||||
|
char name[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Align variable length records to 64bit boundary */
|
||||||
|
#define FUSE_REC_ALIGN(x) \
|
||||||
|
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
|
||||||
|
|
||||||
|
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
|
||||||
|
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
|
||||||
|
#define FUSE_DIRENT_SIZE(d) \
|
||||||
|
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
142
Kernel/FileSystem/FUSE/FUSEConnection.h
Normal file
142
Kernel/FileSystem/FUSE/FUSEConnection.h
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Kernel/FileSystem/FUSE/Definitions.h>
|
||||||
|
#include <Kernel/FileSystem/OpenFileDescription.h>
|
||||||
|
#include <Kernel/Library/KBuffer.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class FUSEConnection : public RefCounted<FUSEConnection> {
|
||||||
|
public:
|
||||||
|
static ErrorOr<NonnullRefPtr<FUSEConnection>> try_create(NonnullRefPtr<OpenFileDescription> description)
|
||||||
|
{
|
||||||
|
if (!description->is_device())
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
if (description->device()->class_name() != "FUSEDevice"sv)
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
auto connection = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEConnection(description)));
|
||||||
|
|
||||||
|
auto* device = bit_cast<FUSEDevice*>(description->device());
|
||||||
|
TRY(device->initialize_instance(*description));
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorOr<NonnullOwnPtr<KBuffer>> create_request(FUSEOpcode opcode, u32 nodeid, u32 unique, ReadonlyBytes request_body)
|
||||||
|
{
|
||||||
|
size_t request_length = sizeof(fuse_in_header) + request_body.size();
|
||||||
|
auto request = TRY(KBuffer::try_create_with_size("FUSE: Request"sv, request_length));
|
||||||
|
memset(request->data(), 0, request_length);
|
||||||
|
fuse_in_header* header = bit_cast<fuse_in_header*>(request->data());
|
||||||
|
header->len = request_length;
|
||||||
|
header->opcode = to_underlying(opcode);
|
||||||
|
header->unique = unique;
|
||||||
|
header->nodeid = nodeid;
|
||||||
|
|
||||||
|
// FIXME: Fill in proper values for these.
|
||||||
|
header->uid = 0;
|
||||||
|
header->gid = 0;
|
||||||
|
header->pid = 1;
|
||||||
|
|
||||||
|
u8* payload = bit_cast<u8*>(request->data() + sizeof(fuse_in_header));
|
||||||
|
memcpy(payload, request_body.data(), request_body.size());
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullOwnPtr<KBuffer>> send_request_and_wait_for_a_reply(FUSEOpcode opcode, u32 nodeid, ReadonlyBytes request_body)
|
||||||
|
{
|
||||||
|
auto* device = bit_cast<FUSEDevice*>(m_description->device());
|
||||||
|
|
||||||
|
// FIXME: Send the init request from the filesystem itself right after it has been
|
||||||
|
// mounted. (Without blocking the mount syscall.)
|
||||||
|
if (!m_initialized)
|
||||||
|
TRY(handle_init());
|
||||||
|
|
||||||
|
auto request = TRY(create_request(opcode, nodeid, m_unique, request_body));
|
||||||
|
auto response = TRY(device->send_request_and_wait_for_a_reply(m_description, request->bytes()));
|
||||||
|
|
||||||
|
if (validate_response(*response, m_unique++).is_error())
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
~FUSEConnection()
|
||||||
|
{
|
||||||
|
auto* device = bit_cast<FUSEDevice*>(m_description->device());
|
||||||
|
// Unblock the userspace daemon and tell it to shut down.
|
||||||
|
device->shutdown_for_description(m_description);
|
||||||
|
(void)m_description->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FUSEConnection(NonnullRefPtr<OpenFileDescription> description)
|
||||||
|
: m_description(description)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> validate_response(KBuffer const& response, u32 unique)
|
||||||
|
{
|
||||||
|
if (response.size() < sizeof(fuse_out_header)) {
|
||||||
|
dmesgln("FUSE: Received a request with a malformed header");
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response.data());
|
||||||
|
if (header->unique != unique) {
|
||||||
|
dmesgln("FUSE: Received a mismatched request");
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->len > response.size()) {
|
||||||
|
dmesgln("FUSE: Received an excessively large request");
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> handle_init()
|
||||||
|
{
|
||||||
|
fuse_init_in init_request;
|
||||||
|
init_request.major = FUSE_KERNEL_VERSION;
|
||||||
|
init_request.minor = FUSE_KERNEL_MINOR_VERSION;
|
||||||
|
init_request.max_readahead = 512;
|
||||||
|
init_request.flags = 0;
|
||||||
|
|
||||||
|
auto* device = bit_cast<FUSEDevice*>(m_description->device());
|
||||||
|
|
||||||
|
auto request = TRY(create_request(FUSEOpcode::FUSE_INIT, 0, 0, { &init_request, sizeof(init_request) }));
|
||||||
|
auto response = TRY(device->send_request_and_wait_for_a_reply(m_description, request->bytes()));
|
||||||
|
|
||||||
|
if (validate_response(*response, 0).is_error())
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
fuse_init_out* init = bit_cast<fuse_init_out*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
m_major = init->major;
|
||||||
|
m_minor = init->minor;
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<OpenFileDescription> m_description;
|
||||||
|
bool m_initialized { false };
|
||||||
|
u32 m_unique { 0 };
|
||||||
|
|
||||||
|
u32 m_major { 0 };
|
||||||
|
u32 m_minor { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
90
Kernel/FileSystem/FUSE/FileSystem.cpp
Normal file
90
Kernel/FileSystem/FUSE/FileSystem.cpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/IntegralMath.h>
|
||||||
|
#include <Kernel/Devices/DeviceManagement.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/FileSystem.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/Inode.h>
|
||||||
|
#include <Kernel/Tasks/Process.h>
|
||||||
|
#include <Kernel/Time/TimeManagement.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
struct [[gnu::packed]] FUSESpecificFlagsBytes {
|
||||||
|
u64 pid;
|
||||||
|
u64 device_inode;
|
||||||
|
u64 rootmode;
|
||||||
|
u64 gid;
|
||||||
|
u64 uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorOr<NonnullRefPtr<FileSystem>> FUSE::try_create(ReadonlyBytes mount_flags)
|
||||||
|
{
|
||||||
|
auto* fuse_mount_flags = reinterpret_cast<FUSESpecificFlagsBytes const*>(mount_flags.data());
|
||||||
|
|
||||||
|
auto description = TRY(Process::current().open_file_description(fuse_mount_flags->device_inode));
|
||||||
|
auto connection = TRY(FUSEConnection::try_create(description));
|
||||||
|
|
||||||
|
u64 rootmode = AK::reinterpret_as_octal(fuse_mount_flags->rootmode);
|
||||||
|
u64 gid = fuse_mount_flags->gid;
|
||||||
|
u64 uid = fuse_mount_flags->uid;
|
||||||
|
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSE(connection, rootmode, uid, gid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSE::handle_mount_unsigned_integer_flag(Bytes mount_file_specific_flags_buffer, StringView key, u64 value)
|
||||||
|
{
|
||||||
|
auto* fuse_mount_flags = reinterpret_cast<FUSESpecificFlagsBytes*>(mount_file_specific_flags_buffer.data());
|
||||||
|
if (key == "fd") {
|
||||||
|
fuse_mount_flags->device_inode = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (key == "rootmode") {
|
||||||
|
fuse_mount_flags->rootmode = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (key == "gid") {
|
||||||
|
fuse_mount_flags->gid = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (key == "uid") {
|
||||||
|
fuse_mount_flags->uid = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FUSE::FUSE(NonnullRefPtr<FUSEConnection> connection, u64 rootmode, u64 gid, u64 uid)
|
||||||
|
: m_connection(connection)
|
||||||
|
, m_rootmode(rootmode)
|
||||||
|
, m_gid(gid)
|
||||||
|
, m_uid(uid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FUSE::~FUSE() = default;
|
||||||
|
|
||||||
|
ErrorOr<void> FUSE::initialize()
|
||||||
|
{
|
||||||
|
m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEInode(*this)));
|
||||||
|
m_root_inode->m_metadata.mode = m_rootmode;
|
||||||
|
m_root_inode->m_metadata.uid = m_gid;
|
||||||
|
m_root_inode->m_metadata.gid = m_uid;
|
||||||
|
m_root_inode->m_metadata.size = 0;
|
||||||
|
m_root_inode->m_metadata.mtime = TimeManagement::boot_time();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Inode& FUSE::root_inode()
|
||||||
|
{
|
||||||
|
return *m_root_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 FUSE::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const
|
||||||
|
{
|
||||||
|
return ram_backed_file_type_to_directory_entry_type(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
Kernel/FileSystem/FUSE/FileSystem.h
Normal file
45
Kernel/FileSystem/FUSE/FileSystem.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/Devices/FUSEDevice.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/FUSEConnection.h>
|
||||||
|
#include <Kernel/FileSystem/FileSystem.h>
|
||||||
|
#include <Kernel/FileSystem/Inode.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class FUSEInode;
|
||||||
|
|
||||||
|
class FUSE final : public FileSystem {
|
||||||
|
friend class FUSEInode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~FUSE() override;
|
||||||
|
static ErrorOr<NonnullRefPtr<FileSystem>> try_create(ReadonlyBytes mount_flags);
|
||||||
|
|
||||||
|
static ErrorOr<void> handle_mount_unsigned_integer_flag(Bytes mount_file_specific_flags_buffer, StringView key, u64);
|
||||||
|
|
||||||
|
virtual ErrorOr<void> initialize() override;
|
||||||
|
virtual StringView class_name() const override { return "FUSE"sv; }
|
||||||
|
|
||||||
|
virtual Inode& root_inode() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override;
|
||||||
|
|
||||||
|
FUSE(NonnullRefPtr<FUSEConnection> connection, u64 rootmode, u64 gid, u64 uid);
|
||||||
|
|
||||||
|
RefPtr<FUSEInode> m_root_inode;
|
||||||
|
NonnullRefPtr<FUSEConnection> m_connection;
|
||||||
|
u64 m_rootmode { 0 };
|
||||||
|
u64 m_gid { 0 };
|
||||||
|
u64 m_uid { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
333
Kernel/FileSystem/FUSE/Inode.cpp
Normal file
333
Kernel/FileSystem/FUSE/Inode.cpp
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/FileSystem/FUSE/Definitions.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/Inode.h>
|
||||||
|
#include <Kernel/FileSystem/RAMBackedFileType.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
FUSEInode::FUSEInode(FUSE& fs, InodeIndex index)
|
||||||
|
: Inode(fs, index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FUSEInode::FUSEInode(FUSE& fs)
|
||||||
|
: Inode(fs, 1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FUSEInode::~FUSEInode() = default;
|
||||||
|
|
||||||
|
ErrorOr<size_t> FUSEInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const
|
||||||
|
{
|
||||||
|
VERIFY(m_inode_lock.is_locked());
|
||||||
|
VERIFY(!is_directory());
|
||||||
|
|
||||||
|
constexpr size_t max_read_size = 0x21000 - sizeof(fuse_in_header) - sizeof(fuse_read_in);
|
||||||
|
u64 id = TRY(try_open(false, O_RDONLY));
|
||||||
|
u32 nodeid = identifier().index().value();
|
||||||
|
|
||||||
|
size_t nread = 0;
|
||||||
|
size_t target_size = size;
|
||||||
|
while (target_size) {
|
||||||
|
size_t chunk_size = min(size, max_read_size);
|
||||||
|
fuse_read_in payload {};
|
||||||
|
payload.fh = id;
|
||||||
|
payload.offset = offset + nread;
|
||||||
|
payload.size = chunk_size;
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_READ, nodeid, { &payload, sizeof(payload) }));
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
u32 data_size = min(target_size, header->len - sizeof(fuse_out_header));
|
||||||
|
if (data_size == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
u8* data = bit_cast<u8*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
TRY(buffer.write(data, nread, data_size));
|
||||||
|
nread += data_size;
|
||||||
|
target_size -= data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(try_flush(id));
|
||||||
|
TRY(try_release(id, false));
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<size_t> FUSEInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& buffer, OpenFileDescription*)
|
||||||
|
{
|
||||||
|
VERIFY(m_inode_lock.is_locked());
|
||||||
|
VERIFY(!is_directory());
|
||||||
|
VERIFY(offset >= 0);
|
||||||
|
|
||||||
|
constexpr size_t max_write_size = 0x21000 - sizeof(fuse_in_header) - sizeof(fuse_write_in);
|
||||||
|
u64 id = TRY(try_open(false, O_WRONLY));
|
||||||
|
u32 nodeid = identifier().index().value();
|
||||||
|
|
||||||
|
size_t nwritten = 0;
|
||||||
|
while (size) {
|
||||||
|
size_t chunk_size = min(size, max_write_size);
|
||||||
|
auto request_buffer = TRY(KBuffer::try_create_with_size("FUSE: Write buffer"sv, sizeof(fuse_write_in) + chunk_size));
|
||||||
|
fuse_write_in* write_header = bit_cast<fuse_write_in*>(request_buffer->data());
|
||||||
|
write_header->fh = id;
|
||||||
|
write_header->offset = offset + nwritten;
|
||||||
|
write_header->size = chunk_size;
|
||||||
|
TRY(buffer.read(request_buffer->data() + sizeof(fuse_write_in), nwritten, chunk_size));
|
||||||
|
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_WRITE, nodeid, request_buffer->bytes()));
|
||||||
|
if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_write_out))
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
fuse_write_out* write_response = bit_cast<fuse_write_out*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
nwritten += write_response->size;
|
||||||
|
size -= write_response->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(try_flush(id));
|
||||||
|
TRY(try_release(id, false));
|
||||||
|
|
||||||
|
return nwritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
InodeMetadata FUSEInode::metadata() const
|
||||||
|
{
|
||||||
|
InodeMetadata metadata;
|
||||||
|
metadata.inode = identifier();
|
||||||
|
u32 id = identifier().index().value();
|
||||||
|
|
||||||
|
fuse_getattr_in payload {};
|
||||||
|
|
||||||
|
auto response_or_error = fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_GETATTR, id, { &payload, sizeof(payload) });
|
||||||
|
|
||||||
|
if (response_or_error.is_error())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto response = response_or_error.release_value();
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error || response->size() < sizeof(fuse_out_header) + sizeof(fuse_attr_out))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
fuse_attr_out* getattr_response = bit_cast<fuse_attr_out*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
metadata.mode = getattr_response->attr.mode;
|
||||||
|
metadata.size = getattr_response->attr.size;
|
||||||
|
metadata.block_size = getattr_response->attr.blksize;
|
||||||
|
metadata.block_count = getattr_response->attr.blocks;
|
||||||
|
|
||||||
|
metadata.uid = getattr_response->attr.uid;
|
||||||
|
metadata.gid = getattr_response->attr.gid;
|
||||||
|
metadata.link_count = getattr_response->attr.nlink;
|
||||||
|
metadata.atime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.atime);
|
||||||
|
metadata.ctime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.ctime);
|
||||||
|
metadata.mtime = UnixDateTime::from_seconds_since_epoch(getattr_response->attr.mtime);
|
||||||
|
metadata.major_device = major_from_encoded_device(getattr_response->attr.rdev);
|
||||||
|
metadata.minor_device = minor_from_encoded_device(getattr_response->attr.rdev);
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<u64> FUSEInode::try_open(bool directory, u32 flags) const
|
||||||
|
{
|
||||||
|
u32 id = identifier().index().value();
|
||||||
|
|
||||||
|
fuse_open_in payload {};
|
||||||
|
payload.flags = flags;
|
||||||
|
|
||||||
|
auto opcode = directory ? FUSEOpcode::FUSE_OPENDIR : FUSEOpcode::FUSE_OPEN;
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(opcode, id, { &payload, sizeof(payload) }));
|
||||||
|
if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_open_out))
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
fuse_open_out* open_response = bit_cast<fuse_open_out*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
return open_response->fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::try_flush(u64 id) const
|
||||||
|
{
|
||||||
|
u32 nodeid = identifier().index().value();
|
||||||
|
|
||||||
|
fuse_flush_in payload {};
|
||||||
|
payload.fh = id;
|
||||||
|
(void)TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_FLUSH, nodeid, { &payload, sizeof(payload) }));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::try_release(u64 id, bool directory) const
|
||||||
|
{
|
||||||
|
u32 nodeid = identifier().index().value();
|
||||||
|
|
||||||
|
fuse_release_in payload {};
|
||||||
|
payload.fh = id;
|
||||||
|
auto opcode = directory ? FUSEOpcode::FUSE_RELEASEDIR : FUSEOpcode::FUSE_RELEASE;
|
||||||
|
(void)TRY(fs().m_connection->send_request_and_wait_for_a_reply(opcode, nodeid, { &payload, sizeof(payload) }));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t get_dirent_entry_length(size_t name_length)
|
||||||
|
{
|
||||||
|
return name_length + FUSE_NAME_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t get_dirent_entry_length_padded(size_t name_length)
|
||||||
|
{
|
||||||
|
return FUSE_DIRENT_ALIGN(get_dirent_entry_length(name_length));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
|
||||||
|
{
|
||||||
|
u64 id = TRY(try_open(true, 0));
|
||||||
|
u32 nodeid = identifier().index().value();
|
||||||
|
|
||||||
|
fuse_read_in payload {};
|
||||||
|
payload.fh = id;
|
||||||
|
payload.size = 4096;
|
||||||
|
while (true) {
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_READDIR, nodeid, { &payload, sizeof(payload) }));
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->len == sizeof(fuse_out_header))
|
||||||
|
break;
|
||||||
|
char* dirents = bit_cast<char*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
u32 total_size = header->len - sizeof(fuse_out_header);
|
||||||
|
u32 offset = 0;
|
||||||
|
while (offset < total_size) {
|
||||||
|
fuse_dirent* dirent = bit_cast<fuse_dirent*>(dirents + offset);
|
||||||
|
if (dirent->ino == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (dirent->namelen > NAME_MAX)
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
TRY(callback({ { dirent->name, dirent->namelen }, { fsid(), dirent->ino }, to_underlying(ram_backed_file_type_from_mode(dirent->type << 12)) }));
|
||||||
|
offset += get_dirent_entry_length_padded(dirent->namelen);
|
||||||
|
}
|
||||||
|
payload.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(try_release(id, true));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullRefPtr<Inode>> FUSEInode::lookup(StringView name)
|
||||||
|
{
|
||||||
|
auto name_buffer = TRY(KBuffer::try_create_with_size("FUSE: Lookup name string"sv, name.length() + 1));
|
||||||
|
memset(name_buffer->data(), 0, name_buffer->size());
|
||||||
|
memcpy(name_buffer->data(), name.characters_without_null_termination(), name.length());
|
||||||
|
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_LOOKUP, identifier().index().value(), name_buffer->bytes()));
|
||||||
|
if (response->size() < sizeof(fuse_out_header) + sizeof(fuse_entry_out))
|
||||||
|
return Error::from_errno(EIO);
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
fuse_entry_out* entry = bit_cast<fuse_entry_out*>(response->data() + sizeof(fuse_out_header));
|
||||||
|
|
||||||
|
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FUSEInode(fs(), entry->nodeid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::flush_metadata()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::add_child(Inode&, StringView, mode_t)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullRefPtr<Inode>> FUSEInode::create_child(StringView, mode_t, dev_t, UserID, GroupID)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::remove_child(StringView)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::replace_child(StringView, Inode&)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::chmod(mode_t)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::chown(UserID, GroupID)
|
||||||
|
{
|
||||||
|
return ENOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::truncate_locked(u64 new_size)
|
||||||
|
{
|
||||||
|
VERIFY(m_inode_lock.is_locked());
|
||||||
|
VERIFY(!is_directory());
|
||||||
|
u64 id = TRY(try_open(is_directory(), 0));
|
||||||
|
|
||||||
|
fuse_setattr_in setattr {};
|
||||||
|
setattr.fh = id;
|
||||||
|
setattr.valid = FATTR_SIZE;
|
||||||
|
setattr.size = new_size;
|
||||||
|
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_SETATTR, identifier().index().value(), { &setattr, sizeof(setattr) }));
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
return try_release(id, is_directory());
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FUSEInode::update_timestamps(Optional<UnixDateTime> atime, Optional<UnixDateTime> ctime, Optional<UnixDateTime> mtime)
|
||||||
|
{
|
||||||
|
MutexLocker locker(m_inode_lock);
|
||||||
|
|
||||||
|
u64 id = TRY(try_open(is_directory(), 0));
|
||||||
|
fuse_setattr_in setattr {};
|
||||||
|
setattr.fh = id;
|
||||||
|
|
||||||
|
if (atime.has_value()) {
|
||||||
|
setattr.valid |= FATTR_ATIME;
|
||||||
|
setattr.atime = atime.value().to_timespec().tv_sec;
|
||||||
|
}
|
||||||
|
if (ctime.has_value()) {
|
||||||
|
setattr.valid |= FATTR_CTIME;
|
||||||
|
setattr.ctime = ctime.value().to_timespec().tv_sec;
|
||||||
|
}
|
||||||
|
if (mtime.has_value()) {
|
||||||
|
setattr.valid |= FATTR_MTIME;
|
||||||
|
setattr.mtime = mtime.value().to_timespec().tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response = TRY(fs().m_connection->send_request_and_wait_for_a_reply(FUSEOpcode::FUSE_SETATTR, identifier().index().value(), { &setattr, sizeof(setattr) }));
|
||||||
|
|
||||||
|
fuse_out_header* header = bit_cast<fuse_out_header*>(response->data());
|
||||||
|
if (header->error)
|
||||||
|
return Error::from_errno(-header->error);
|
||||||
|
|
||||||
|
return try_release(id, is_directory());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
Kernel/FileSystem/FUSE/Inode.h
Normal file
51
Kernel/FileSystem/FUSE/Inode.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/FileSystem.h>
|
||||||
|
#include <Kernel/FileSystem/Inode.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class FUSEInode final : public Inode {
|
||||||
|
friend class FUSE;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~FUSEInode() override;
|
||||||
|
|
||||||
|
FUSE& fs() { return static_cast<FUSE&>(Inode::fs()); }
|
||||||
|
FUSE const& fs() const { return static_cast<FUSE const&>(Inode::fs()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FUSEInode(FUSE&, InodeIndex);
|
||||||
|
FUSEInode(FUSE&);
|
||||||
|
|
||||||
|
// ^Inode
|
||||||
|
virtual ErrorOr<size_t> read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override;
|
||||||
|
virtual InodeMetadata metadata() const override;
|
||||||
|
virtual ErrorOr<void> traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)>) const override;
|
||||||
|
virtual ErrorOr<NonnullRefPtr<Inode>> lookup(StringView name) override;
|
||||||
|
virtual ErrorOr<void> flush_metadata() override;
|
||||||
|
virtual ErrorOr<size_t> write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override;
|
||||||
|
virtual ErrorOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
|
||||||
|
virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
|
||||||
|
virtual ErrorOr<void> remove_child(StringView name) override;
|
||||||
|
virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
|
||||||
|
virtual ErrorOr<void> chmod(mode_t) override;
|
||||||
|
virtual ErrorOr<void> chown(UserID, GroupID) override;
|
||||||
|
virtual ErrorOr<void> truncate_locked(u64) override;
|
||||||
|
virtual ErrorOr<void> update_timestamps(Optional<UnixDateTime> atime, Optional<UnixDateTime> ctime, Optional<UnixDateTime> mtime) override;
|
||||||
|
|
||||||
|
ErrorOr<u64> try_open(bool directory, u32 flags) const;
|
||||||
|
ErrorOr<void> try_flush(u64 id) const;
|
||||||
|
ErrorOr<void> try_release(u64 id, bool directory) const;
|
||||||
|
|
||||||
|
InodeMetadata m_metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -29,6 +29,7 @@
|
||||||
#include <Kernel/FileSystem/DevPtsFS/FileSystem.h>
|
#include <Kernel/FileSystem/DevPtsFS/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/Ext2FS/FileSystem.h>
|
#include <Kernel/FileSystem/Ext2FS/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/FATFS/FileSystem.h>
|
#include <Kernel/FileSystem/FATFS/FileSystem.h>
|
||||||
|
#include <Kernel/FileSystem/FUSE/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/ISO9660FS/FileSystem.h>
|
#include <Kernel/FileSystem/ISO9660FS/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/Plan9FS/FileSystem.h>
|
#include <Kernel/FileSystem/Plan9FS/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/ProcFS/FileSystem.h>
|
#include <Kernel/FileSystem/ProcFS/FileSystem.h>
|
||||||
|
@ -70,6 +71,7 @@ static constexpr FileSystemInitializer s_initializers[] = {
|
||||||
{ "iso9660"sv, "ISO9660FS"sv, true, true, true, ISO9660FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
{ "iso9660"sv, "ISO9660FS"sv, true, true, true, ISO9660FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
||||||
{ "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
{ "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
||||||
{ "devloop"sv, "DevLoopFS"sv, false, false, false, {}, DevLoopFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
{ "devloop"sv, "DevLoopFS"sv, false, false, false, {}, DevLoopFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
||||||
|
{ "fuse"sv, "FUSE"sv, false, false, false, {}, FUSE::try_create, handle_mount_boolean_flag_as_invalid, FUSE::handle_mount_unsigned_integer_flag, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorOr<FileSystemInitializer const*> VirtualFileSystem::find_filesystem_type_initializer(StringView fs_type)
|
ErrorOr<FileSystemInitializer const*> VirtualFileSystem::find_filesystem_type_initializer(StringView fs_type)
|
||||||
|
|
|
@ -55,6 +55,7 @@ set(FILE_WATCHER_DEBUG ON)
|
||||||
set(FILL_PATH_DEBUG ON)
|
set(FILL_PATH_DEBUG ON)
|
||||||
set(FLAC_ENCODER_DEBUG ON)
|
set(FLAC_ENCODER_DEBUG ON)
|
||||||
set(FORK_DEBUG ON)
|
set(FORK_DEBUG ON)
|
||||||
|
set(FUSE_DEBUG ON)
|
||||||
set(FUTEX_DEBUG ON)
|
set(FUTEX_DEBUG ON)
|
||||||
set(FUTEXQUEUE_DEBUG ON)
|
set(FUTEXQUEUE_DEBUG ON)
|
||||||
set(GEMINI_DEBUG ON)
|
set(GEMINI_DEBUG ON)
|
||||||
|
|
|
@ -77,6 +77,8 @@ def should_check_file(filename):
|
||||||
return False
|
return False
|
||||||
if filename == 'Kernel/FileSystem/Ext2FS/Definitions.h':
|
if filename == 'Kernel/FileSystem/Ext2FS/Definitions.h':
|
||||||
return False
|
return False
|
||||||
|
if filename == 'Kernel/FileSystem/FUSE/Definitions.h':
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@ static ErrorOr<void> prepare_bare_minimum_devtmpfs_directory_structure()
|
||||||
TRY(Core::System::create_char_device("/dev/console"sv, 0666, 5, 1));
|
TRY(Core::System::create_char_device("/dev/console"sv, 0666, 5, 1));
|
||||||
TRY(Core::System::create_char_device("/dev/ptmx"sv, 0666, 5, 2));
|
TRY(Core::System::create_char_device("/dev/ptmx"sv, 0666, 5, 2));
|
||||||
TRY(Core::System::create_char_device("/dev/tty"sv, 0666, 5, 0));
|
TRY(Core::System::create_char_device("/dev/tty"sv, 0666, 5, 0));
|
||||||
|
TRY(Core::System::create_char_device("/dev/fuse"sv, 0666, 10, 229));
|
||||||
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
|
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
|
||||||
TRY(Core::System::create_block_device("/dev/kcov"sv, 0666, 30, 0));
|
TRY(Core::System::create_block_device("/dev/kcov"sv, 0666, 30, 0));
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue