* Copyright 2001-2025, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef INODE_H
#define INODE_H
#include "system_dependencies.h"
#include "CachedBlock.h"
#include "Debug.h"
#include "Journal.h"
#include "Volume.h"
class BPlusTree;
class TreeIterator;
class AttributeIterator;
class Index;
class InodeAllocator;
class NodeGetter;
class Transaction;
#define BFS_DO_NOT_PUBLISH_VNODE 0x80000000
class Inode : public TransactionListener {
typedef DoublyLinkedListLink<Inode> Link;
public:
Inode(Volume* volume, ino_t id);
Inode(Volume* volume, Transaction& transaction,
ino_t id, mode_t mode, block_run& run);
~Inode();
status_t InitCheck(bool checkNode = true) const;
ino_t ID() const { return fID; }
off_t BlockNumber() const
{ return fVolume->VnodeToBlock(fID); }
rw_lock& Lock() { return fLock; }
ReadLocker ReadLock() { return ReadLocker(fLock); }
void WriteLockInTransaction(Transaction& transaction);
recursive_lock& SmallDataLock() { return fSmallDataLock; }
status_t WriteBack(Transaction& transaction);
status_t UpdateNodeFromDisk();
bool IsContainer() const
{ return S_ISDIR(Mode()); }
bool IsDirectory() const
{ return is_directory(Mode()); }
bool IsIndex() const
{ return is_index(Mode()); }
bool IsAttributeDirectory() const
{ return (Mode() & S_EXTENDED_TYPES)
== S_ATTR_DIR; }
bool IsAttribute() const
{ return (Mode() & S_EXTENDED_TYPES)
== S_ATTR; }
bool IsFile() const
{ return (Mode() & (S_IFMT
| S_EXTENDED_TYPES)) == S_FILE; }
bool IsRegularNode() const
{ return (Mode() & S_EXTENDED_TYPES) == 0; }
bool IsSymLink() const { return S_ISLNK(Mode()); }
bool IsLongSymLink() const
{ return (Flags() & INODE_LONG_SYMLINK)
!= 0; }
bool HasUserAccessableStream() const
{ return IsFile(); }
bool NeedsFileCache() const
{ return IsFile() || IsAttribute()
|| IsLongSymLink(); }
bool IsDeleted() const
{ return (Flags() & INODE_DELETED) != 0; }
mode_t Mode() const { return fNode.Mode(); }
uint32 Type() const { return fNode.Type(); }
int32 Flags() const { return fNode.Flags(); }
off_t Size() const { return fNode.data.Size(); }
off_t AllocatedSize() const;
off_t LastModified() const
{ return fNode.LastModifiedTime(); }
const block_run& BlockRun() const
{ return fNode.inode_num; }
block_run& Parent() { return fNode.parent; }
const block_run& Parent() const { return fNode.parent; }
ino_t ParentID() const
{ return fVolume->ToVnode(Parent()); }
block_run& Attributes() { return fNode.attributes; }
Volume* GetVolume() const { return fVolume; }
status_t CheckPermissions(int accessMode) const;
small_data* FindSmallData(const bfs_inode* node,
const char* name) const;
const char* Name(const bfs_inode* node) const;
status_t GetName(char* buffer, size_t bufferSize
= B_FILE_NAME_LENGTH) const;
status_t SetName(Transaction& transaction,
const char* name);
status_t ReadAttribute(const char* name, int32 type,
off_t pos, uint8* buffer, size_t* _length);
status_t WriteAttribute(Transaction& transaction,
const char* name, int32 type, off_t pos,
const uint8* buffer, size_t* _length,
bool* _created);
status_t RemoveAttribute(Transaction& transaction,
const char* name);
status_t GetAttribute(const char* name,
Inode** _attribute);
void ReleaseAttribute(Inode* attribute);
status_t CreateAttribute(Transaction& transaction,
const char* name, uint32 type,
Inode** attribute);
BPlusTree* Tree() const { return fTree; }
bool IsEmpty();
status_t ContainerContentsChanged(
Transaction& transaction);
status_t FindBlockRun(off_t pos, block_run& run,
off_t& offset);
status_t ReadAt(off_t pos, uint8* buffer, size_t* length);
status_t WriteAt(Transaction& transaction, off_t pos,
const uint8* buffer, size_t* length);
status_t FillGapWithZeros(off_t oldSize, off_t newSize);
status_t SetFileSize(Transaction& transaction,
off_t size);
status_t Append(Transaction& transaction, off_t bytes);
status_t TrimPreallocation(Transaction& transaction);
bool NeedsTrimming() const;
status_t Free(Transaction& transaction);
status_t Sync();
bfs_inode& Node() { return fNode; }
const bfs_inode& Node() const { return fNode; }
status_t Remove(Transaction& transaction,
const char* name, ino_t* _id = NULL,
bool isDirectory = false,
bool force = false);
static status_t Create(Transaction& transaction, Inode* parent,
const char* name, int32 mode, int openMode,
uint32 type, bool* _created = NULL,
ino_t* _id = NULL, Inode** _inode = NULL,
fs_vnode_ops* vnodeOps = NULL,
uint32 publishFlags = 0);
void UpdateOldSize() { fOldSize = Size(); }
void UpdateOldLastModified()
{ fOldLastModified
= Node().LastModifiedTime(); }
off_t OldSize() { return fOldSize; }
off_t OldLastModified() { return fOldLastModified; }
bool InNameIndex() const;
bool InSizeIndex() const;
bool InLastModifiedIndex() const;
void* FileCache() const { return fCache; }
void SetFileCache(void* cache) { fCache = cache; }
void* Map() const { return fMap; }
void SetMap(void* map) { fMap = map; }
status_t CopyBlockTo(Transaction& transaction, off_t targetBlock);
#if _KERNEL_MODE && KDEBUG
void AssertReadLocked()
{ ASSERT_READ_LOCKED_RW_LOCK(&fLock); }
void AssertWriteLocked()
{ ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); }
#endif
Link* GetDoublyLinkedListLink()
{ return (Link*)TransactionListener
::GetDoublyLinkedListLink(); }
const Link* GetDoublyLinkedListLink() const
{ return (Link*)TransactionListener
::GetDoublyLinkedListLink(); }
protected:
virtual void TransactionDone(bool success);
virtual void RemovedFromTransaction();
private:
Inode(const Inode& other);
Inode& operator=(const Inode& other);
friend class AttributeIterator;
friend class InodeAllocator;
status_t _MakeSpaceForSmallData(Transaction& transaction,
bfs_inode* node, const char* name,
int32 length);
status_t _RemoveSmallData(Transaction& transaction,
NodeGetter& node, const char* name);
status_t _AddSmallData(Transaction& transaction,
NodeGetter& node, const char* name,
uint32 type, off_t pos, const uint8* data,
size_t length, bool force = false);
status_t _GetNextSmallData(bfs_inode* node,
small_data** _smallData) const;
status_t _RemoveSmallData(bfs_inode* node,
small_data* item, int32 index);
status_t _RemoveAttribute(Transaction& transaction,
const char* name, bool hasIndex,
Index* index);
void _AddIterator(AttributeIterator* iterator);
void _RemoveIterator(AttributeIterator* iterator);
size_t _DoubleIndirectBlockLength() const;
status_t _FreeStaticStreamArray(Transaction& transaction,
int32 level, block_run run, off_t size,
off_t offset, off_t& max);
status_t _FreeStreamArray(Transaction& transaction,
block_run* array, uint32 arrayLength,
off_t size, off_t& offset, off_t& max);
status_t _AllocateBlockArray(Transaction& transaction,
block_run& run, size_t length,
bool variableSize = false);
status_t _GrowStream(Transaction& transaction,
off_t size);
status_t _ShrinkStream(Transaction& transaction,
off_t size);
private:
rw_lock fLock;
Volume* fVolume;
ino_t fID;
BPlusTree* fTree;
Inode* fAttributes;
void* fCache;
void* fMap;
bfs_inode fNode;
off_t fOldSize;
off_t fOldLastModified;
mutable recursive_lock fSmallDataLock;
SinglyLinkedList<AttributeIterator> fIterators;
};
inline bool
Inode::InNameIndex() const
{
return IsRegularNode();
}
inline bool
Inode::InSizeIndex() const
{
return IsFile();
}
inline bool
Inode::InLastModifiedIndex() const
{
return IsFile() || IsSymLink();
}
#if _KERNEL_MODE && KDEBUG
# define ASSERT_READ_LOCKED_INODE(inode) inode->AssertReadLocked()
# define ASSERT_WRITE_LOCKED_INODE(inode) inode->AssertWriteLocked()
#else
# define ASSERT_READ_LOCKED_INODE(inode)
# define ASSERT_WRITE_LOCKED_INODE(inode)
#endif
class InodeReadLocker {
public:
InodeReadLocker(Inode* inode)
:
fLock(&inode->Lock())
{
rw_lock_read_lock(fLock);
}
~InodeReadLocker()
{
if (fLock != NULL)
rw_lock_read_unlock(fLock);
}
void Unlock()
{
if (fLock != NULL) {
rw_lock_read_unlock(fLock);
fLock = NULL;
}
}
private:
rw_lock* fLock;
};
class NodeGetter : public CachedBlock {
public:
NodeGetter(Volume* volume = NULL)
:
CachedBlock(volume)
{
}
~NodeGetter()
{
}
status_t SetTo(const Inode* inode)
{
Unset();
fVolume = inode->GetVolume();
return CachedBlock::SetTo(fVolume->VnodeToBlock(inode->ID()));
}
status_t SetToWritable(Transaction& transaction, const Inode* inode, bool empty = false)
{
Unset();
fVolume = inode->GetVolume();
return CachedBlock::SetToWritable(transaction, fVolume->VnodeToBlock(inode->ID()), empty);
}
const bfs_inode* Node() const { return (const bfs_inode*)Block(); }
bfs_inode* WritableNode() const { return (bfs_inode*)Block(); }
};
class Vnode {
public:
Vnode(Volume* volume, ino_t id)
:
fInode(NULL)
{
SetTo(volume, id);
}
Vnode(Volume* volume, block_run run)
:
fInode(NULL)
{
SetTo(volume, run);
}
Vnode()
:
fStatus(B_NO_INIT),
fInode(NULL)
{
}
~Vnode()
{
Unset();
}
status_t InitCheck()
{
return fStatus;
}
void Unset()
{
if (fInode != NULL) {
put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID());
fInode = NULL;
fStatus = B_NO_INIT;
}
}
status_t SetTo(Volume* volume, ino_t id)
{
Unset();
return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
}
status_t SetTo(Volume* volume, block_run run)
{
return SetTo(volume, volume->ToVnode(run));
}
status_t Get(Inode** _inode)
{
*_inode = fInode;
return fStatus;
}
void Keep()
{
fInode = NULL;
}
private:
status_t fStatus;
Inode* fInode;
};
class AttributeIterator : public SinglyLinkedListLinkImpl<AttributeIterator> {
public:
AttributeIterator(Inode* inode);
~AttributeIterator();
status_t Rewind();
status_t GetNext(char* name, size_t* length, uint32* type,
ino_t* id);
private:
friend class Inode;
void Update(uint16 index, int8 change);
private:
int32 fCurrentSmallData;
Inode* fInode;
Inode* fAttributes;
TreeIterator* fIterator;
void* fBuffer;
};
#endif