* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008, 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 <fs_cache.h>
#include <lock.h>
#include <string.h>
#include "ext2.h"
#include "Volume.h"
#ifdef TRACE_EXT2
# define TRACEI(x...) dprintf("\33[34mext2:\33[0m " x)
#else
# define TRACEI(x...) ;
#endif
class Inode : public TransactionListener {
public:
Inode(Volume* volume, ino_t id);
~Inode();
status_t InitCheck();
ino_t ID() const { return fID; }
rw_lock* Lock() { return &fLock; }
void WriteLockInTransaction(Transaction& transaction);
status_t UpdateNodeFromDisk();
status_t WriteBack(Transaction& transaction);
recursive_lock& SmallDataLock() { return fSmallDataLock; }
bool IsDirectory() const
{ return S_ISDIR(Mode()); }
bool IsFile() const
{ return S_ISREG(Mode()); }
bool IsSymLink() const
{ return S_ISLNK(Mode()); }
status_t CheckPermissions(int accessMode) const;
bool IsDeleted() const { return fUnlinked; }
bool HasExtraAttributes() const
{ return fHasExtraAttributes; }
bool IsIndexed() const
{ return fVolume->IndexedDirectories()
&& (Flags() & EXT2_INODE_INDEXED) != 0; }
mode_t Mode() const { return fNode.Mode(); }
int32 Flags() const { return fNode.Flags(); }
off_t Size() const { return fNode.Size(); }
uint64 NumBlocks();
void GetChangeTime(struct timespec *timespec) const
{ fNode.GetChangeTime(timespec, fHasExtraAttributes); }
void GetModificationTime(struct timespec *timespec) const
{ fNode.GetModificationTime(timespec,
fHasExtraAttributes); }
void GetCreationTime(struct timespec *timespec) const
{ fNode.GetCreationTime(timespec,
fHasExtraAttributes); }
void GetAccessTime(struct timespec *timespec) const
{ fNode.GetAccessTime(timespec, fHasExtraAttributes); }
void SetChangeTime(const struct timespec *timespec)
{ fNode.SetChangeTime(timespec, fHasExtraAttributes); }
void SetModificationTime(const struct timespec *timespec)
{ fNode.SetModificationTime(timespec,
fHasExtraAttributes); }
void SetCreationTime(const struct timespec *timespec)
{ fNode.SetCreationTime(timespec,
fHasExtraAttributes); }
void SetAccessTime(const struct timespec *timespec)
{ fNode.SetAccessTime(timespec, fHasExtraAttributes); }
void IncrementNumLinks(Transaction& transaction);
Volume* GetVolume() const { return fVolume; }
status_t FindBlock(off_t offset, fsblock_t& block,
uint32 *_count = NULL);
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 start, off_t end);
status_t Resize(Transaction& transaction, off_t size);
ext2_inode& Node() { return fNode; }
status_t InitDirectory(Transaction& transaction, Inode* parent);
status_t Unlink(Transaction& transaction);
static status_t Create(Transaction& transaction, Inode* parent,
const char* name, int32 mode, int openMode,
uint8 type, bool* _created = NULL,
ino_t* _id = NULL, Inode** _inode = NULL,
fs_vnode_ops* vnodeOps = NULL,
uint32 publishFlags = 0);
static void _BigtimeToTimespec(bigtime_t time,
struct timespec *timespec)
{ timespec->tv_sec = time / 1000000LL;
timespec->tv_nsec = (time % 1000000LL) * 1000; }
void* FileCache() const { return fCache; }
void* Map() const { return fMap; }
status_t CreateFileCache();
void DeleteFileCache();
bool HasFileCache() { return fCache != NULL; }
status_t EnableFileCache();
status_t DisableFileCache();
status_t Sync();
void SetDirEntryChecksum(uint8* block, uint32 id, uint32 gen);
void SetDirEntryChecksum(uint8* block);
void SetExtentChecksum(ext2_extent_stream* stream);
bool VerifyExtentChecksum(ext2_extent_stream* stream);
protected:
virtual void TransactionDone(bool success);
virtual void RemovedFromTransaction();
private:
Inode(Volume* volume);
Inode(const Inode&);
Inode &operator=(const Inode&);
status_t _EnlargeDataStream(Transaction& transaction,
off_t size);
status_t _ShrinkDataStream(Transaction& transaction,
off_t size);
status_t _SetNumBlocks(uint64 numBlocks);
uint32 _InodeChecksum(ext2_inode* inode);
ext2_dir_entry_tail* _DirEntryTail(uint8* block) const;
uint32 _DirEntryChecksum(uint8* block, uint32 id,
uint32 gen) const;
uint32 _ExtentLength(ext2_extent_stream* stream) const;
uint32 _ExtentChecksum(ext2_extent_stream* stream) const;
rw_lock fLock;
::Volume* fVolume;
ino_t fID;
void* fCache;
void* fMap;
bool fUnlinked;
bool fHasExtraAttributes;
ext2_inode fNode;
uint32 fNodeSize;
status_t fInitStatus;
mutable recursive_lock fSmallDataLock;
};
class Vnode {
public:
Vnode(Volume* volume, ino_t id)
:
fInode(NULL)
{
SetTo(volume, id);
}
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 Get(Inode** _inode)
{
*_inode = fInode;
return fStatus;
}
void Keep()
{
TRACEI("Vnode::Keep()\n");
fInode = NULL;
}
status_t Publish(Transaction& transaction, Inode* inode,
fs_vnode_ops* vnodeOps, uint32 publishFlags)
{
TRACEI("Vnode::Publish()\n");
Volume* volume = transaction.GetVolume();
status_t status = B_OK;
if (!inode->IsSymLink() && volume->ID() >= 0) {
TRACEI("Vnode::Publish(): Publishing volume: %p, %" B_PRIdINO
", %p, %p, %" B_PRIu16 ", %" B_PRIx32 "\n", volume->FSVolume(),
inode->ID(), inode, vnodeOps != NULL ? vnodeOps : &gExt2VnodeOps,
inode->Mode(), publishFlags);
status = publish_vnode(volume->FSVolume(), inode->ID(), inode,
vnodeOps != NULL ? vnodeOps : &gExt2VnodeOps, inode->Mode(),
publishFlags);
TRACEI("Vnode::Publish(): Result: %s\n", strerror(status));
}
if (status == B_OK) {
TRACEI("Vnode::Publish(): Preparing internal data\n");
fInode = inode;
fStatus = B_OK;
cache_add_transaction_listener(volume->BlockCache(),
transaction.ID(), TRANSACTION_ABORTED, &_TransactionListener,
inode);
}
return status;
}
private:
status_t fStatus;
Inode* fInode;
static void _TransactionListener(int32 id, int32 event, void* _inode)
{
Inode* inode = (Inode*)_inode;
if (event == TRANSACTION_ABORTED) {
panic("Transaction %d aborted, inode %p still exists!\n", (int)id,
inode);
}
}
};
#endif