1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:47:45 +00:00

Kernel: Move FS-related files into Kernel/FileSystem/

This commit is contained in:
Andreas Kling 2019-04-03 12:25:24 +02:00
parent f6d0e1052b
commit f9864940eb
28 changed files with 27 additions and 26 deletions

View file

@ -0,0 +1,69 @@
#include <Kernel/FileSystem/DevPtsFS.h>
#include <Kernel/SlavePTY.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <AK/StringBuilder.h>
static DevPtsFS* s_the;
DevPtsFS& DevPtsFS::the()
{
ASSERT(s_the);
return *s_the;
}
Retained<DevPtsFS> DevPtsFS::create()
{
return adopt(*new DevPtsFS);
}
DevPtsFS::DevPtsFS()
{
s_the = this;
}
DevPtsFS::~DevPtsFS()
{
}
bool DevPtsFS::initialize()
{
SynthFS::initialize();
return true;
}
const char* DevPtsFS::class_name() const
{
return "DevPtsFS";
}
Retained<SynthFSInode> DevPtsFS::create_slave_pty_device_file(unsigned index)
{
auto file = adopt(*new SynthFSInode(*this, generate_inode_index()));
StringBuilder builder;
builder.appendf("%u", index);
file->m_name = builder.to_string();
auto* device = VFS::the().get_device(11, index);
ASSERT(device);
file->m_metadata.size = 0;
file->m_metadata.uid = device->uid();
file->m_metadata.gid = device->gid();
file->m_metadata.mode = 0020644;
file->m_metadata.major_device = device->major();
file->m_metadata.minor_device = device->minor();
file->m_metadata.mtime = mepoch;
return file;
}
void DevPtsFS::register_slave_pty(SlavePTY& slave_pty)
{
auto inode_id = add_file(create_slave_pty_device_file(slave_pty.index()));
slave_pty.set_devpts_inode_id(inode_id);
}
void DevPtsFS::unregister_slave_pty(SlavePTY& slave_pty)
{
remove_file(slave_pty.devpts_inode_id().index());
}

View file

@ -0,0 +1,29 @@
#pragma once
#include <AK/Types.h>
#include <Kernel/FileSystem/SyntheticFileSystem.h>
class Process;
class SlavePTY;
class DevPtsFS final : public SynthFS {
public:
[[gnu::pure]] static DevPtsFS& the();
virtual ~DevPtsFS() override;
static Retained<DevPtsFS> create();
virtual bool initialize() override;
virtual const char* class_name() const override;
void register_slave_pty(SlavePTY&);
void unregister_slave_pty(SlavePTY&);
private:
DevPtsFS();
Retained<SynthFSInode> create_slave_pty_device_file(unsigned index);
HashTable<SlavePTY*> m_slave_ptys;
};

View file

@ -0,0 +1,140 @@
#include "DiskBackedFileSystem.h"
#include "i386.h"
#include <AK/InlineLRUCache.h>
#include <Kernel/Process.h>
//#define DBFS_DEBUG
struct BlockIdentifier {
unsigned fsid { 0 };
unsigned index { 0 };
bool operator==(const BlockIdentifier& other) const { return fsid == other.fsid && index == other.index; }
};
namespace AK {
template<>
struct Traits<BlockIdentifier> {
static unsigned hash(const BlockIdentifier& block_id) { return pair_int_hash(block_id.fsid, block_id.index); }
static void dump(const BlockIdentifier& block_id) { kprintf("[block %02u:%08u]", block_id.fsid, block_id.index); }
};
}
class CachedBlock : public InlineLinkedListNode<CachedBlock> {
public:
CachedBlock(const BlockIdentifier& block_id, const ByteBuffer& buffer)
: m_key(block_id)
, m_buffer(buffer)
{
}
BlockIdentifier m_key;
CachedBlock* m_next { nullptr };
CachedBlock* m_prev { nullptr };
ByteBuffer m_buffer;
};
Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>& block_cache()
{
static Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>* s_cache;
if (!s_cache)
s_cache = new Lockable<InlineLRUCache<BlockIdentifier, CachedBlock>>;
return *s_cache;
}
DiskBackedFS::DiskBackedFS(Retained<DiskDevice>&& device)
: m_device(move(device))
{
}
DiskBackedFS::~DiskBackedFS()
{
}
bool DiskBackedFS::write_block(unsigned index, const ByteBuffer& data)
{
#ifdef DBFS_DEBUG
kprintf("DiskBackedFileSystem::write_block %u, size=%u\n", index, data.size());
#endif
ASSERT(data.size() == block_size());
{
LOCKER(block_cache().lock());
if (auto* cached_block = block_cache().resource().get({ fsid(), index }))
cached_block->m_buffer = data;
}
DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
return device().write(base_offset, block_size(), data.pointer());
}
bool DiskBackedFS::write_blocks(unsigned index, unsigned count, const ByteBuffer& data)
{
#ifdef DBFS_DEBUG
kprintf("DiskBackedFileSystem::write_blocks %u x%u\n", index, count);
#endif
// FIXME: Maybe reorder this so we send out the write commands before updating cache?
{
LOCKER(block_cache().lock());
for (unsigned i = 0; i < count; ++i) {
if (auto* cached_block = block_cache().resource().get({ fsid(), index + i }))
cached_block->m_buffer = data.slice(i * block_size(), block_size());
}
}
DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
return device().write(base_offset, count * block_size(), data.pointer());
}
ByteBuffer DiskBackedFS::read_block(unsigned index) const
{
#ifdef DBFS_DEBUG
kprintf("DiskBackedFileSystem::read_block %u\n", index);
#endif
{
LOCKER(block_cache().lock());
if (auto* cached_block = block_cache().resource().get({ fsid(), index }))
return cached_block->m_buffer;
}
auto buffer = ByteBuffer::create_uninitialized(block_size());
//kprintf("created block buffer with size %u\n", block_size());
DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
auto* buffer_pointer = buffer.pointer();
bool success = device().read(base_offset, block_size(), buffer_pointer);
ASSERT(success);
ASSERT(buffer.size() == block_size());
{
LOCKER(block_cache().lock());
block_cache().resource().put({ fsid(), index }, CachedBlock({ fsid(), index }, buffer));
}
return buffer;
}
ByteBuffer DiskBackedFS::read_blocks(unsigned index, unsigned count) const
{
if (!count)
return nullptr;
if (count == 1)
return read_block(index);
auto blocks = ByteBuffer::create_uninitialized(count * block_size());
byte* out = blocks.pointer();
for (unsigned i = 0; i < count; ++i) {
auto block = read_block(index + i);
if (!block)
return nullptr;
memcpy(out, block.pointer(), block.size());
out += block_size();
}
return blocks;
}
void DiskBackedFS::set_block_size(unsigned block_size)
{
if (block_size == m_block_size)
return;
m_block_size = block_size;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "FileSystem.h"
#include <AK/ByteBuffer.h>
class DiskBackedFS : public FS {
public:
virtual ~DiskBackedFS() override;
DiskDevice& device() { return *m_device; }
const DiskDevice& device() const { return *m_device; }
int block_size() const { return m_block_size; }
protected:
explicit DiskBackedFS(Retained<DiskDevice>&&);
void set_block_size(unsigned);
ByteBuffer read_block(unsigned index) const;
ByteBuffer read_blocks(unsigned index, unsigned count) const;
bool write_block(unsigned index, const ByteBuffer&);
bool write_blocks(unsigned index, unsigned count, const ByteBuffer&);
private:
int m_block_size { 0 };
Retained<DiskDevice> m_device;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,147 @@
#pragma once
#include "DiskBackedFileSystem.h"
#include "UnixTypes.h"
#include <AK/OwnPtr.h>
#include "ext2_fs.h"
struct ext2_group_desc;
struct ext2_inode;
struct ext2_super_block;
class Ext2FS;
class Ext2FSInode final : public Inode {
friend class Ext2FS;
public:
virtual ~Ext2FSInode() override;
size_t size() const { return m_raw_inode.i_size; }
bool is_symlink() const { return ::is_symlink(m_raw_inode.i_mode); }
// ^Inode (Retainable magic)
virtual void one_retain_left() override;
private:
// ^Inode
virtual ssize_t read_bytes(off_t, ssize_t, byte* buffer, FileDescriptor*) const override;
virtual InodeMetadata metadata() const override;
virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const override;
virtual InodeIdentifier lookup(const String& name) override;
virtual String reverse_lookup(InodeIdentifier) override;
virtual void flush_metadata() override;
virtual ssize_t write_bytes(off_t, ssize_t, const byte* data, FileDescriptor*) override;
virtual KResult add_child(InodeIdentifier child_id, const String& name, byte file_type) override;
virtual KResult remove_child(const String& name) override;
virtual RetainPtr<Inode> parent() const override;
virtual int set_atime(time_t) override;
virtual int set_ctime(time_t) override;
virtual int set_mtime(time_t) override;
virtual int increment_link_count() override;
virtual int decrement_link_count() override;
virtual size_t directory_entry_count() const override;
virtual KResult chmod(mode_t) override;
virtual KResult chown(uid_t, gid_t) override;
virtual KResult truncate(int) override;
void populate_lookup_cache() const;
Ext2FS& fs();
const Ext2FS& fs() const;
Ext2FSInode(Ext2FS&, unsigned index);
mutable Vector<unsigned> m_block_list;
mutable HashMap<String, unsigned> m_lookup_cache;
ext2_inode m_raw_inode;
mutable InodeIdentifier m_parent_id;
};
class Ext2FS final : public DiskBackedFS {
friend class Ext2FSInode;
public:
static Retained <Ext2FS> create(Retained<DiskDevice>&&);
virtual ~Ext2FS() override;
virtual bool initialize() override;
virtual unsigned total_block_count() const override;
virtual unsigned free_block_count() const override;
virtual unsigned total_inode_count() const override;
virtual unsigned free_inode_count() const override;
private:
typedef unsigned BlockIndex;
typedef unsigned GroupIndex;
typedef unsigned InodeIndex;
explicit Ext2FS(Retained<DiskDevice>&&);
const ext2_super_block& super_block() const;
const ext2_group_desc& group_descriptor(unsigned groupIndex) const;
void flush_block_group_descriptor_table();
unsigned first_block_of_group(unsigned groupIndex) const;
unsigned inodes_per_block() const;
unsigned inodes_per_group() const;
unsigned blocks_per_group() const;
unsigned inode_size() const;
bool write_ext2_inode(unsigned, const ext2_inode&);
ByteBuffer read_block_containing_inode(unsigned inode, unsigned& block_index, unsigned& offset) const;
ByteBuffer read_super_block() const;
bool write_super_block(const ext2_super_block&);
virtual const char* class_name() const override;
virtual InodeIdentifier root_inode() const override;
virtual RetainPtr<Inode> create_inode(InodeIdentifier parentInode, const String& name, mode_t, unsigned size, int& error) override;
virtual RetainPtr<Inode> create_directory(InodeIdentifier parentInode, const String& name, mode_t, int& error) override;
virtual RetainPtr<Inode> get_inode(InodeIdentifier) const override;
unsigned allocate_inode(unsigned preferredGroup, unsigned expectedSize);
Vector<BlockIndex> allocate_blocks(unsigned group, unsigned count);
unsigned group_index_from_inode(unsigned) const;
GroupIndex group_index_from_block_index(BlockIndex) const;
Vector<unsigned> block_list_for_inode(const ext2_inode&, bool include_block_list_blocks = false) const;
bool write_block_list_for_inode(InodeIndex, ext2_inode&, const Vector<BlockIndex>&);
void dump_block_bitmap(unsigned groupIndex) const;
void dump_inode_bitmap(unsigned groupIndex) const;
template<typename F> void traverse_inode_bitmap(unsigned groupIndex, F) const;
template<typename F> void traverse_block_bitmap(unsigned groupIndex, F) const;
bool add_inode_to_directory(InodeIndex parent, InodeIndex child, const String& name, byte file_type, int& error);
bool write_directory_inode(unsigned directoryInode, Vector<DirectoryEntry>&&);
bool get_inode_allocation_state(InodeIndex) const;
bool set_inode_allocation_state(unsigned inode, bool);
bool set_block_allocation_state(BlockIndex, bool);
void uncache_inode(InodeIndex);
void free_inode(Ext2FSInode&);
struct BlockListShape {
unsigned direct_blocks { 0 };
unsigned indirect_blocks { 0 };
unsigned doubly_indirect_blocks { 0 };
unsigned triply_indirect_blocks { 0 };
unsigned meta_blocks { 0 };
};
BlockListShape compute_block_list_shape(unsigned blocks);
unsigned m_block_group_count { 0 };
mutable ByteBuffer m_cached_super_block;
mutable ByteBuffer m_cached_group_descriptor_table;
mutable HashMap<BlockIndex, RetainPtr<Ext2FSInode>> m_inode_cache;
};
inline Ext2FS& Ext2FSInode::fs()
{
return static_cast<Ext2FS&>(Inode::fs());
}
inline const Ext2FS& Ext2FSInode::fs() const
{
return static_cast<const Ext2FS&>(Inode::fs());
}

View file

@ -0,0 +1,179 @@
#include <AK/Assertions.h>
#include <AK/HashMap.h>
#include <AK/StringBuilder.h>
#include <LibC/errno_numbers.h>
#include "FileSystem.h"
#include "MemoryManager.h"
#include <Kernel/LocalSocket.h>
static dword s_lastFileSystemID;
static HashMap<dword, FS*>* s_fs_map;
static HashTable<Inode*>* s_inode_set;
static HashMap<dword, FS*>& all_fses()
{
if (!s_fs_map)
s_fs_map = new HashMap<dword, FS*>();
return *s_fs_map;
}
HashTable<Inode*>& all_inodes()
{
if (!s_inode_set)
s_inode_set = new HashTable<Inode*>();
return *s_inode_set;
}
FS::FS()
: m_lock("FS")
, m_fsid(++s_lastFileSystemID)
{
all_fses().set(m_fsid, this);
}
FS::~FS()
{
all_fses().remove(m_fsid);
}
FS* FS::from_fsid(dword id)
{
auto it = all_fses().find(id);
if (it != all_fses().end())
return (*it).value;
return nullptr;
}
ByteBuffer Inode::read_entire(FileDescriptor* descriptor) const
{
size_t initial_size = metadata().size ? metadata().size : 4096;
StringBuilder builder(initial_size);
ssize_t nread;
byte buffer[4096];
off_t offset = 0;
for (;;) {
nread = read_bytes(offset, sizeof(buffer), buffer, descriptor);
ASSERT(nread <= (ssize_t)sizeof(buffer));
if (nread <= 0)
break;
builder.append((const char*)buffer, nread);
offset += nread;
}
if (nread < 0) {
kprintf("Inode::read_entire: ERROR: %d\n", nread);
return nullptr;
}
return builder.to_byte_buffer();
}
FS::DirectoryEntry::DirectoryEntry(const char* n, InodeIdentifier i, byte ft)
: name_length(strlen(n))
, inode(i)
, file_type(ft)
{
memcpy(name, n, name_length);
name[name_length] = '\0';
}
FS::DirectoryEntry::DirectoryEntry(const char* n, size_t nl, InodeIdentifier i, byte ft)
: name_length(nl)
, inode(i)
, file_type(ft)
{
memcpy(name, n, nl);
name[nl] = '\0';
}
Inode::Inode(FS& fs, unsigned index)
: m_lock("Inode")
, m_fs(fs)
, m_index(index)
{
all_inodes().set(this);
}
Inode::~Inode()
{
all_inodes().remove(this);
}
void Inode::will_be_destroyed()
{
if (m_metadata_dirty)
flush_metadata();
}
void Inode::inode_contents_changed(off_t offset, ssize_t size, const byte* data)
{
if (m_vmo)
m_vmo->inode_contents_changed(Badge<Inode>(), offset, size, data);
}
void Inode::inode_size_changed(size_t old_size, size_t new_size)
{
if (m_vmo)
m_vmo->inode_size_changed(Badge<Inode>(), old_size, new_size);
}
int Inode::set_atime(time_t)
{
return -ENOTIMPL;
}
int Inode::set_ctime(time_t)
{
return -ENOTIMPL;
}
int Inode::set_mtime(time_t)
{
return -ENOTIMPL;
}
int Inode::increment_link_count()
{
return -ENOTIMPL;
}
int Inode::decrement_link_count()
{
return -ENOTIMPL;
}
void FS::sync()
{
Vector<Retained<Inode>> inodes;
{
InterruptDisabler disabler;
for (auto* inode : all_inodes()) {
if (inode->is_metadata_dirty())
inodes.append(*inode);
}
}
for (auto& inode : inodes) {
ASSERT(inode->is_metadata_dirty());
inode->flush_metadata();
}
}
void Inode::set_vmo(VMObject& vmo)
{
m_vmo = vmo.make_weak_ptr();
}
bool Inode::bind_socket(LocalSocket& socket)
{
ASSERT(!m_socket);
m_socket = socket;
return true;
}
bool Inode::unbind_socket()
{
ASSERT(m_socket);
m_socket = nullptr;
return true;
}

View file

@ -0,0 +1,174 @@
#pragma once
#include "DiskDevice.h"
#include "InodeIdentifier.h"
#include "InodeMetadata.h"
#include "Limits.h"
#include "UnixTypes.h"
#include <AK/ByteBuffer.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
#include <AK/AKString.h>
#include <AK/Function.h>
#include <AK/kstdio.h>
#include <Kernel/Lock.h>
#include <AK/WeakPtr.h>
#include <Kernel/KResult.h>
static const dword mepoch = 476763780;
class Inode;
class FileDescriptor;
class LocalSocket;
class VMObject;
class FS : public Retainable<FS> {
friend class Inode;
public:
virtual ~FS();
unsigned fsid() const { return m_fsid; }
static FS* from_fsid(dword);
static void sync();
virtual bool initialize() = 0;
virtual const char* class_name() const = 0;
virtual InodeIdentifier root_inode() const = 0;
bool is_readonly() const { return m_readonly; }
virtual unsigned total_block_count() const { return 0; }
virtual unsigned free_block_count() const { return 0; }
virtual unsigned total_inode_count() const { return 0; }
virtual unsigned free_inode_count() const { return 0; }
struct DirectoryEntry {
DirectoryEntry(const char* name, InodeIdentifier, byte file_type);
DirectoryEntry(const char* name, size_t name_length, InodeIdentifier, byte file_type);
char name[256];
int name_length { 0 };
InodeIdentifier inode;
byte file_type { 0 };
};
virtual RetainPtr<Inode> create_inode(InodeIdentifier parentInode, const String& name, mode_t, unsigned size, int& error) = 0;
virtual RetainPtr<Inode> create_directory(InodeIdentifier parentInode, const String& name, mode_t, int& error) = 0;
virtual RetainPtr<Inode> get_inode(InodeIdentifier) const = 0;
protected:
FS();
mutable Lock m_lock;
private:
unsigned m_fsid { 0 };
bool m_readonly { false };
};
class Inode : public Retainable<Inode> {
friend class VFS;
friend class FS;
public:
virtual ~Inode();
virtual void one_retain_left() { }
FS& fs() { return m_fs; }
const FS& fs() const { return m_fs; }
unsigned fsid() const;
unsigned index() const { return m_index; }
size_t size() const { return metadata().size; }
bool is_symlink() const { return metadata().is_symlink(); }
bool is_directory() const { return metadata().is_directory(); }
bool is_character_device() const { return metadata().is_character_device(); }
mode_t mode() const { return metadata().mode; }
InodeIdentifier identifier() const { return { fsid(), index() }; }
virtual InodeMetadata metadata() const = 0;
ByteBuffer read_entire(FileDescriptor* = nullptr) const;
virtual ssize_t read_bytes(off_t, ssize_t, byte* buffer, FileDescriptor*) const = 0;
virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const = 0;
virtual InodeIdentifier lookup(const String& name) = 0;
virtual String reverse_lookup(InodeIdentifier) = 0;
virtual ssize_t write_bytes(off_t, ssize_t, const byte* data, FileDescriptor*) = 0;
virtual KResult add_child(InodeIdentifier child_id, const String& name, byte file_type) = 0;
virtual KResult remove_child(const String& name) = 0;
virtual RetainPtr<Inode> parent() const = 0;
virtual size_t directory_entry_count() const = 0;
virtual KResult chmod(mode_t) = 0;
virtual KResult chown(uid_t, gid_t) = 0;
virtual KResult truncate(int) { return KSuccess; }
LocalSocket* socket() { return m_socket.ptr(); }
const LocalSocket* socket() const { return m_socket.ptr(); }
bool bind_socket(LocalSocket&);
bool unbind_socket();
bool is_metadata_dirty() const { return m_metadata_dirty; }
virtual int set_atime(time_t);
virtual int set_ctime(time_t);
virtual int set_mtime(time_t);
virtual int increment_link_count();
virtual int decrement_link_count();
virtual void flush_metadata() = 0;
void will_be_destroyed();
void set_vmo(VMObject&);
VMObject* vmo() { return m_vmo.ptr(); }
const VMObject* vmo() const { return m_vmo.ptr(); }
protected:
Inode(FS& fs, unsigned index);
void set_metadata_dirty(bool b) { m_metadata_dirty = b; }
void inode_contents_changed(off_t, ssize_t, const byte*);
void inode_size_changed(size_t old_size, size_t new_size);
mutable Lock m_lock;
private:
FS& m_fs;
unsigned m_index { 0 };
WeakPtr<VMObject> m_vmo;
RetainPtr<LocalSocket> m_socket;
bool m_metadata_dirty { false };
};
inline FS* InodeIdentifier::fs()
{
return FS::from_fsid(m_fsid);
}
inline const FS* InodeIdentifier::fs() const
{
return FS::from_fsid(m_fsid);
}
inline bool InodeIdentifier::is_root_inode() const
{
return (*this) == fs()->root_inode();
}
inline unsigned Inode::fsid() const
{
return m_fs.fsid();
}
namespace AK {
template<>
struct Traits<InodeIdentifier> {
static unsigned hash(const InodeIdentifier& inode) { return pair_int_hash(inode.fsid(), inode.index()); }
static void dump(const InodeIdentifier& inode) { kprintf("%02u:%08u", inode.fsid(), inode.index()); }
};
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Types.h>
class FS;
struct InodeMetadata;
class InodeIdentifier {
public:
InodeIdentifier() { }
InodeIdentifier(dword fsid, dword inode)
: m_fsid(fsid)
, m_index(inode)
{
}
bool is_valid() const { return m_fsid != 0 && m_index != 0; }
dword fsid() const { return m_fsid; }
dword index() const { return m_index; }
FS* fs();
const FS* fs() const;
bool operator==(const InodeIdentifier& other) const
{
return m_fsid == other.m_fsid && m_index == other.m_index;
}
bool operator!=(const InodeIdentifier& other) const
{
return m_fsid != other.m_fsid || m_index != other.m_index;
}
bool is_root_inode() const;
private:
dword m_fsid { 0 };
dword m_index { 0 };
};

View file

@ -0,0 +1,88 @@
#pragma once
#include "InodeIdentifier.h"
#include "UnixTypes.h"
#include <AK/HashTable.h>
class Process;
inline bool is_directory(mode_t mode) { return (mode & 0170000) == 0040000; }
inline bool is_character_device(mode_t mode) { return (mode & 0170000) == 0020000; }
inline bool is_block_device(mode_t mode) { return (mode & 0170000) == 0060000; }
inline bool is_regular_file(mode_t mode) { return (mode & 0170000) == 0100000; }
inline bool is_fifo(mode_t mode) { return (mode & 0170000) == 0010000; }
inline bool is_symlink(mode_t mode) { return (mode & 0170000) == 0120000; }
inline bool is_socket(mode_t mode) { return (mode & 0170000) == 0140000; }
inline bool is_sticky(mode_t mode) { return mode & 01000; }
inline bool is_setuid(mode_t mode) { return mode & 04000; }
inline bool is_setgid(mode_t mode) { return mode & 02000; }
struct InodeMetadata {
bool is_valid() const { return inode.is_valid(); }
bool may_read(Process&) const;
bool may_write(Process&) const;
bool may_execute(Process&) const;
bool may_read(uid_t u, const HashTable<gid_t>& g) const
{
if (u == 0)
return true;
if (uid == u)
return mode & 0400;
if (g.contains(gid))
return mode & 0040;
return mode & 0004;
}
bool may_write(uid_t u, const HashTable<gid_t>& g) const
{
if (u == 0)
return true;
if (uid == u)
return mode & 0200;
if (g.contains(gid))
return mode & 0020;
return mode & 0002;
}
bool may_execute(uid_t u, const HashTable<gid_t>& g) const
{
if (u == 0)
return true;
if (uid == u)
return mode & 0100;
if (g.contains(gid))
return mode & 0010;
return mode & 0001;
}
bool is_directory() const { return ::is_directory(mode); }
bool is_character_device() const { return ::is_character_device(mode); }
bool is_block_device() const { return ::is_block_device(mode); }
bool is_device() const { return is_character_device() || is_block_device(); }
bool is_regular_file() const { return ::is_regular_file(mode); }
bool is_fifo() const { return ::is_fifo(mode); }
bool is_symlink() const { return ::is_symlink(mode); }
bool is_socket() const { return ::is_socket(mode); }
bool is_sticky() const { return ::is_sticky(mode); }
bool is_setuid() const { return ::is_setuid(mode); }
bool is_setgid() const { return ::is_setgid(mode); }
InodeIdentifier inode;
off_t size { 0 };
mode_t mode { 0 };
uid_t uid { 0 };
gid_t gid { 0 };
nlink_t link_count { 0 };
time_t atime { 0 };
time_t ctime { 0 };
time_t mtime { 0 };
time_t dtime { 0 };
blkcnt_t block_count { 0 };
blksize_t block_size { 0 };
unsigned major_device { 0 };
unsigned minor_device { 0 };
};

1177
Kernel/FileSystem/ProcFS.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,98 @@
#pragma once
#include <Kernel/Lock.h>
#include <AK/Types.h>
#include <Kernel/FileSystem/FileSystem.h>
class Process;
class ProcFSInode;
class ProcFS final : public FS {
friend class ProcFSInode;
public:
[[gnu::pure]] static ProcFS& the();
virtual ~ProcFS() override;
static Retained<ProcFS> create();
virtual bool initialize() override;
virtual const char* class_name() const override;
virtual InodeIdentifier root_inode() const override;
virtual RetainPtr<Inode> get_inode(InodeIdentifier) const override;
virtual RetainPtr<Inode> create_inode(InodeIdentifier parent_id, const String& name, mode_t, unsigned size, int& error) override;
virtual RetainPtr<Inode> create_directory(InodeIdentifier parent_id, const String& name, mode_t, int& error) override;
void add_sys_file(String&&, Function<ByteBuffer(ProcFSInode&)>&& read_callback, Function<ssize_t(ProcFSInode&, const ByteBuffer&)>&& write_callback);
void add_sys_bool(String&&, Lockable<bool>&, Function<void()>&& notify_callback = nullptr);
void add_sys_string(String&&, Lockable<String>&, Function<void()>&& notify_callback = nullptr);
private:
ProcFS();
struct ProcFSDirectoryEntry {
ProcFSDirectoryEntry() { }
ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, Function<ByteBuffer(InodeIdentifier)>&& a_read_callback = nullptr, Function<ssize_t(InodeIdentifier, const ByteBuffer&)>&& a_write_callback = nullptr, RetainPtr<ProcFSInode>&& a_inode = nullptr)
: name(a_name)
, proc_file_type(a_proc_file_type)
, read_callback(move(a_read_callback))
, write_callback(move(a_write_callback))
, inode(move(a_inode))
{
}
const char* name { nullptr };
unsigned proc_file_type { 0 };
Function<ByteBuffer(InodeIdentifier)> read_callback;
Function<ssize_t(InodeIdentifier, const ByteBuffer&)> write_callback;
RetainPtr<ProcFSInode> inode;
InodeIdentifier identifier(unsigned fsid) const;
};
ProcFSDirectoryEntry* get_directory_entry(InodeIdentifier) const;
Vector<ProcFSDirectoryEntry> m_entries;
Vector<ProcFSDirectoryEntry> m_sys_entries;
mutable Lock m_inodes_lock;
mutable HashMap<unsigned, ProcFSInode*> m_inodes;
RetainPtr<ProcFSInode> m_root_inode;
};
struct ProcFSInodeCustomData {
virtual ~ProcFSInodeCustomData();
};
class ProcFSInode final : public Inode {
friend class ProcFS;
public:
virtual ~ProcFSInode() override;
void set_custom_data(OwnPtr<ProcFSInodeCustomData>&& custom_data) { m_custom_data = move(custom_data); }
ProcFSInodeCustomData* custom_data() { return m_custom_data.ptr(); }
const ProcFSInodeCustomData* custom_data() const { return m_custom_data.ptr(); }
private:
// ^Inode
virtual ssize_t read_bytes(off_t, ssize_t, byte* buffer, FileDescriptor*) const override;
virtual InodeMetadata metadata() const override;
virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const override;
virtual InodeIdentifier lookup(const String& name) override;
virtual String reverse_lookup(InodeIdentifier) override;
virtual void flush_metadata() override;
virtual ssize_t write_bytes(off_t, ssize_t, const byte* buffer, FileDescriptor*) override;
virtual KResult add_child(InodeIdentifier child_id, const String& name, byte file_type) override;
virtual KResult remove_child(const String& name) override;
virtual RetainPtr<Inode> parent() const override;
virtual size_t directory_entry_count() const override;
virtual KResult chmod(mode_t) override;
virtual KResult chown(uid_t, gid_t) override;
ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); }
ProcFSInode(ProcFS&, unsigned index);
OwnPtr<ProcFSInodeCustomData> m_custom_data;
};

View file

@ -0,0 +1,316 @@
#include <Kernel/FileSystem/SyntheticFileSystem.h>
#include <Kernel/FileDescriptor.h>
#include <LibC/errno_numbers.h>
#include <AK/StdLibExtras.h>
//#define SYNTHFS_DEBUG
Retained<SynthFS> SynthFS::create()
{
return adopt(*new SynthFS);
}
SynthFS::SynthFS()
{
}
SynthFS::~SynthFS()
{
}
bool SynthFS::initialize()
{
// Add a File for the root directory.
// FIXME: This needs work.
auto root = adopt(*new SynthFSInode(*this, RootInodeIndex));
root->m_parent = { fsid(), RootInodeIndex };
root->m_metadata.mode = 0040555;
root->m_metadata.uid = 0;
root->m_metadata.gid = 0;
root->m_metadata.size = 0;
root->m_metadata.mtime = mepoch;
m_inodes.set(RootInodeIndex, move(root));
return true;
}
Retained<SynthFSInode> SynthFS::create_directory(String&& name)
{
auto file = adopt(*new SynthFSInode(*this, generate_inode_index()));
file->m_name = move(name);
file->m_metadata.size = 0;
file->m_metadata.uid = 0;
file->m_metadata.gid = 0;
file->m_metadata.mode = 0040555;
file->m_metadata.mtime = mepoch;
return file;
}
Retained<SynthFSInode> SynthFS::create_text_file(String&& name, ByteBuffer&& contents, mode_t mode)
{
auto file = adopt(*new SynthFSInode(*this, generate_inode_index()));
file->m_data = contents;
file->m_name = move(name);
file->m_metadata.size = file->m_data.size();
file->m_metadata.uid = 100;
file->m_metadata.gid = 200;
file->m_metadata.mode = mode;
file->m_metadata.mtime = mepoch;
return file;
}
Retained<SynthFSInode> SynthFS::create_generated_file(String&& name, Function<ByteBuffer(SynthFSInode&)>&& generator, mode_t mode)
{
auto file = adopt(*new SynthFSInode(*this, generate_inode_index()));
file->m_generator = move(generator);
file->m_name = move(name);
file->m_metadata.size = 0;
file->m_metadata.uid = 0;
file->m_metadata.gid = 0;
file->m_metadata.mode = mode;
file->m_metadata.mtime = mepoch;
return file;
}
Retained<SynthFSInode> SynthFS::create_generated_file(String&& name, Function<ByteBuffer(SynthFSInode&)>&& read_callback, Function<ssize_t(SynthFSInode&, const ByteBuffer&)>&& write_callback, mode_t mode)
{
auto file = adopt(*new SynthFSInode(*this, generate_inode_index()));
file->m_generator = move(read_callback);
file->m_write_callback = move(write_callback);
file->m_name = move(name);
file->m_metadata.size = 0;
file->m_metadata.uid = 0;
file->m_metadata.gid = 0;
file->m_metadata.mode = mode;
file->m_metadata.mtime = mepoch;
return file;
}
InodeIdentifier SynthFS::add_file(RetainPtr<SynthFSInode>&& file, InodeIndex parent)
{
LOCKER(m_lock);
ASSERT(file);
auto it = m_inodes.find(parent);
ASSERT(it != m_inodes.end());
auto new_inode_id = file->identifier();
file->m_metadata.inode = new_inode_id;
file->m_parent = { fsid(), parent };
(*it).value->m_children.append(file.ptr());
m_inodes.set(new_inode_id.index(), move(file));
return new_inode_id;
}
bool SynthFS::remove_file(InodeIndex inode)
{
LOCKER(m_lock);
auto it = m_inodes.find(inode);
if (it == m_inodes.end())
return false;
auto& file = *(*it).value;
auto pit = m_inodes.find(file.m_parent.index());
if (pit == m_inodes.end())
return false;
auto& parent = *(*pit).value;
for (ssize_t i = 0; i < parent.m_children.size(); ++i) {
if (parent.m_children[i]->m_metadata.inode.index() != inode)
continue;
parent.m_children.remove(i);
break;
}
Vector<InodeIndex> indices_to_remove;
indices_to_remove.ensure_capacity(file.m_children.size());
for (auto& child : file.m_children)
indices_to_remove.unchecked_append(child->m_metadata.inode.index());
for (auto& index : indices_to_remove)
remove_file(index);
m_inodes.remove(inode);
return true;
}
const char* SynthFS::class_name() const
{
return "synthfs";
}
InodeIdentifier SynthFS::root_inode() const
{
return { fsid(), 1 };
}
RetainPtr<Inode> SynthFS::create_inode(InodeIdentifier parentInode, const String& name, mode_t mode, unsigned size, int& error)
{
(void) parentInode;
(void) name;
(void) mode;
(void) size;
(void) error;
kprintf("FIXME: Implement SyntheticFileSystem::create_inode().\n");
return { };
}
RetainPtr<Inode> SynthFS::create_directory(InodeIdentifier, const String&, mode_t, int& error)
{
error = -EROFS;
return nullptr;
}
auto SynthFS::generate_inode_index() -> InodeIndex
{
LOCKER(m_lock);
return m_next_inode_index++;
}
RetainPtr<Inode> SynthFSInode::parent() const
{
LOCKER(m_lock);
return fs().get_inode(m_parent);
}
RetainPtr<Inode> SynthFS::get_inode(InodeIdentifier inode) const
{
LOCKER(m_lock);
auto it = m_inodes.find(inode.index());
if (it == m_inodes.end())
return { };
return (*it).value;
}
SynthFSInode::SynthFSInode(SynthFS& fs, unsigned index)
: Inode(fs, index)
{
m_metadata.inode = { fs.fsid(), index };
}
SynthFSInode::~SynthFSInode()
{
}
InodeMetadata SynthFSInode::metadata() const
{
return m_metadata;
}
ssize_t SynthFSInode::read_bytes(off_t offset, ssize_t count, byte* buffer, FileDescriptor* descriptor) const
{
LOCKER(m_lock);
#ifdef SYNTHFS_DEBUG
kprintf("SynthFS: read_bytes %u\n", index());
#endif
ASSERT(offset >= 0);
ASSERT(buffer);
ByteBuffer generated_data;
if (m_generator) {
if (!descriptor) {
generated_data = m_generator(const_cast<SynthFSInode&>(*this));
} else {
if (!descriptor->generator_cache())
descriptor->generator_cache() = m_generator(const_cast<SynthFSInode&>(*this));
generated_data = descriptor->generator_cache();
}
}
auto* data = generated_data ? &generated_data : &m_data;
ssize_t nread = min(static_cast<off_t>(data->size() - offset), static_cast<off_t>(count));
memcpy(buffer, data->pointer() + offset, nread);
if (nread == 0 && descriptor && descriptor->generator_cache())
descriptor->generator_cache().clear();
return nread;
}
bool SynthFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) const
{
LOCKER(m_lock);
#ifdef SYNTHFS_DEBUG
kprintf("SynthFS: traverse_as_directory %u\n", index());
#endif
if (!m_metadata.is_directory())
return false;
callback({ ".", 1, m_metadata.inode, 2 });
callback({ "..", 2, m_parent, 2 });
for (auto& child : m_children)
callback({ child->m_name.characters(), child->m_name.length(), child->m_metadata.inode, child->m_metadata.is_directory() ? (byte)2 : (byte)1 });
return true;
}
InodeIdentifier SynthFSInode::lookup(const String& name)
{
LOCKER(m_lock);
ASSERT(is_directory());
if (name == ".")
return identifier();
if (name == "..")
return m_parent;
for (auto& child : m_children) {
if (child->m_name == name)
return child->identifier();
}
return { };
}
String SynthFSInode::reverse_lookup(InodeIdentifier child_id)
{
LOCKER(m_lock);
ASSERT(is_directory());
for (auto& child : m_children) {
if (child->identifier() == child_id)
return child->m_name;
}
return { };
}
void SynthFSInode::flush_metadata()
{
}
ssize_t SynthFSInode::write_bytes(off_t offset, ssize_t size, const byte* buffer, FileDescriptor*)
{
LOCKER(m_lock);
if (!m_write_callback)
return -EPERM;
// FIXME: Being able to write into SynthFS at a non-zero offset seems like something we should support..
ASSERT(offset == 0);
bool success = m_write_callback(*this, ByteBuffer::wrap((byte*)buffer, size));
ASSERT(success);
return 0;
}
KResult SynthFSInode::add_child(InodeIdentifier child_id, const String& name, byte file_type)
{
(void)child_id;
(void)name;
(void)file_type;
ASSERT_NOT_REACHED();
}
KResult SynthFSInode::remove_child(const String& name)
{
(void)name;
ASSERT_NOT_REACHED();
}
SynthFSInodeCustomData::~SynthFSInodeCustomData()
{
}
size_t SynthFSInode::directory_entry_count() const
{
LOCKER(m_lock);
ASSERT(is_directory());
// NOTE: The 2 is for '.' and '..'
return m_children.size() + 2;
}
KResult SynthFSInode::chmod(mode_t)
{
return KResult(-EPERM);
}
KResult SynthFSInode::chown(uid_t, gid_t)
{
return KResult(-EPERM);
}

View file

@ -0,0 +1,94 @@
#pragma once
#include "FileSystem.h"
#include "UnixTypes.h"
#include <AK/HashMap.h>
class SynthFSInode;
class SynthFS : public FS {
public:
virtual ~SynthFS() override;
static Retained<SynthFS> create();
virtual bool initialize() override;
virtual const char* class_name() const override;
virtual InodeIdentifier root_inode() const override;
virtual RetainPtr<Inode> create_inode(InodeIdentifier parentInode, const String& name, mode_t, unsigned size, int& error) override;
virtual RetainPtr<Inode> create_directory(InodeIdentifier parentInode, const String& name, mode_t, int& error) override;
virtual RetainPtr<Inode> get_inode(InodeIdentifier) const override;
protected:
typedef unsigned InodeIndex;
InodeIndex generate_inode_index();
static constexpr InodeIndex RootInodeIndex = 1;
SynthFS();
Retained<SynthFSInode> create_directory(String&& name);
Retained<SynthFSInode> create_text_file(String&& name, ByteBuffer&&, mode_t = 0010644);
Retained<SynthFSInode> create_generated_file(String&& name, Function<ByteBuffer(SynthFSInode&)>&&, mode_t = 0100644);
Retained<SynthFSInode> create_generated_file(String&& name, Function<ByteBuffer(SynthFSInode&)>&&, Function<ssize_t(SynthFSInode&, const ByteBuffer&)>&&, mode_t = 0100644);
InodeIdentifier add_file(RetainPtr<SynthFSInode>&&, InodeIndex parent = RootInodeIndex);
bool remove_file(InodeIndex);
private:
InodeIndex m_next_inode_index { 2 };
HashMap<InodeIndex, RetainPtr<SynthFSInode>> m_inodes;
};
struct SynthFSInodeCustomData {
virtual ~SynthFSInodeCustomData();
};
class SynthFSInode final : public Inode {
friend class SynthFS;
friend class DevPtsFS;
public:
virtual ~SynthFSInode() override;
void set_custom_data(OwnPtr<SynthFSInodeCustomData>&& custom_data) { m_custom_data = move(custom_data); }
SynthFSInodeCustomData* custom_data() { return m_custom_data.ptr(); }
const SynthFSInodeCustomData* custom_data() const { return m_custom_data.ptr(); }
private:
// ^Inode
virtual ssize_t read_bytes(off_t, ssize_t, byte* buffer, FileDescriptor*) const override;
virtual InodeMetadata metadata() const override;
virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const override;
virtual InodeIdentifier lookup(const String& name) override;
virtual String reverse_lookup(InodeIdentifier) override;
virtual void flush_metadata() override;
virtual ssize_t write_bytes(off_t, ssize_t, const byte* buffer, FileDescriptor*) override;
virtual KResult add_child(InodeIdentifier child_id, const String& name, byte file_type) override;
virtual KResult remove_child(const String& name) override;
virtual RetainPtr<Inode> parent() const override;
virtual size_t directory_entry_count() const override;
virtual KResult chmod(mode_t) override;
virtual KResult chown(uid_t, gid_t) override;
SynthFS& fs();
const SynthFS& fs() const;
SynthFSInode(SynthFS&, unsigned index);
String m_name;
InodeIdentifier m_parent;
ByteBuffer m_data;
Function<ByteBuffer(SynthFSInode&)> m_generator;
Function<ssize_t(SynthFSInode&, const ByteBuffer&)> m_write_callback;
Vector<SynthFSInode*> m_children;
InodeMetadata m_metadata;
OwnPtr<SynthFSInodeCustomData> m_custom_data;
};
inline SynthFS& SynthFSInode::fs()
{
return static_cast<SynthFS&>(Inode::fs());
}
inline const SynthFS& SynthFSInode::fs() const
{
return static_cast<const SynthFS&>(Inode::fs());
}

View file

@ -0,0 +1,665 @@
#include "VirtualFileSystem.h"
#include "FileDescriptor.h"
#include "FileSystem.h"
#include <AK/FileSystemPath.h>
#include <AK/StringBuilder.h>
#include "CharacterDevice.h"
#include <LibC/errno_numbers.h>
#include <Kernel/Process.h>
//#define VFS_DEBUG
static VFS* s_the;
VFS& VFS::the()
{
ASSERT(s_the);
return *s_the;
}
VFS::VFS()
{
#ifdef VFS_DEBUG
kprintf("VFS: Constructing VFS\n");
#endif
s_the = this;
}
VFS::~VFS()
{
}
InodeIdentifier VFS::root_inode_id() const
{
ASSERT(m_root_inode);
return m_root_inode->identifier();
}
bool VFS::mount(RetainPtr<FS>&& file_system, const String& path)
{
ASSERT(file_system);
int error;
auto inode = old_resolve_path(path, root_inode_id(), error);
if (!inode.is_valid()) {
kprintf("VFS: mount can't resolve mount point '%s'\n", path.characters());
return false;
}
kprintf("VFS: mounting %s{%p} at %s (inode: %u)\n", file_system->class_name(), file_system.ptr(), path.characters(), inode.index());
// FIXME: check that this is not already a mount point
auto mount = make<Mount>(inode, move(file_system));
m_mounts.append(move(mount));
return true;
}
bool VFS::mount_root(RetainPtr<FS>&& file_system)
{
if (m_root_inode) {
kprintf("VFS: mount_root can't mount another root\n");
return false;
}
auto mount = make<Mount>(InodeIdentifier(), move(file_system));
auto root_inode_id = mount->guest().fs()->root_inode();
auto root_inode = mount->guest().fs()->get_inode(root_inode_id);
if (!root_inode->is_directory()) {
kprintf("VFS: root inode (%02u:%08u) for / is not a directory :(\n", root_inode_id.fsid(), root_inode_id.index());
return false;
}
m_root_inode = move(root_inode);
kprintf("VFS: mounted root on %s{%p}\n",
m_root_inode->fs().class_name(),
&m_root_inode->fs());
m_mounts.append(move(mount));
return true;
}
auto VFS::find_mount_for_host(InodeIdentifier inode) -> Mount*
{
for (auto& mount : m_mounts) {
if (mount->host() == inode)
return mount.ptr();
}
return nullptr;
}
auto VFS::find_mount_for_guest(InodeIdentifier inode) -> Mount*
{
for (auto& mount : m_mounts) {
if (mount->guest() == inode)
return mount.ptr();
}
return nullptr;
}
bool VFS::is_vfs_root(InodeIdentifier inode) const
{
return inode == root_inode_id();
}
void VFS::traverse_directory_inode(Inode& dir_inode, Function<bool(const FS::DirectoryEntry&)> callback)
{
dir_inode.traverse_as_directory([&] (const FS::DirectoryEntry& entry) {
InodeIdentifier resolved_inode;
if (auto mount = find_mount_for_host(entry.inode))
resolved_inode = mount->guest();
else
resolved_inode = entry.inode;
if (dir_inode.identifier().is_root_inode() && !is_vfs_root(dir_inode.identifier()) && !strcmp(entry.name, "..")) {
auto mount = find_mount_for_guest(entry.inode);
ASSERT(mount);
resolved_inode = mount->host();
}
callback(FS::DirectoryEntry(entry.name, entry.name_length, resolved_inode, entry.file_type));
return true;
});
}
KResultOr<Retained<FileDescriptor>> VFS::open(RetainPtr<Device>&& device, int options)
{
// FIXME: Respect options.
(void)options;
return FileDescriptor::create(move(device));
}
KResult VFS::utime(const String& path, Inode& base, time_t atime, time_t mtime)
{
auto descriptor_or_error = VFS::the().open(move(path), 0, 0, base);
if (descriptor_or_error.is_error())
return descriptor_or_error.error();
auto& inode = *descriptor_or_error.value()->inode();
if (inode.fs().is_readonly())
return KResult(-EROFS);
if (inode.metadata().uid != current->process().euid())
return KResult(-EACCES);
int error = inode.set_atime(atime);
if (error)
return KResult(error);
error = inode.set_mtime(mtime);
if (error)
return KResult(error);
return KSuccess;
}
KResult VFS::stat(const String& path, int options, Inode& base, struct stat& statbuf)
{
auto inode_or_error = resolve_path_to_inode(path, base, nullptr, options);
if (inode_or_error.is_error())
return inode_or_error.error();
return FileDescriptor::create(inode_or_error.value().ptr())->fstat(statbuf);
}
KResultOr<Retained<FileDescriptor>> VFS::open(const String& path, int options, mode_t mode, Inode& base)
{
auto inode_or_error = resolve_path_to_inode(path, base, nullptr, options);
if (options & O_CREAT) {
if (inode_or_error.is_error())
return create(path, options, mode, base);
if (options & O_EXCL)
return KResult(-EEXIST);
}
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
auto metadata = inode->metadata();
bool should_truncate_file = false;
// NOTE: Read permission is a bit weird, since O_RDONLY == 0,
// so we check if (NOT write_only OR read_and_write)
if (!(options & O_WRONLY) || (options & O_RDWR)) {
if (!metadata.may_read(current->process()))
return KResult(-EACCES);
}
if ((options & O_WRONLY) || (options & O_RDWR)) {
if (!metadata.may_write(current->process()))
return KResult(-EACCES);
if (metadata.is_directory())
return KResult(-EISDIR);
should_truncate_file = options & O_TRUNC;
}
if (metadata.is_device()) {
auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device));
if (it == m_devices.end()) {
return KResult(-ENODEV);
}
auto descriptor_or_error = (*it).value->open(options);
if (descriptor_or_error.is_error())
return descriptor_or_error.error();
descriptor_or_error.value()->set_original_inode(Badge<VFS>(), *inode);
return descriptor_or_error;
}
if (should_truncate_file)
inode->truncate(0);
return FileDescriptor::create(*inode);
}
KResultOr<Retained<FileDescriptor>> VFS::create(const String& path, int options, mode_t mode, Inode& base)
{
(void)options;
if (!is_socket(mode) && !is_fifo(mode) && !is_block_device(mode) && !is_character_device(mode)) {
// Turn it into a regular file. (This feels rather hackish.)
mode |= 0100000;
}
RetainPtr<Inode> parent_inode;
auto existing_file_or_error = resolve_path_to_inode(path, base, &parent_inode);
if (!existing_file_or_error.is_error())
return KResult(-EEXIST);
if (!parent_inode)
return KResult(-ENOENT);
if (existing_file_or_error.error() != -ENOENT)
return existing_file_or_error.error();
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
FileSystemPath p(path);
dbgprintf("VFS::create_file: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index());
int error;
auto new_file = parent_inode->fs().create_inode(parent_inode->identifier(), p.basename(), mode, 0, error);
if (!new_file)
return KResult(error);
return FileDescriptor::create(move(new_file));
}
KResult VFS::mkdir(const String& path, mode_t mode, Inode& base)
{
RetainPtr<Inode> parent_inode;
auto result = resolve_path_to_inode(path, base, &parent_inode);
if (!result.is_error())
return KResult(-EEXIST);
if (!parent_inode)
return KResult(-ENOENT);
if (result.error() != -ENOENT)
return result.error();
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
FileSystemPath p(path);
dbgprintf("VFS::mkdir: '%s' in %u:%u\n", p.basename().characters(), parent_inode->fsid(), parent_inode->index());
int error;
auto new_dir = parent_inode->fs().create_directory(parent_inode->identifier(), p.basename(), mode, error);
if (new_dir)
return KSuccess;
return KResult(error);
}
KResult VFS::access(const String& path, int mode, Inode& base)
{
auto inode_or_error = resolve_path_to_inode(path, base);
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
auto metadata = inode->metadata();
if (mode & R_OK) {
if (!metadata.may_read(current->process()))
return KResult(-EACCES);
}
if (mode & W_OK) {
if (!metadata.may_write(current->process()))
return KResult(-EACCES);
}
if (mode & X_OK) {
if (!metadata.may_execute(current->process()))
return KResult(-EACCES);
}
return KSuccess;
}
KResultOr<Retained<Inode>> VFS::open_directory(const String& path, Inode& base)
{
auto inode_or_error = resolve_path_to_inode(path, base);
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
if (!inode->is_directory())
return KResult(-ENOTDIR);
if (!inode->metadata().may_execute(current->process()))
return KResult(-EACCES);
return Retained<Inode>(*inode);
}
KResult VFS::chmod(Inode& inode, mode_t mode)
{
if (inode.fs().is_readonly())
return KResult(-EROFS);
if (current->process().euid() != inode.metadata().uid && !current->process().is_superuser())
return KResult(-EPERM);
// Only change the permission bits.
mode = (inode.mode() & ~04777u) | (mode & 04777u);
return inode.chmod(mode);
}
KResult VFS::chmod(const String& path, mode_t mode, Inode& base)
{
auto inode_or_error = resolve_path_to_inode(path, base);
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
return chmod(*inode, mode);
}
KResult VFS::chown(const String& path, uid_t a_uid, gid_t a_gid, Inode& base)
{
auto inode_or_error = resolve_path_to_inode(path, base);
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
if (inode->fs().is_readonly())
return KResult(-EROFS);
if (current->process().euid() != inode->metadata().uid && !current->process().is_superuser())
return KResult(-EPERM);
uid_t new_uid = inode->metadata().uid;
gid_t new_gid = inode->metadata().gid;
if (a_uid != (uid_t)-1) {
if (current->process().euid() != a_uid && !current->process().is_superuser())
return KResult(-EPERM);
new_uid = a_uid;
}
if (a_gid != (gid_t)-1) {
if (!current->process().in_group(a_gid) && !current->process().is_superuser())
return KResult(-EPERM);
new_gid = a_gid;
}
dbgprintf("VFS::chown(): inode %u:%u <- uid:%d, gid:%d\n", inode->fsid(), inode->index(), new_uid, new_gid);
return inode->chown(new_uid, new_gid);
}
KResultOr<Retained<Inode>> VFS::resolve_path_to_inode(const String& path, Inode& base, RetainPtr<Inode>* parent_inode, int options)
{
// FIXME: This won't work nicely across mount boundaries.
FileSystemPath p(path);
if (!p.is_valid())
return KResult(-EINVAL);
InodeIdentifier parent_id;
auto result = resolve_path(path, base.identifier(), options, &parent_id);
if (parent_inode && parent_id.is_valid())
*parent_inode = get_inode(parent_id);
if (result.is_error())
return result.error();
return Retained<Inode>(*get_inode(result.value()));
}
KResult VFS::link(const String& old_path, const String& new_path, Inode& base)
{
auto old_inode_or_error = resolve_path_to_inode(old_path, base);
if (old_inode_or_error.is_error())
return old_inode_or_error.error();
auto old_inode = old_inode_or_error.value();
RetainPtr<Inode> parent_inode;
auto new_inode_or_error = resolve_path_to_inode(new_path, base, &parent_inode);
if (!new_inode_or_error.is_error())
return KResult(-EEXIST);
if (!parent_inode)
return KResult(-ENOENT);
if (parent_inode->fsid() != old_inode->fsid())
return KResult(-EXDEV);
if (parent_inode->fs().is_readonly())
return KResult(-EROFS);
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
return parent_inode->add_child(old_inode->identifier(), FileSystemPath(new_path).basename(), 0);
}
KResult VFS::unlink(const String& path, Inode& base)
{
RetainPtr<Inode> parent_inode;
auto inode_or_error = resolve_path_to_inode(path, base, &parent_inode);
if (inode_or_error.is_error())
return inode_or_error.error();
auto inode = inode_or_error.value();
if (inode->is_directory())
return KResult(-EISDIR);
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
return parent_inode->remove_child(FileSystemPath(path).basename());
}
KResult VFS::symlink(const String& target, const String& linkpath, Inode& base)
{
RetainPtr<Inode> parent_inode;
auto existing_file_or_error = resolve_path_to_inode(linkpath, base, &parent_inode);
if (!existing_file_or_error.is_error())
return KResult(-EEXIST);
if (!parent_inode)
return KResult(-ENOENT);
if (existing_file_or_error.error() != -ENOENT)
return existing_file_or_error.error();
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
FileSystemPath p(linkpath);
dbgprintf("VFS::symlink: '%s' (-> '%s') in %u:%u\n", p.basename().characters(), target.characters(), parent_inode->fsid(), parent_inode->index());
int error;
auto new_file = parent_inode->fs().create_inode(parent_inode->identifier(), p.basename(), 0120644, 0, error);
if (!new_file)
return KResult(error);
ssize_t nwritten = new_file->write_bytes(0, target.length(), (const byte*)target.characters(), nullptr);
if (nwritten < 0)
return KResult(nwritten);
return KSuccess;
}
KResult VFS::rmdir(const String& path, Inode& base)
{
RetainPtr<Inode> parent_inode;
auto inode_or_error = resolve_path_to_inode(path, base, &parent_inode);
if (inode_or_error.is_error())
return KResult(inode_or_error.error());
auto inode = inode_or_error.value();
if (inode->fs().is_readonly())
return KResult(-EROFS);
// FIXME: We should return EINVAL if the last component of the path is "."
// FIXME: We should return ENOTEMPTY if the last component of the path is ".."
if (!inode->is_directory())
return KResult(-ENOTDIR);
if (!parent_inode->metadata().may_write(current->process()))
return KResult(-EACCES);
if (inode->directory_entry_count() != 2)
return KResult(-ENOTEMPTY);
auto result = inode->remove_child(".");
if (result.is_error())
return result;
result = inode->remove_child("..");
if (result.is_error())
return result;
// FIXME: The reverse_lookup here can definitely be avoided.
return parent_inode->remove_child(parent_inode->reverse_lookup(inode->identifier()));
}
KResultOr<InodeIdentifier> VFS::resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode)
{
auto symlink_contents = symlink_inode.read_entire();
if (!symlink_contents)
return KResult(-ENOENT);
auto linkee = String((const char*)symlink_contents.pointer(), symlink_contents.size());
#ifdef VFS_DEBUG
kprintf("linkee (%s)(%u) from %u:%u\n", linkee.characters(), linkee.length(), base.fsid(), base.index());
#endif
return resolve_path(linkee, base);
}
RetainPtr<Inode> VFS::get_inode(InodeIdentifier inode_id)
{
if (!inode_id.is_valid())
return nullptr;
return inode_id.fs()->get_inode(inode_id);
}
KResultOr<String> VFS::absolute_path(InodeIdentifier inode_id)
{
auto inode = get_inode(inode_id);
if (!inode)
return KResult(-EIO);
return absolute_path(*inode);
}
KResultOr<String> VFS::absolute_path(Inode& core_inode)
{
Vector<InodeIdentifier> lineage;
RetainPtr<Inode> inode = &core_inode;
while (inode->identifier() != root_inode_id()) {
if (auto* mount = find_mount_for_guest(inode->identifier()))
lineage.append(mount->host());
else
lineage.append(inode->identifier());
InodeIdentifier parent_id;
if (inode->is_directory()) {
auto result = resolve_path("..", inode->identifier());
if (result.is_error())
return result.error();
parent_id = result.value();
} else {
parent_id = inode->parent()->identifier();
}
if (!parent_id.is_valid())
return KResult(-EIO);
inode = get_inode(parent_id);
}
if (lineage.is_empty())
return "/";
lineage.append(root_inode_id());
StringBuilder builder;
for (size_t i = lineage.size() - 1; i >= 1; --i) {
auto& child = lineage[i - 1];
auto parent = lineage[i];
if (auto* mount = find_mount_for_host(parent))
parent = mount->guest();
builder.append('/');
auto parent_inode = get_inode(parent);
builder.append(parent_inode->reverse_lookup(child));
}
return builder.to_string();
}
KResultOr<InodeIdentifier> VFS::resolve_path(const String& path, InodeIdentifier base, int options, InodeIdentifier* parent_id)
{
if (path.is_empty())
return KResult(-EINVAL);
auto parts = path.split('/');
InodeIdentifier crumb_id;
if (path[0] == '/')
crumb_id = root_inode_id();
else
crumb_id = base.is_valid() ? base : root_inode_id();
if (parent_id)
*parent_id = crumb_id;
for (int i = 0; i < parts.size(); ++i) {
bool inode_was_root_at_head_of_loop = crumb_id.is_root_inode();
auto& part = parts[i];
if (part.is_empty())
break;
auto crumb_inode = get_inode(crumb_id);
if (!crumb_inode) {
#ifdef VFS_DEBUG
kprintf("invalid metadata\n");
#endif
return KResult(-EIO);
}
auto metadata = crumb_inode->metadata();
if (!metadata.is_directory()) {
#ifdef VFS_DEBUG
kprintf("parent of <%s> not directory, it's inode %u:%u / %u:%u, mode: %u, size: %u\n", part.characters(), crumb_id.fsid(), crumb_id.index(), metadata.inode.fsid(), metadata.inode.index(), metadata.mode, metadata.size);
#endif
return KResult(-ENOTDIR);
}
if (!metadata.may_execute(current->process()))
return KResult(-EACCES);
auto parent = crumb_id;
crumb_id = crumb_inode->lookup(part);
if (!crumb_id.is_valid()) {
#ifdef VFS_DEBUG
kprintf("child <%s>(%u) not found in directory, %02u:%08u\n", part.characters(), part.length(), parent.fsid(), parent.index());
#endif
return KResult(-ENOENT);
}
#ifdef VFS_DEBUG
kprintf("<%s> %u:%u\n", part.characters(), crumb_id.fsid(), crumb_id.index());
#endif
if (auto mount = find_mount_for_host(crumb_id)) {
#ifdef VFS_DEBUG
kprintf(" -- is host\n");
#endif
crumb_id = mount->guest();
}
if (inode_was_root_at_head_of_loop && crumb_id.is_root_inode() && !is_vfs_root(crumb_id) && part == "..") {
#ifdef VFS_DEBUG
kprintf(" -- is guest\n");
#endif
auto mount = find_mount_for_guest(crumb_id);
auto dir_inode = get_inode(mount->host());
ASSERT(dir_inode);
crumb_id = dir_inode->lookup("..");
}
crumb_inode = get_inode(crumb_id);
ASSERT(crumb_inode);
metadata = crumb_inode->metadata();
if (metadata.is_directory()) {
if (i != parts.size() - 1) {
if (parent_id)
*parent_id = crumb_id;
}
}
if (metadata.is_symlink()) {
if (i == parts.size() - 1) {
if (options & O_NOFOLLOW)
return KResult(-ELOOP);
if (options & O_NOFOLLOW_NOERROR)
return crumb_id;
}
auto result = resolve_symbolic_link(parent, *crumb_inode);
if (result.is_error())
return KResult(-ENOENT);
crumb_id = result.value();
ASSERT(crumb_id.is_valid());
}
}
return crumb_id;
}
InodeIdentifier VFS::old_resolve_path(const String& path, InodeIdentifier base, int& error, int options, InodeIdentifier* parent_id)
{
auto result = resolve_path(path, base, options, parent_id);
if (result.is_error()) {
error = result.error();
return { };
}
return result.value();
}
VFS::Mount::Mount(InodeIdentifier host, RetainPtr<FS>&& guest_fs)
: m_host(host)
, m_guest(guest_fs->root_inode())
, m_guest_fs(move(guest_fs))
{
}
void VFS::register_device(Device& device)
{
m_devices.set(encoded_device(device.major(), device.minor()), &device);
}
void VFS::unregister_device(Device& device)
{
m_devices.remove(encoded_device(device.major(), device.minor()));
}
Device* VFS::get_device(unsigned major, unsigned minor)
{
auto it = m_devices.find(encoded_device(major, minor));
if (it == m_devices.end())
return nullptr;
return (*it).value;
}
void VFS::for_each_mount(Function<void(const Mount&)> callback) const
{
for (auto& mount : m_mounts) {
callback(*mount);
}
}
void VFS::sync()
{
FS::sync();
}

View file

@ -0,0 +1,118 @@
#pragma once
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/RetainPtr.h>
#include <AK/AKString.h>
#include <AK/Vector.h>
#include <AK/Function.h>
#include "InodeIdentifier.h"
#include "InodeMetadata.h"
#include "Limits.h"
#include "FileSystem.h"
#include <Kernel/KResult.h>
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0100
#define O_EXCL 0200
#define O_NOCTTY 0400
#define O_TRUNC 01000
#define O_APPEND 02000
#define O_NONBLOCK 04000
#define O_DIRECTORY 00200000
#define O_NOFOLLOW 00400000
#define O_CLOEXEC 02000000
#define O_NOFOLLOW_NOERROR 0x4000000
class Device;
class FileDescriptor;
inline constexpr dword encoded_device(unsigned major, unsigned minor)
{
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
}
class VFS;
class VFS {
AK_MAKE_ETERNAL
public:
class Mount {
public:
Mount(InodeIdentifier host, RetainPtr<FS>&&);
InodeIdentifier host() const { return m_host; }
InodeIdentifier guest() const { return m_guest; }
const FS& guest_fs() const { return *m_guest_fs; }
private:
InodeIdentifier m_host;
InodeIdentifier m_guest;
RetainPtr<FS> m_guest_fs;
};
[[gnu::pure]] static VFS& the();
VFS();
~VFS();
bool mount_root(RetainPtr<FS>&&);
bool mount(RetainPtr<FS>&&, const String& path);
KResultOr<Retained<FileDescriptor>> open(RetainPtr<Device>&&, int options);
KResultOr<Retained<FileDescriptor>> open(const String& path, int options, mode_t mode, Inode& base);
KResultOr<Retained<FileDescriptor>> create(const String& path, int options, mode_t mode, Inode& base);
KResult mkdir(const String& path, mode_t mode, Inode& base);
KResult link(const String& old_path, const String& new_path, Inode& base);
KResult unlink(const String& path, Inode& base);
KResult symlink(const String& target, const String& linkpath, Inode& base);
KResult rmdir(const String& path, Inode& base);
KResult chmod(const String& path, mode_t, Inode& base);
KResult chmod(Inode&, mode_t);
KResult chown(const String& path, uid_t, gid_t, Inode& base);
KResult access(const String& path, int mode, Inode& base);
KResult stat(const String& path, int options, Inode& base, struct stat&);
KResult utime(const String& path, Inode& base, time_t atime, time_t mtime);
KResultOr<Retained<Inode>> open_directory(const String& path, Inode& base);
void register_device(Device&);
void unregister_device(Device&);
size_t mount_count() const { return m_mounts.size(); }
void for_each_mount(Function<void(const Mount&)>) const;
KResultOr<String> absolute_path(Inode&);
KResultOr<String> absolute_path(InodeIdentifier);
InodeIdentifier root_inode_id() const;
Inode* root_inode() { return m_root_inode.ptr(); }
const Inode* root_inode() const { return m_root_inode.ptr(); }
void sync();
Device* get_device(unsigned major, unsigned minor);
private:
friend class FileDescriptor;
RetainPtr<Inode> get_inode(InodeIdentifier);
bool is_vfs_root(InodeIdentifier) const;
void traverse_directory_inode(Inode&, Function<bool(const FS::DirectoryEntry&)>);
InodeIdentifier old_resolve_path(const String& path, InodeIdentifier base, int& error, int options = 0, InodeIdentifier* parent_id = nullptr);
KResultOr<InodeIdentifier> resolve_path(const String& path, InodeIdentifier base, int options = 0, InodeIdentifier* parent_id = nullptr);
KResultOr<Retained<Inode>> resolve_path_to_inode(const String& path, Inode& base, RetainPtr<Inode>* parent_id = nullptr, int options = 0);
KResultOr<InodeIdentifier> resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode);
Mount* find_mount_for_host(InodeIdentifier);
Mount* find_mount_for_guest(InodeIdentifier);
RetainPtr<Inode> m_root_inode;
Vector<OwnPtr<Mount>> m_mounts;
HashMap<dword, Device*> m_devices;
};

750
Kernel/FileSystem/ext2_fs.h Normal file
View file

@ -0,0 +1,750 @@
/*
* linux/include/linux/ext2_fs.h
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/include/linux/minix_fs.h
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#ifndef _LINUX_EXT2_FS_H
#define _LINUX_EXT2_FS_H
#include "ext2_types.h" /* Changed from linux/types.h */
/*
* The second extended filesystem constants/structures
*/
/*
* Define EXT2FS_DEBUG to produce debug messages
*/
#undef EXT2FS_DEBUG
/*
* Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
*/
#define EXT2_PREALLOCATE
#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
/*
* The second extended file system version
*/
#define EXT2FS_DATE "95/08/09"
#define EXT2FS_VERSION "0.5b"
/*
* Special inode numbers
*/
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
#define EXT2_JOURNAL_INO 8 /* Journal inode */
/* First non-reserved inode for old ext2 filesystems */
#define EXT2_GOOD_OLD_FIRST_INO 11
/*
* The second extended file system magic number
*/
#define EXT2_SUPER_MAGIC 0xEF53
#ifdef __KERNEL__
#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
#else
/* Assume that user mode programs are passing in an ext2fs superblock, not
* a kernel struct super_block. This will allow us to call the feature-test
* macros from user land. */
#define EXT2_SB(sb) (sb)
#endif
/*
* Maximal count of links to a file
*/
#define EXT2_LINK_MAX 65000
/*
* Macro-instructions used to manage several block sizes
*/
#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
#ifdef __KERNEL__
#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits)
#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size)
#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino)
#else
#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
#endif
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
/*
* Macro-instructions used to manage fragments
*/
#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
#ifdef __KERNEL__
# define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block)
#else
# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
#endif
/*
* ACL structures
*/
struct ext2_acl_header /* Header of Access Control Lists */
{
__u32 aclh_size;
__u32 aclh_file_count;
__u32 aclh_acle_count;
__u32 aclh_first_acle;
};
struct ext2_acl_entry /* Access Control List Entry */
{
__u32 acle_size;
__u16 acle_perms; /* Access permissions */
__u16 acle_type; /* Type of entry */
__u16 acle_tag; /* User or group identity */
__u16 acle_pad1;
__u32 acle_next; /* Pointer on next entry for the */
/* same inode or on next free entry */
};
/*
* Structure of a blocks group descriptor
*/
struct ext2_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_flags;
__u32 bg_reserved[2];
__u16 bg_itable_unused; /* Unused inodes count */
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
};
struct ext4_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_flags;
__u32 bg_reserved[2];
__u16 bg_itable_unused; /* Unused inodes count */
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
__u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */
__u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */
__u32 bg_inode_table_hi; /* Inodes table block MSB */
__u16 bg_free_blocks_count_hi;/* Free blocks count MSB */
__u16 bg_free_inodes_count_hi;/* Free inodes count MSB */
__u16 bg_used_dirs_count_hi; /* Directories count MSB */
__u16 bg_pad;
__u32 bg_reserved2[3];
};
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
/*
* Data structures used by the directory indexing feature
*
* Note: all of the multibyte integer fields are little endian.
*/
/*
* Note: dx_root_info is laid out so that if it should somehow get
* overlaid by a dirent the two low bits of the hash version will be
* zero. Therefore, the hash version mod 4 should never be 0.
* Sincerely, the paranoia department.
*/
struct ext2_dx_root_info {
__u32 reserved_zero;
__u8 hash_version; /* 0 now, 1 at release */
__u8 info_length; /* 8 */
__u8 indirect_levels;
__u8 unused_flags;
};
#define EXT2_HASH_LEGACY 0
#define EXT2_HASH_HALF_MD4 1
#define EXT2_HASH_TEA 2
#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */
#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */
#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */
#define EXT2_HASH_FLAG_INCOMPAT 0x1
struct ext2_dx_entry {
__u32 hash;
__u32 block;
};
struct ext2_dx_countlimit {
__u16 limit;
__u16 count;
};
/*
* Macro-instructions used to manage group descriptors
*/
#define EXT2_MIN_DESC_SIZE 32
#define EXT2_MIN_DESC_SIZE_64BIT 64
#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE
#define EXT2_DESC_SIZE(s) \
((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \
(s)->s_desc_size : EXT2_MIN_DESC_SIZE)
#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
#ifdef __KERNEL__
#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
#else
#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s))
#endif
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
/*
* Inode flags
*/
#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
#define EXT2_UNRM_FL 0x00000002 /* Undelete */
#define EXT2_COMPR_FL 0x00000004 /* Compress file */
#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
/* Reserved for compression usage... */
#define EXT2_DIRTY_FL 0x00000100
#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
/* End compression flags --- maybe not all used */
#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
#define EXT2_IMAGIC_FL 0x00002000
#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
/*
* ioctl commands
*/
/* Used for online resize */
struct ext2_new_group_input {
__u32 group; /* Group number for this data */
__u32 block_bitmap; /* Absolute block number of block bitmap */
__u32 inode_bitmap; /* Absolute block number of inode bitmap */
__u32 inode_table; /* Absolute block number of inode table start */
__u32 blocks_count; /* Total number of blocks in this group */
__u16 reserved_blocks; /* Number of reserved blocks in this group */
__u16 unused; /* Number of reserved GDT blocks in group */
};
struct ext4_new_group_input {
__u32 group; /* Group number for this data */
__u64 block_bitmap; /* Absolute block number of block bitmap */
__u64 inode_bitmap; /* Absolute block number of inode bitmap */
__u64 inode_table; /* Absolute block number of inode table start */
__u32 blocks_count; /* Total number of blocks in this group */
__u16 reserved_blocks; /* Number of reserved blocks in this group */
__u16 unused;
};
#ifdef __GNU__ /* Needed for the Hurd */
#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0)
#endif
#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long)
#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long)
#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input)
#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input)
/*
* Structure of an inode on the disk
*/
struct ext2_inode {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Inode change time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_version; /* was l_i_reserved1 */
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u16 l_i_blocks_hi;
__u16 l_i_file_acl_high;
__u16 l_i_uid_high; /* these 2 fields */
__u16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
} osd2; /* OS dependent 2 */
};
/*
* Permanent part of an large inode on the disk
*/
struct ext2_inode_large {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Inode Change time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_version; /* was l_i_reserved1 */
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u16 l_i_blocks_hi;
__u16 l_i_file_acl_high;
__u16 l_i_uid_high; /* these 2 fields */
__u16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
} osd2; /* OS dependent 2 */
__u16 i_extra_isize;
__u16 i_pad1;
__u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
__u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */
__u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
__u32 i_crtime; /* File creation time */
__u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
__u32 i_version_hi; /* high 32 bits for 64-bit version */
};
#define i_size_high i_dir_acl
#if defined(__KERNEL__) || defined(__linux__)
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_frag osd2.linux2.l_i_frag
#define i_fsize osd2.linux2.l_i_fsize
#define i_uid_low i_uid
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
#define i_gid_high osd2.linux2.l_i_gid_high
#define i_reserved2 osd2.linux2.l_i_reserved2
#else
#if defined(__GNU__)
#define i_translator osd1.hurd1.h_i_translator
#define i_frag osd2.hurd2.h_i_frag;
#define i_fsize osd2.hurd2.h_i_fsize;
#define i_uid_high osd2.hurd2.h_i_uid_high
#define i_gid_high osd2.hurd2.h_i_gid_high
#define i_author osd2.hurd2.h_i_author
#endif /* __GNU__ */
#endif /* defined(__KERNEL__) || defined(__linux__) */
#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16)
#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16)
#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x))
#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x))
/*
* File system states
*/
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */
/*
* Misc. filesystem flags
*/
#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */
#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */
#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */
/*
* Mount flags
*/
#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
EXT2_MOUNT_##opt)
/*
* Maximal mount counts between two filesystem checks
*/
#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
/*
* Behaviour when detecting errors
*/
#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
#define EXT2_ERRORS_PANIC 3 /* Panic */
#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
/*
* Structure of the super block
*/
struct ext2_super_block {
__u32 s_inodes_count; /* Inodes count */
__u32 s_blocks_count; /* Blocks count */
__u32 s_r_blocks_count; /* Reserved blocks count */
__u32 s_free_blocks_count; /* Free blocks count */
__u32 s_free_inodes_count; /* Free inodes count */
__u32 s_first_data_block; /* First Data Block */
__u32 s_log_block_size; /* Block size */
__s32 s_log_frag_size; /* Fragment size */
__u32 s_blocks_per_group; /* # Blocks per group */
__u32 s_frags_per_group; /* # Fragments per group */
__u32 s_inodes_per_group; /* # Inodes per group */
__u32 s_mtime; /* Mount time */
__u32 s_wtime; /* Write time */
__u16 s_mnt_count; /* Mount count */
__s16 s_max_mnt_count; /* Maximal mount count */
__u16 s_magic; /* Magic signature */
__u16 s_state; /* File system state */
__u16 s_errors; /* Behaviour when detecting errors */
__u16 s_minor_rev_level; /* minor revision level */
__u32 s_lastcheck; /* time of last check */
__u32 s_checkinterval; /* max. time between checks */
__u32 s_creator_os; /* OS */
__u32 s_rev_level; /* Revision level */
__u16 s_def_resuid; /* Default uid for reserved blocks */
__u16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT2_DYNAMIC_REV superblocks only.
*
* Note: the difference between the compatible feature set and
* the incompatible feature set is that if there is a bit set
* in the incompatible feature set that the kernel doesn't
* know about, it should refuse to mount the filesystem.
*
* e2fsck's requirements are more strict; if it doesn't know
* about a feature in either the compatible or incompatible
* feature set, it must abort and not try to meddle with
* things it doesn't understand...
*/
__u32 s_first_ino; /* First non-reserved inode */
__u16 s_inode_size; /* size of inode structure */
__u16 s_block_group_nr; /* block group # of this superblock */
__u32 s_feature_compat; /* compatible feature set */
__u32 s_feature_incompat; /* incompatible feature set */
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__u32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
*/
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__u16 s_reserved_gdt_blocks; /* Per group table for online growth */
/*
* Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
*/
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
__u32 s_hash_seed[4]; /* HTREE hash seed */
__u8 s_def_hash_version; /* Default hash version to use */
__u8 s_jnl_backup_type; /* Default type of journal backup */
__u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */
__u32 s_default_mount_opts;
__u32 s_first_meta_bg; /* First metablock group */
__u32 s_mkfs_time; /* When the filesystem was created */
__u32 s_jnl_blocks[17]; /* Backup of the journal inode */
__u32 s_blocks_count_hi; /* Blocks count high 32bits */
__u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/
__u32 s_free_blocks_hi; /* Free blocks count */
__u16 s_min_extra_isize; /* All inodes have at least # bytes */
__u16 s_want_extra_isize; /* New inodes should reserve # bytes */
__u32 s_flags; /* Miscellaneous flags */
__u16 s_raid_stride; /* RAID stride */
__u16 s_mmp_interval; /* # seconds to wait in MMP checking */
__u64 s_mmp_block; /* Block for multi-mount protection */
__u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
__u8 s_reserved_char_pad;
__u16 s_reserved_pad; /* Padding to next 32bits */
__u32 s_reserved[162]; /* Padding to the end of the block */
};
/*
* Codes for operating systems
*/
#define EXT2_OS_LINUX 0
#define EXT2_OS_HURD 1
#define EXT2_OBSO_OS_MASIX 2
#define EXT2_OS_FREEBSD 3
#define EXT2_OS_LITES 4
/*
* Revision levels
*/
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
#define EXT2_GOOD_OLD_INODE_SIZE 128
/*
* Journal inode backup types
*/
#define EXT3_JNL_BACKUP_BLOCKS 1
/*
* Feature set definitions
*/
#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_compat & (mask) )
#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_incompat & (mask) )
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
/*
* Default values for user and/or group using reserved blocks
*/
#define EXT2_DEF_RESUID 0
#define EXT2_DEF_RESGID 0
/*
* Default mount options
*/
#define EXT2_DEFM_DEBUG 0x0001
#define EXT2_DEFM_BSDGROUPS 0x0002
#define EXT2_DEFM_XATTR_USER 0x0004
#define EXT2_DEFM_ACL 0x0008
#define EXT2_DEFM_UID16 0x0010
#define EXT3_DEFM_JMODE 0x0060
#define EXT3_DEFM_JMODE_DATA 0x0020
#define EXT3_DEFM_JMODE_ORDERED 0x0040
#define EXT3_DEFM_JMODE_WBACK 0x0060
/*
* Structure of a directory entry
*/
#define EXT2_NAME_LEN 255
struct ext2_dir_entry {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u16 name_len; /* Name length */
char name[EXT2_NAME_LEN]; /* File name */
};
/*
* The new version of the directory entry. Since EXT2 structures are
* stored in intel byte order, and the name_len field could never be
* bigger than 255 chars, it's safe to reclaim the extra byte for the
* file_type field.
*/
struct ext2_dir_entry_2 {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */
__u8 file_type;
char name[EXT2_NAME_LEN]; /* File name */
};
/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
#define EXT2_FT_MAX 8
/*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
*/
#define EXT2_DIR_PAD 4
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
/*
* This structure will be used for multiple mount protection. It will be
* written into the block number saved in the s_mmp_block field in the
* superblock.
*/
#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */
#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */
#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */
struct mmp_struct {
__u32 mmp_magic;
__u32 mmp_seq;
__u64 mmp_time;
char mmp_nodename[64];
char mmp_bdevname[32];
__u16 mmp_interval;
__u16 mmp_pad1;
__u32 mmp_pad2;
};
/*
* Interval in number of seconds to update the MMP sequence number.
*/
#define EXT2_MMP_DEF_INTERVAL 5
#endif /* _LINUX_EXT2_FS_H */

View file

@ -0,0 +1,145 @@
/*
* If linux/types.h is already been included, assume it has defined
* everything we need. (cross fingers) Other header files may have
* also defined the types that we need.
*/
#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \
!defined(_EXT2_TYPES_H))
#define _EXT2_TYPES_H
#define __S8_TYPEDEF __signed__ char
#define __U8_TYPEDEF unsigned char
#define __S16_TYPEDEF __signed__ short
#define __U16_TYPEDEF unsigned short
#define __S32_TYPEDEF __signed__ int
#define __U32_TYPEDEF unsigned int
#define __S64_TYPEDEF __signed__ long long
#define __U64_TYPEDEF unsigned long long
#ifdef __U8_TYPEDEF
typedef __U8_TYPEDEF __u8;
#else
typedef unsigned char __u8;
#endif
#ifdef __S8_TYPEDEF
typedef __S8_TYPEDEF __s8;
#else
typedef signed char __s8;
#endif
#ifdef __U16_TYPEDEF
typedef __U16_TYPEDEF __u16;
#else
#if (4 == 2)
typedef unsigned int __u16;
#else
#if (2 == 2)
typedef unsigned short __u16;
#else
?==error: undefined 16 bit type
#endif /* SIZEOF_SHORT == 2 */
#endif /* SIZEOF_INT == 2 */
#endif /* __U16_TYPEDEF */
#ifdef __S16_TYPEDEF
typedef __S16_TYPEDEF __s16;
#else
#if (4 == 2)
typedef int __s16;
#else
#if (2 == 2)
typedef short __s16;
#else
?==error: undefined 16 bit type
#endif /* SIZEOF_SHORT == 2 */
#endif /* SIZEOF_INT == 2 */
#endif /* __S16_TYPEDEF */
#ifdef __U32_TYPEDEF
typedef __U32_TYPEDEF __u32;
#else
#if (4 == 4)
typedef unsigned int __u32;
#else
#if (4 == 4)
typedef unsigned long __u32;
#else
#if (2 == 4)
typedef unsigned short __u32;
#else
?== error: undefined 32 bit type
#endif /* SIZEOF_SHORT == 4 */
#endif /* SIZEOF_LONG == 4 */
#endif /* SIZEOF_INT == 4 */
#endif /* __U32_TYPEDEF */
#ifdef __S32_TYPEDEF
typedef __S32_TYPEDEF __s32;
#else
#if (4 == 4)
typedef int __s32;
#else
#if (4 == 4)
typedef long __s32;
#else
#if (2 == 4)
typedef short __s32;
#else
?== error: undefined 32 bit type
#endif /* SIZEOF_SHORT == 4 */
#endif /* SIZEOF_LONG == 4 */
#endif /* SIZEOF_INT == 4 */
#endif /* __S32_TYPEDEF */
#ifdef __U64_TYPEDEF
typedef __U64_TYPEDEF __u64;
#else
#if (4 == 8)
typedef unsigned int __u64;
#else
#if (4 == 8)
typedef unsigned long __u64;
#else
#if (8 == 8)
typedef unsigned long long __u64;
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __U64_TYPEDEF */
#ifdef __S64_TYPEDEF
typedef __S64_TYPEDEF __s64;
#else
#if (4 == 8)
typedef int __s64;
#else
#if (4 == 8)
typedef long __s64;
#else
#if (8 == 8)
#if defined(__GNUC__)
typedef __signed__ long long __s64;
#else
typedef signed long long __s64;
#endif /* __GNUC__ */
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __S64_TYPEDEF */
#undef __S8_TYPEDEF
#undef __U8_TYPEDEF
#undef __S16_TYPEDEF
#undef __U16_TYPEDEF
#undef __S32_TYPEDEF
#undef __U32_TYPEDEF
#undef __S64_TYPEDEF
#undef __U64_TYPEDEF
#endif /* _*_TYPES_H */
/* These defines are needed for the public ext2fs.h header file */
#define HAVE_SYS_TYPES_H 1
#undef WORDS_BIGENDIAN