/* * 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 #include #include #include "ext2.h" #include "Volume.h" //#define TRACE_EXT2 #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* _Volume() const { return fVolume; } 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&); // no implementation 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; // Inodes have a variable size, but the important // information is always the same size (except in ext4) status_t fInitStatus; mutable recursive_lock fSmallDataLock; }; // The Vnode class provides a convenience layer upon get_vnode(), so that // you don't have to call put_vnode() anymore, which may make code more // readable in some cases 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; // TODO: How to apply coding style here? static void _TransactionListener(int32 id, int32 event, void* _inode) { Inode* inode = (Inode*)_inode; if (event == TRANSACTION_ABORTED) { // TODO: Unpublish? panic("Transaction %d aborted, inode %p still exists!\n", (int)id, inode); } } }; #endif // INODE_H