diff --git a/src/add-ons/kernel/file_systems/xfs/BPlusTree.cpp b/src/add-ons/kernel/file_systems/xfs/BPlusTree.cpp index 819e50f633..77321583bb 100644 --- a/src/add-ons/kernel/file_systems/xfs/BPlusTree.cpp +++ b/src/add-ons/kernel/file_systems/xfs/BPlusTree.cpp @@ -32,7 +32,7 @@ TreeDirectory::TreeDirectory(Inode* inode) } memcpy((void*)fRoot, - DIR_DFORK_PTR(fInode->Buffer()), sizeof(BlockInDataFork)); + DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()), sizeof(BlockInDataFork)); for (int i = 0; i < MAX_TREE_DEPTH; i++) { fPathForLeaves[i].blockData = NULL; @@ -85,7 +85,7 @@ TreeDirectory::MaxRecordsPossibleRoot() lengthOfDataFork = fInode->ForkOffset() << 3; if (fInode->ForkOffset() == 0) { lengthOfDataFork = fInode->GetVolume()->InodeSize() - - INODE_CORE_UNLINKED_SIZE; + - fInode->CoreInodeSize(); } lengthOfDataFork -= sizeof(BlockInDataFork); @@ -122,7 +122,8 @@ TreePointer* TreeDirectory::GetPtrFromRoot(int pos) { return (TreePointer*) - ((char*)DIR_DFORK_PTR(fInode->Buffer()) + GetPtrOffsetIntoRoot(pos)); + ((char*)DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()) + + GetPtrOffsetIntoRoot(pos)); } @@ -146,7 +147,7 @@ TreeKey* TreeDirectory::GetKeyFromRoot(int pos) { off_t offset = (pos - 1) * KeySize(); - char* base = (char*)DIR_DFORK_PTR(fInode->Buffer()) + char* base = (char*)DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()) + sizeof(BlockInDataFork); return (TreeKey*) (base + offset); } diff --git a/src/add-ons/kernel/file_systems/xfs/Extent.cpp b/src/add-ons/kernel/file_systems/xfs/Extent.cpp index dbe4023b8d..7bc3ab1440 100644 --- a/src/add-ons/kernel/file_systems/xfs/Extent.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Extent.cpp @@ -70,7 +70,7 @@ Extent::Init() return B_NO_MEMORY; ASSERT(IsBlockType() == true); - void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer()); + void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()); FillMapEntry(pointerToMap); ASSERT(fMap->br_blockcount == 1); //TODO: This is always true for block directories diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.cpp b/src/add-ons/kernel/file_systems/xfs/Inode.cpp index a99d89ae1b..6ec3bfc30e 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Inode.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2022, Raghav Sharma, raghavself28@gmail.com * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com * All rights reserved. Distributed under the terms of the MIT License. */ @@ -6,6 +7,7 @@ #include "Inode.h" #include "BPlusTree.h" +#include "Checksum.h" void xfs_inode_t::SwapEndian() @@ -34,6 +36,13 @@ xfs_inode_t::SwapEndian() di_flags = B_BENDIAN_TO_HOST_INT16(di_flags); di_gen = B_BENDIAN_TO_HOST_INT32(di_gen); di_next_unlinked = B_BENDIAN_TO_HOST_INT32(di_next_unlinked); + di_changecount = B_BENDIAN_TO_HOST_INT64(di_changecount); + di_lsn = B_BENDIAN_TO_HOST_INT64(di_lsn); + di_flags2 = B_BENDIAN_TO_HOST_INT64(di_flags2); + di_cowextsize = B_BENDIAN_TO_HOST_INT64(di_cowextsize); + di_crtime.t_sec = B_BENDIAN_TO_HOST_INT32(di_crtime.t_sec); + di_crtime.t_nsec = B_BENDIAN_TO_HOST_INT32(di_crtime.t_nsec); + di_ino = B_BENDIAN_TO_HOST_INT64(di_ino); } @@ -103,6 +112,14 @@ xfs_inode_t::GetChangeTime(struct timespec& stamp) } +void +xfs_inode_t::GetCreationTime(struct timespec& stamp) +{ + stamp.tv_sec = di_crtime.t_sec; + stamp.tv_nsec = di_crtime.t_nsec; +} + + uint32 xfs_inode_t::NLink() const { @@ -148,6 +165,201 @@ Inode::Inode(Volume* volume, xfs_ino_t id) } +//Convert inode mode to directory entry filetype +unsigned char +Inode::XfsModeToFtype() const +{ + switch (Mode() & S_IFMT) { + case S_IFREG: + return XFS_DIR3_FT_REG_FILE; + case S_IFDIR: + return XFS_DIR3_FT_DIR; + case S_IFCHR: + return XFS_DIR3_FT_CHRDEV; + case S_IFBLK: + return XFS_DIR3_FT_BLKDEV; + case S_IFIFO: + return XFS_DIR3_FT_FIFO; + case S_IFSOCK: + return XFS_DIR3_FT_SOCK; + case S_IFLNK: + return XFS_DIR3_FT_SYMLINK; + default: + return XFS_DIR3_FT_UNKNOWN; + } +} + +bool +Inode::VerifyFork(int whichFork) const +{ + uint32 di_nextents = XFS_DFORK_NEXTENTS(fNode, whichFork); + + switch (XFS_DFORK_FORMAT(fNode, whichFork)) { + case XFS_DINODE_FMT_LOCAL: + if (whichFork == XFS_DATA_FORK) { + if (S_ISREG(Mode())) + return false; + if (Size() > (xfs_fsize_t) DFORK_SIZE(fNode, fVolume, whichFork)) + return false; + } + if (di_nextents) + return false; + break; + case XFS_DINODE_FMT_EXTENTS: + if (di_nextents > DFORK_MAXEXT(fNode, fVolume, whichFork)) + return false; + break; + case XFS_DINODE_FMT_BTREE: + if (whichFork == XFS_ATTR_FORK) { + if (di_nextents > MAXAEXTNUM) + return false; + } else if (di_nextents > MAXEXTNUM) { + return false; + } + break; + default: + return false; + } + + return true; +} + +bool +Inode::VerifyForkoff() const +{ + switch(Format()) { + case XFS_DINODE_FMT_DEV: + if (fNode->di_forkoff != (ROUNDUP(sizeof(uint32), 8) >> 3)) + return false; + break; + case XFS_DINODE_FMT_LOCAL: + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + if (fNode->di_forkoff >= (LITINO(fVolume) >> 3)) + return false; + break; + default: + return false; + } + + return true; +} + + +bool +Inode::VerifyInode() const +{ + if(fNode->di_magic != INODE_MAGIC) { + ERROR("Bad inode magic number"); + return false; + } + + // check if inode version is valid + if(fNode->Version() < 1 || fNode->Version() > 3) { + ERROR("Bad inode version"); + return false; + } + + // verify version 3 inodes first + if(fNode->Version() == 3) { + + if(!HAS_V3INODES(fVolume)) { + ERROR("xfs v4 doesn't have v3 inodes"); + return false; + } + + if(!xfs_verify_cksum(fBuffer, fVolume->InodeSize(), INODE_CRC_OFF)) { + ERROR("Inode is corrupted"); + return false; + } + + if(fNode->di_ino != fId) { + ERROR("Incorrect inode number"); + return false; + } + + // TODO : uuid verification + } + + if(fNode->di_size & (1ULL << 63)) { + ERROR("Invalid EOF of inode"); + return false; + } + + if (Mode() && XfsModeToFtype() == XFS_DIR3_FT_UNKNOWN) { + ERROR("Entry points to an unknown inode type"); + return false; + } + + if(!VerifyForkoff()) { + ERROR("Invalid inode fork offset"); + return false; + } + + // Check for appropriate data fork formats for the mode + switch (Mode() & S_IFMT) { + case S_IFIFO: + case S_IFCHR: + case S_IFBLK: + case S_IFSOCK: + if (fNode->di_format != XFS_DINODE_FMT_DEV) + return false; + break; + case S_IFREG: + case S_IFLNK: + case S_IFDIR: + if (!VerifyFork(XFS_DATA_FORK)) { + ERROR("Invalid data fork in inode"); + return false; + } + break; + case 0: + // Uninitialized inode is fine + break; + default: + return false; + } + + if (fNode->di_forkoff) { + if (!VerifyFork(XFS_ATTR_FORK)) { + ERROR("Invalid attribute fork in inode"); + return false; + } + } else { + /* + If there is no fork offset, this may be a freshly-made inode + in a new disk cluster, in which case di_aformat is zeroed. + Otherwise, such an inode must be in EXTENTS format; this goes + for freed inodes as well. + */ + switch (fNode->di_aformat) { + case 0: + case XFS_DINODE_FMT_EXTENTS: + break; + default: + return false; + } + + if (fNode->di_anextents) + return false; + } + + // TODO : Add reflink and big-timestamps check using di_flags2 + + return true; +} + + +uint32 +Inode::CoreInodeSize() const +{ + if (Version() == 3) + return sizeof(struct xfs_inode_t); + + return offsetof(struct xfs_inode_t, di_crc); +} + + status_t Inode::Init() { @@ -162,7 +374,7 @@ Inode::Init() status_t status = GetFromDisk(); if (status == B_OK) { - if (fNode->di_magic == INODE_MAGIC) { + if (VerifyInode()) { TRACE("Init(): Inode successfully read.\n"); status = B_OK; } else { @@ -186,7 +398,11 @@ Inode::~Inode() bool Inode::HasFileTypeField() const { - return fVolume->SuperBlockFeatures2() & XFS_SB_VERSION2_FTYPE; + if((fNode->Version() == 3 && fVolume->XfsHasIncompatFeature()) + || (fVolume->SuperBlockFeatures2() & XFS_SB_VERSION2_FTYPE)) + return true; + + return false; } @@ -219,7 +435,7 @@ status_t Inode::ReadExtentsFromExtentBasedInode() { fExtents = new(std::nothrow) ExtentMapEntry[DataExtentsCount()]; - char* dataStart = (char*) DIR_DFORK_PTR(Buffer()); + char* dataStart = (char*) DIR_DFORK_PTR(Buffer(), CoreInodeSize()); uint64 wrappedExtent[2]; for (int i = 0; i < DataExtentsCount(); i++) { wrappedExtent[0] = *(uint64*)(dataStart); @@ -239,7 +455,7 @@ Inode::MaxRecordsPossibleInTreeRoot() lengthOfDataFork = ForkOffset() << 3; else if(ForkOffset() == 0) { lengthOfDataFork = GetVolume()->InodeSize() - - INODE_CORE_UNLINKED_SIZE; + - CoreInodeSize(); } lengthOfDataFork -= sizeof(BlockInDataFork); @@ -260,7 +476,7 @@ TreePointer* Inode::GetPtrFromRoot(int pos) { return (TreePointer*) - ((char*)DIR_DFORK_PTR(Buffer()) + GetPtrOffsetIntoRoot(pos)); + ((char*)DIR_DFORK_PTR(Buffer(), CoreInodeSize()) + GetPtrOffsetIntoRoot(pos)); } @@ -342,7 +558,7 @@ Inode::ReadExtentsFromTreeInode() if (root == NULL) return B_NO_MEMORY; memcpy((void*)root, - DIR_DFORK_PTR(Buffer()), sizeof(BlockInDataFork)); + DIR_DFORK_PTR(Buffer(), CoreInodeSize()), sizeof(BlockInDataFork)); size_t maxRecords = MaxRecordsPossibleInTreeRoot(); TRACE("Maxrecords: (%" B_PRIuSIZE ")\n", maxRecords); @@ -550,7 +766,11 @@ Inode::GetFromDisk() return B_IO_ERROR; } - memcpy(fNode, fBuffer, INODE_CORE_UNLINKED_SIZE); + if(fVolume->IsVersion5()) + memcpy(fNode, fBuffer, sizeof(xfs_inode_t)); + else + memcpy(fNode, fBuffer, INODE_CRC_OFF); + fNode->SwapEndian(); return B_OK; diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.h b/src/add-ons/kernel/file_systems/xfs/Inode.h index 052a8afe5f..a04d7e8460 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.h +++ b/src/add-ons/kernel/file_systems/xfs/Inode.h @@ -1,4 +1,5 @@ /* + * Copyright 2022, Raghav Sharma, raghavself28@gmail.com * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com * All rights reserved. Distributed under the terms of the MIT License. */ @@ -11,38 +12,80 @@ #include "xfs_types.h" -#define INODE_MAGIC 0x494e -#define INODE_MINSIZE_LOG 8 -#define INODE_MAXSIZE_LOG 11 -#define INODE_CORE_SIZE 96 -#define INODE_CORE_UNLINKED_SIZE 100 - // Inode core but with unlinked pointer -#define DATA_FORK_OFFSET 0x64 - // For v4 FS -#define INO_MASK(x) ((1ULL << (x)) - 1) +#define INODE_MAGIC 0x494e +#define INODE_MINSIZE_LOG 8 +#define INODE_MAXSIZE_LOG 11 +#define INODE_MIN_SIZE (1 << INODE_MINSIZE_LOG) +#define INODE_MAX_SIZE (1 << INODE_MAXSIZE_LOG) +#define INODE_CRC_OFF offsetof(struct xfs_inode_t, di_crc) +#define MAXAEXTNUM ((xfs_aextnum_t) 0x7fff) +#define MAXEXTNUM ((xfs_extnum_t) 0x7fffffff) + +// Does fs volume has v3 inodes? +#define HAS_V3INODES(volume) (volume->IsVersion5() ? 1 : 0 ) + +// Inode size for given fs +#define DINODE_SIZE(volume) \ + (HAS_V3INODES(volume) ? \ + sizeof(struct xfs_inode_t) : offsetof(struct xfs_inode_t, di_crc)) +#define LITINO(volume) \ + ((volume)->InodeSize() - DINODE_SIZE(volume)) + +// inode data and attribute fork sizes +#define DFORK_BOFF(ino) ((int)((ino)->di_forkoff << 3)) + +#define DFORK_DSIZE(ino, volume) \ + ((ino)->di_forkoff ? DFORK_BOFF(ino) : LITINO(volume)) +#define DFORK_ASIZE(ino, volume) \ + ((ino)->di_forkoff ? LITINO(volume) - DFORK_BOFF(ino) : 0) +#define DFORK_SIZE(ino, volume, w) \ + ((w) == XFS_DATA_FORK ? \ + DFORK_DSIZE(ino, volume) : DFORK_ASIZE(ino, volume)) + + +#define INO_MASK(x) ((1ULL << (x)) - 1) // Gets 2^x - 1 -#define INO_TO_AGNO(id, volume) (xfs_agnumber_t)id >> (volume->AgInodeBits()) +#define INO_TO_AGNO(id, volume) ((xfs_agnumber_t)id >> (volume->AgInodeBits())) // Gets AG number from inode number -#define INO_TO_AGINO(id, bits) (uint32) id & INO_MASK(bits); +#define INO_TO_AGINO(id, bits) ((uint32) id & INO_MASK(bits)) // Gets the AG relative inode number #define INO_TO_AGBLOCK(id, volume) \ (id >> (volume->InodesPerBlkLog())) \ & (INO_MASK(volume->AgBlocksLog())) // Gets the AG relative block number that contains inode -#define INO_TO_BLOCKOFFSET(id, volume) (id & INO_MASK(volume->InodesPerBlkLog())) +#define INO_TO_BLOCKOFFSET(id, volume) (id & INO_MASK(volume->InodesPerBlkLog())) // Gets the offset into the block from the inode number -#define DIR_DFORK_PTR(dir_ino_ptr) (void*) \ + +// Data and attribute fork pointers +#define DIR_DFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET) (void*) \ ((char*) dir_ino_ptr + DATA_FORK_OFFSET) -#define DIR_AFORK_PTR(dir_ino_ptr, forkoff) \ - (void*)((char*)DIR_DFORK_PTR(dir_ino_ptr) + \ +#define DIR_AFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET, forkoff) \ + (void*)((char*)DIR_DFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET) + \ (((uint32)forkoff)<<3)) -#define DIR_AFORK_EXIST(dir_ino_ptr) dir_ino_ptr->di_forkoff!=0 -#define MASK(n) ((1UL << n) - 1) -#define FSBLOCKS_TO_AGNO(n, volume) ((n) >> volume->AgBlocksLog()) -#define FSBLOCKS_TO_AGBLOCKNO(n, volume) ((n) & MASK(volume->AgBlocksLog())) + +#define DIR_AFORK_EXIST(dir_ino_ptr) (dir_ino_ptr->di_forkoff != 0) + +#define MASK(n) ((1UL << n) - 1) +#define FSBLOCKS_TO_AGNO(n, volume) ((n) >> volume->AgBlocksLog()) +#define FSBLOCKS_TO_AGBLOCKNO(n, volume) ((n) & MASK(volume->AgBlocksLog())) #define BLOCKNO_FROM_POSITION(n, volume) \ ((n) >> (volume->BlockLog())) -#define BLOCKOFFSET_FROM_POSITION(n, inode) ((n) & (inode->BlockSize() - 1)) +#define BLOCKOFFSET_FROM_POSITION(n, inode) ((n) & (inode->BlockSize() - 1)) + +#define MAXINUMBER ((xfs_ino_t)((1ULL << 56) - 1ULL)) + +// Inode fork identifiers +#define XFS_DATA_FORK 0 +#define XFS_ATTR_FORK 1 + +#define XFS_DFORK_FORMAT(ino, w) \ + ((w) == XFS_DATA_FORK ? (ino)->di_format : (ino)->di_aformat) + +#define XFS_DFORK_NEXTENTS(ino, w) \ + ((w) == XFS_DATA_FORK ? (ino)->di_nextents : (ino)->di_anextents) + +#define DFORK_MAXEXT(ino, volume, w) \ + (DFORK_SIZE(ino, volume, w) / (2 * sizeof(uint64))) // xfs_bmdr_block @@ -102,6 +145,33 @@ enum xfs_dinode_fmt_t { // Not used }; + +#define XFS_INODE_FORMAT_STR \ + { XFS_DINODE_FMT_DEV, "dev" }, \ + { XFS_DINODE_FMT_LOCAL, "local" }, \ + { XFS_DINODE_FMT_EXTENTS, "extent" }, \ + { XFS_DINODE_FMT_BTREE, "btree" }, \ + { XFS_DINODE_FMT_UUID, "uuid" } + + +/* + Dirents in version 3 directories have a file type field. Additions to this + list are an on-disk format change, requiring feature bits. Valid values + are as follows: +*/ +#define XFS_DIR3_FT_UNKNOWN 0 +#define XFS_DIR3_FT_REG_FILE 1 +#define XFS_DIR3_FT_DIR 2 +#define XFS_DIR3_FT_CHRDEV 3 +#define XFS_DIR3_FT_BLKDEV 4 +#define XFS_DIR3_FT_FIFO 5 +#define XFS_DIR3_FT_SOCK 6 +#define XFS_DIR3_FT_SYMLINK 7 +#define XFS_DIR3_FT_WHT 8 + +#define XFS_DIR3_FT_MAX 9 + + /* * The xfs_ino_t is the same for all types of inodes, the data and attribute * fork might be different and that is to be handled accordingly. @@ -114,6 +184,8 @@ struct xfs_inode_t { timestamp); void GetChangeTime(struct timespec& timestamp); void GetAccessTime(struct timespec& timestamp); + void GetCreationTime(struct timespec& timestamp); + int8 Format() const; // The format of the inode xfs_fsize_t Size() const; @@ -160,17 +232,97 @@ struct xfs_inode_t { uint16 di_flags; uint32 di_gen; uint32 di_next_unlinked; + + // XFS Version 5 + + uint32 di_crc; + uint64 di_changecount; + uint64 di_lsn; + uint64 di_flags2; + uint32 di_cowextsize; + uint8 di_pad2[12]; + + // fields only written to during inode creation + + xfs_timestamp_t di_crtime; + uint64 di_ino; + uuid_t di_uuid; }; +// Values for di_flags +#define XFS_DIFLAG_REALTIME_BIT 0 // file's blocks come from rt area +#define XFS_DIFLAG_PREALLOC_BIT 1 // file space has been preallocated +#define XFS_DIFLAG_NEWRTBM_BIT 2 // for rtbitmap inode, new format +#define XFS_DIFLAG_IMMUTABLE_BIT 3 // inode is immutable +#define XFS_DIFLAG_APPEND_BIT 4 // inode is append-only +#define XFS_DIFLAG_SYNC_BIT 5 // inode is written synchronously +#define XFS_DIFLAG_NOATIME_BIT 6 // do not update atime +#define XFS_DIFLAG_NODUMP_BIT 7 // do not dump +#define XFS_DIFLAG_RTINHERIT_BIT 8 // create with realtime bit set +#define XFS_DIFLAG_PROJINHERIT_BIT 9 // create with parents projid +#define XFS_DIFLAG_NOSYMLINKS_BIT 10 // disallow symlink creation +#define XFS_DIFLAG_EXTSIZE_BIT 11 // inode extent size allocator hint +#define XFS_DIFLAG_EXTSZINHERIT_BIT 12 // inherit inode extent size +#define XFS_DIFLAG_NODEFRAG_BIT 13 // do not reorganize/defragment +#define XFS_DIFLAG_FILESTREAM_BIT 14 // use filestream allocator + +#define XFS_DIFLAG_REALTIME (1 << XFS_DIFLAG_REALTIME_BIT) +#define XFS_DIFLAG_PREALLOC (1 << XFS_DIFLAG_PREALLOC_BIT) +#define XFS_DIFLAG_NEWRTBM (1 << XFS_DIFLAG_NEWRTBM_BIT) +#define XFS_DIFLAG_IMMUTABLE (1 << XFS_DIFLAG_IMMUTABLE_BIT) +#define XFS_DIFLAG_APPEND (1 << XFS_DIFLAG_APPEND_BIT) +#define XFS_DIFLAG_SYNC (1 << XFS_DIFLAG_SYNC_BIT) +#define XFS_DIFLAG_NOATIME (1 << XFS_DIFLAG_NOATIME_BIT) +#define XFS_DIFLAG_NODUMP (1 << XFS_DIFLAG_NODUMP_BIT) +#define XFS_DIFLAG_RTINHERIT (1 << XFS_DIFLAG_RTINHERIT_BIT) +#define XFS_DIFLAG_PROJINHERIT (1 << XFS_DIFLAG_PROJINHERIT_BIT) +#define XFS_DIFLAG_NOSYMLINKS (1 << XFS_DIFLAG_NOSYMLINKS_BIT) +#define XFS_DIFLAG_EXTSIZE (1 << XFS_DIFLAG_EXTSIZE_BIT) +#define XFS_DIFLAG_EXTSZINHERIT (1 << XFS_DIFLAG_EXTSZINHERIT_BIT) +#define XFS_DIFLAG_NODEFRAG (1 << XFS_DIFLAG_NODEFRAG_BIT) +#define XFS_DIFLAG_FILESTREAM (1 << XFS_DIFLAG_FILESTREAM_BIT) + +#define XFS_DIFLAG_ANY \ + (XFS_DIFLAG_REALTIME | XFS_DIFLAG_PREALLOC | XFS_DIFLAG_NEWRTBM | \ + XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND | XFS_DIFLAG_SYNC | \ + XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | XFS_DIFLAG_RTINHERIT | \ + XFS_DIFLAG_PROJINHERIT | XFS_DIFLAG_NOSYMLINKS | XFS_DIFLAG_EXTSIZE | \ + XFS_DIFLAG_EXTSZINHERIT | XFS_DIFLAG_NODEFRAG | XFS_DIFLAG_FILESTREAM) + +/* + Values for di_flags2 These start by being exposed to userspace in the upper + 16 bits of the XFS_XFLAG_S range. +*/ +#define XFS_DIFLAG2_DAX_BIT 0 // use DAX for this inode +#define XFS_DIFLAG2_REFLINK_BIT 1 // file's blocks may be shared +#define XFS_DIFLAG2_COWEXTSIZE_BIT 2 // copy on write extent size hint +#define XFS_DIFLAG2_BIGTIME_BIT 3 // big timestamps + +#define XFS_DIFLAG2_DAX (1 << XFS_DIFLAG2_DAX_BIT) +#define XFS_DIFLAG2_REFLINK (1 << XFS_DIFLAG2_REFLINK_BIT) +#define XFS_DIFLAG2_COWEXTSIZE (1 << XFS_DIFLAG2_COWEXTSIZE_BIT) +#define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) + +#define XFS_DIFLAG2_ANY \ + (XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \ + XFS_DIFLAG2_BIGTIME) + class Inode { public: Inode(Volume* volume, xfs_ino_t id); ~Inode(); + status_t Init(); xfs_ino_t ID() const { return fId; } + bool VerifyInode() const; + + bool VerifyForkoff() const; + + bool VerifyFork(int WhichFork) const; + bool IsDirectory() const { return S_ISDIR(Mode()); } @@ -209,6 +361,8 @@ public: uint32 BlockSize() const { return fVolume->BlockSize(); } + uint32 CoreInodeSize() const; + void GetChangeTime(struct timespec& timestamp) const { fNode->GetChangeTime(timestamp); } @@ -219,6 +373,10 @@ public: void GetAccessTime(struct timespec& timestamp) const { fNode->GetAccessTime(timestamp); } + void GetCreationTime(struct timespec& timestamp) const + { fNode->GetCreationTime(timestamp); } + + unsigned char XfsModeToFtype() const; status_t CheckPermissions(int accessMode) const; uint32 UserId() const { return fNode->UserId(); } uint32 GroupId() const { return fNode->GroupId(); } diff --git a/src/add-ons/kernel/file_systems/xfs/LeafDirectory.cpp b/src/add-ons/kernel/file_systems/xfs/LeafDirectory.cpp index 7359a93d77..7c8b1c32ac 100644 --- a/src/add-ons/kernel/file_systems/xfs/LeafDirectory.cpp +++ b/src/add-ons/kernel/file_systems/xfs/LeafDirectory.cpp @@ -74,7 +74,7 @@ LeafDirectory::IsLeafType() void LeafDirectory::FillMapEntry(int num, ExtentMapEntry* fMap) { - void* directoryFork = DIR_DFORK_PTR(fInode->Buffer()); + void* directoryFork = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()); uint64* pointerToMap = (uint64*)((char*)directoryFork + num * EXTENT_SIZE); uint64 firstHalf = pointerToMap[0]; diff --git a/src/add-ons/kernel/file_systems/xfs/Node.cpp b/src/add-ons/kernel/file_systems/xfs/Node.cpp index c2f983d876..ed854d368e 100644 --- a/src/add-ons/kernel/file_systems/xfs/Node.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Node.cpp @@ -62,7 +62,7 @@ NodeDirectory::IsNodeType() void NodeDirectory::FillMapEntry(int num, ExtentMapEntry* fMap) { - void* directoryFork = DIR_DFORK_PTR(fInode->Buffer()); + void* directoryFork = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()); void* pointerToMap = (void*)((char*)directoryFork + num * EXTENT_SIZE); uint64 firstHalf = *((uint64*)pointerToMap); uint64 secondHalf = *((uint64*)pointerToMap + 1); diff --git a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp index 50b8e113ed..dab97f5be5 100644 --- a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp +++ b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp @@ -12,8 +12,7 @@ ShortDirectory::ShortDirectory(Inode* inode) fInode(inode), fTrack(0) { - - fHeader = (ShortFormHeader*)(DIR_DFORK_PTR(fInode->Buffer())); + fHeader = (ShortFormHeader*)(DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize())); } @@ -72,8 +71,8 @@ size_t ShortDirectory::EntrySize(int namelen) { return sizeof(ShortFormEntry) + namelen - + (fInode->HasFileTypeField()? sizeof(uint8) : 0) - + (fHeader->i8count? sizeof(uint64):sizeof(uint32)); + + (fInode->HasFileTypeField() ? sizeof(uint8) : 0) + + (fHeader->i8count ? sizeof(uint64) : sizeof(uint32)); } @@ -155,7 +154,6 @@ ShortDirectory::GetNext(char* name, size_t* length, xfs_ino_t* ino) name[entry->namelen] = '\0'; *length = entry->namelen + 1; *ino = GetEntryIno(entry); - TRACE("Entry found. Name: (%s), Length: (%" B_PRIuSIZE "),ino: (%" B_PRIu64 ")\n", name,*length, *ino); return B_OK; diff --git a/src/add-ons/kernel/file_systems/xfs/Volume.h b/src/add-ons/kernel/file_systems/xfs/Volume.h index a213451ff0..d5a7fb2cea 100644 --- a/src/add-ons/kernel/file_systems/xfs/Volume.h +++ b/src/add-ons/kernel/file_systems/xfs/Volume.h @@ -31,6 +31,8 @@ public: uint32 blockSize, uint32 sectorSize); bool IsValidSuperBlock() const; + bool IsVersion5() const + { return fSuperBlock.IsVersion5(); } bool IsReadOnly() const { return (fFlags & VOLUME_READ_ONLY) != 0; } @@ -85,6 +87,9 @@ public: uint32 SuperBlockFeatures2() const { return fSuperBlock.Features2(); } + bool XfsHasIncompatFeature() const + { return fSuperBlock.XfsHasIncompatFeature(); } + #if 0 off_t NumBlocks() const { return fSuperBlock.NumBlocks(); } diff --git a/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp index 9aec919404..0d200f57a2 100644 --- a/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp @@ -251,11 +251,13 @@ xfs_read_stat(fs_volume *_volume, fs_vnode *_node, struct stat *stat) inode->GetModificationTime(stat->st_mtim); inode->GetChangeTime(stat->st_ctim); - /* TODO: Can we obtain the Creation Time in v4 system? */ - inode->GetChangeTime(stat->st_crtim); + // Only version 3 Inodes has creation time + if(inode->Version() == 3) + inode->GetCreationTime(stat->st_crtim); + else + inode->GetChangeTime(stat->st_crtim); return B_OK; - } diff --git a/src/add-ons/kernel/file_systems/xfs/xfs.cpp b/src/add-ons/kernel/file_systems/xfs/xfs.cpp index 2f43f882c8..ced32b76d5 100644 --- a/src/add-ons/kernel/file_systems/xfs/xfs.cpp +++ b/src/add-ons/kernel/file_systems/xfs/xfs.cpp @@ -74,6 +74,7 @@ XfsSuperBlock::IsValidFeatureMask() const return true; } + bool XfsSuperBlock::IsValid() const { @@ -154,6 +155,20 @@ XfsSuperBlock::IsValid() const } +bool +XfsSuperBlock::IsVersion5() const +{ + return (Version() & XFS_SB_VERSION_NUMBITS) == 5; +} + + +bool +XfsSuperBlock::XfsHasIncompatFeature() const +{ + return (sb_features_incompat & XFS_SB_FEAT_INCOMPAT_FTYPE) != 0; +} + + uint16 XfsSuperBlock::Version() const { diff --git a/src/add-ons/kernel/file_systems/xfs/xfs.h b/src/add-ons/kernel/file_systems/xfs/xfs.h index e132fe65e4..4f9134bd6e 100644 --- a/src/add-ons/kernel/file_systems/xfs/xfs.h +++ b/src/add-ons/kernel/file_systems/xfs/xfs.h @@ -39,7 +39,6 @@ extern fs_volume_ops gxfsVolumeOps; #define XFS_OPEN_MODE_USER_MASK 0x7fffffff #define XFS_SB_VERSION_NUMBITS 0x000f #define XFS_SB_VERSION_ALLFBITS 0xfff0 -#define XFS_SB_VERSION_NUM(sb) ((sb)->sb_versionnum & XFS_SB_VERSION_NUMBITS) /* Inode minimum and maximum sizes. @@ -73,6 +72,8 @@ public: bool IsValid() const; bool IsValidVersion() const; bool IsValidFeatureMask() const; + bool IsVersion5() const; + bool XfsHasIncompatFeature() const; const char* Name() const; uint32 BlockSize() const; uint8 BlockLog() const;