xfs: version 3 Inodes and shortform directory

- Implemented xfs v5 inodes (known as v3 inodes) and necessary metadata integrity checks

- Implemented correct data fork pointer which handles both xfs v4 and v5 directories

- We can now read inodes and shortform directories for both xfs versions completely fine

Change-Id: I8a75ec1dc663d567d3bf6db64be4a27b55d709b3
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5396
Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
Mashijams 2022-06-24 14:49:37 +05:30 committed by Adrien Destugues
parent e511f0c1cb
commit 6c478b54f6
11 changed files with 443 additions and 43 deletions

View File

@ -32,7 +32,7 @@ TreeDirectory::TreeDirectory(Inode* inode)
} }
memcpy((void*)fRoot, 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++) { for (int i = 0; i < MAX_TREE_DEPTH; i++) {
fPathForLeaves[i].blockData = NULL; fPathForLeaves[i].blockData = NULL;
@ -85,7 +85,7 @@ TreeDirectory::MaxRecordsPossibleRoot()
lengthOfDataFork = fInode->ForkOffset() << 3; lengthOfDataFork = fInode->ForkOffset() << 3;
if (fInode->ForkOffset() == 0) { if (fInode->ForkOffset() == 0) {
lengthOfDataFork = fInode->GetVolume()->InodeSize() lengthOfDataFork = fInode->GetVolume()->InodeSize()
- INODE_CORE_UNLINKED_SIZE; - fInode->CoreInodeSize();
} }
lengthOfDataFork -= sizeof(BlockInDataFork); lengthOfDataFork -= sizeof(BlockInDataFork);
@ -122,7 +122,8 @@ TreePointer*
TreeDirectory::GetPtrFromRoot(int pos) TreeDirectory::GetPtrFromRoot(int pos)
{ {
return (TreePointer*) 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) TreeDirectory::GetKeyFromRoot(int pos)
{ {
off_t offset = (pos - 1) * KeySize(); 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); + sizeof(BlockInDataFork);
return (TreeKey*) (base + offset); return (TreeKey*) (base + offset);
} }

View File

@ -70,7 +70,7 @@ Extent::Init()
return B_NO_MEMORY; return B_NO_MEMORY;
ASSERT(IsBlockType() == true); ASSERT(IsBlockType() == true);
void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer()); void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize());
FillMapEntry(pointerToMap); FillMapEntry(pointerToMap);
ASSERT(fMap->br_blockcount == 1); ASSERT(fMap->br_blockcount == 1);
//TODO: This is always true for block directories //TODO: This is always true for block directories

View File

@ -1,4 +1,5 @@
/* /*
* Copyright 2022, Raghav Sharma, raghavself28@gmail.com
* Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@ -6,6 +7,7 @@
#include "Inode.h" #include "Inode.h"
#include "BPlusTree.h" #include "BPlusTree.h"
#include "Checksum.h"
void void
xfs_inode_t::SwapEndian() xfs_inode_t::SwapEndian()
@ -34,6 +36,13 @@ xfs_inode_t::SwapEndian()
di_flags = B_BENDIAN_TO_HOST_INT16(di_flags); di_flags = B_BENDIAN_TO_HOST_INT16(di_flags);
di_gen = B_BENDIAN_TO_HOST_INT32(di_gen); di_gen = B_BENDIAN_TO_HOST_INT32(di_gen);
di_next_unlinked = B_BENDIAN_TO_HOST_INT32(di_next_unlinked); 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 uint32
xfs_inode_t::NLink() const 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 status_t
Inode::Init() Inode::Init()
{ {
@ -162,7 +374,7 @@ Inode::Init()
status_t status = GetFromDisk(); status_t status = GetFromDisk();
if (status == B_OK) { if (status == B_OK) {
if (fNode->di_magic == INODE_MAGIC) { if (VerifyInode()) {
TRACE("Init(): Inode successfully read.\n"); TRACE("Init(): Inode successfully read.\n");
status = B_OK; status = B_OK;
} else { } else {
@ -186,7 +398,11 @@ Inode::~Inode()
bool bool
Inode::HasFileTypeField() const 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() Inode::ReadExtentsFromExtentBasedInode()
{ {
fExtents = new(std::nothrow) ExtentMapEntry[DataExtentsCount()]; fExtents = new(std::nothrow) ExtentMapEntry[DataExtentsCount()];
char* dataStart = (char*) DIR_DFORK_PTR(Buffer()); char* dataStart = (char*) DIR_DFORK_PTR(Buffer(), CoreInodeSize());
uint64 wrappedExtent[2]; uint64 wrappedExtent[2];
for (int i = 0; i < DataExtentsCount(); i++) { for (int i = 0; i < DataExtentsCount(); i++) {
wrappedExtent[0] = *(uint64*)(dataStart); wrappedExtent[0] = *(uint64*)(dataStart);
@ -239,7 +455,7 @@ Inode::MaxRecordsPossibleInTreeRoot()
lengthOfDataFork = ForkOffset() << 3; lengthOfDataFork = ForkOffset() << 3;
else if(ForkOffset() == 0) { else if(ForkOffset() == 0) {
lengthOfDataFork = GetVolume()->InodeSize() lengthOfDataFork = GetVolume()->InodeSize()
- INODE_CORE_UNLINKED_SIZE; - CoreInodeSize();
} }
lengthOfDataFork -= sizeof(BlockInDataFork); lengthOfDataFork -= sizeof(BlockInDataFork);
@ -260,7 +476,7 @@ TreePointer*
Inode::GetPtrFromRoot(int pos) Inode::GetPtrFromRoot(int pos)
{ {
return (TreePointer*) 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) if (root == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
memcpy((void*)root, memcpy((void*)root,
DIR_DFORK_PTR(Buffer()), sizeof(BlockInDataFork)); DIR_DFORK_PTR(Buffer(), CoreInodeSize()), sizeof(BlockInDataFork));
size_t maxRecords = MaxRecordsPossibleInTreeRoot(); size_t maxRecords = MaxRecordsPossibleInTreeRoot();
TRACE("Maxrecords: (%" B_PRIuSIZE ")\n", maxRecords); TRACE("Maxrecords: (%" B_PRIuSIZE ")\n", maxRecords);
@ -550,7 +766,11 @@ Inode::GetFromDisk()
return B_IO_ERROR; 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(); fNode->SwapEndian();
return B_OK; return B_OK;

View File

@ -1,4 +1,5 @@
/* /*
* Copyright 2022, Raghav Sharma, raghavself28@gmail.com
* Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@ -11,38 +12,80 @@
#include "xfs_types.h" #include "xfs_types.h"
#define INODE_MAGIC 0x494e #define INODE_MAGIC 0x494e
#define INODE_MINSIZE_LOG 8 #define INODE_MINSIZE_LOG 8
#define INODE_MAXSIZE_LOG 11 #define INODE_MAXSIZE_LOG 11
#define INODE_CORE_SIZE 96 #define INODE_MIN_SIZE (1 << INODE_MINSIZE_LOG)
#define INODE_CORE_UNLINKED_SIZE 100 #define INODE_MAX_SIZE (1 << INODE_MAXSIZE_LOG)
// Inode core but with unlinked pointer #define INODE_CRC_OFF offsetof(struct xfs_inode_t, di_crc)
#define DATA_FORK_OFFSET 0x64 #define MAXAEXTNUM ((xfs_aextnum_t) 0x7fff)
// For v4 FS #define MAXEXTNUM ((xfs_extnum_t) 0x7fffffff)
#define INO_MASK(x) ((1ULL << (x)) - 1)
// 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 // 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 // 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 // Gets the AG relative inode number
#define INO_TO_AGBLOCK(id, volume) \ #define INO_TO_AGBLOCK(id, volume) \
(id >> (volume->InodesPerBlkLog())) \ (id >> (volume->InodesPerBlkLog())) \
& (INO_MASK(volume->AgBlocksLog())) & (INO_MASK(volume->AgBlocksLog()))
// Gets the AG relative block number that contains inode // 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 // 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) ((char*) dir_ino_ptr + DATA_FORK_OFFSET)
#define DIR_AFORK_PTR(dir_ino_ptr, forkoff) \ #define DIR_AFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET, forkoff) \
(void*)((char*)DIR_DFORK_PTR(dir_ino_ptr) + \ (void*)((char*)DIR_DFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET) + \
(((uint32)forkoff)<<3)) (((uint32)forkoff)<<3))
#define DIR_AFORK_EXIST(dir_ino_ptr) dir_ino_ptr->di_forkoff!=0
#define MASK(n) ((1UL << n) - 1) #define DIR_AFORK_EXIST(dir_ino_ptr) (dir_ino_ptr->di_forkoff != 0)
#define FSBLOCKS_TO_AGNO(n, volume) ((n) >> volume->AgBlocksLog())
#define FSBLOCKS_TO_AGBLOCKNO(n, volume) ((n) & MASK(volume->AgBlocksLog())) #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) \ #define BLOCKNO_FROM_POSITION(n, volume) \
((n) >> (volume->BlockLog())) ((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 // xfs_bmdr_block
@ -102,6 +145,33 @@ enum xfs_dinode_fmt_t {
// Not used // 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 * 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. * fork might be different and that is to be handled accordingly.
@ -114,6 +184,8 @@ struct xfs_inode_t {
timestamp); timestamp);
void GetChangeTime(struct timespec& timestamp); void GetChangeTime(struct timespec& timestamp);
void GetAccessTime(struct timespec& timestamp); void GetAccessTime(struct timespec& timestamp);
void GetCreationTime(struct timespec& timestamp);
int8 Format() const; int8 Format() const;
// The format of the inode // The format of the inode
xfs_fsize_t Size() const; xfs_fsize_t Size() const;
@ -160,17 +232,97 @@ struct xfs_inode_t {
uint16 di_flags; uint16 di_flags;
uint32 di_gen; uint32 di_gen;
uint32 di_next_unlinked; 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 { class Inode {
public: public:
Inode(Volume* volume, xfs_ino_t id); Inode(Volume* volume, xfs_ino_t id);
~Inode(); ~Inode();
status_t Init(); status_t Init();
xfs_ino_t ID() const { return fId; } xfs_ino_t ID() const { return fId; }
bool VerifyInode() const;
bool VerifyForkoff() const;
bool VerifyFork(int WhichFork) const;
bool IsDirectory() const bool IsDirectory() const
{ return S_ISDIR(Mode()); } { return S_ISDIR(Mode()); }
@ -209,6 +361,8 @@ public:
uint32 BlockSize() const uint32 BlockSize() const
{ return fVolume->BlockSize(); } { return fVolume->BlockSize(); }
uint32 CoreInodeSize() const;
void GetChangeTime(struct timespec& timestamp) const void GetChangeTime(struct timespec& timestamp) const
{ fNode->GetChangeTime(timestamp); } { fNode->GetChangeTime(timestamp); }
@ -219,6 +373,10 @@ public:
void GetAccessTime(struct timespec& timestamp) const void GetAccessTime(struct timespec& timestamp) const
{ fNode->GetAccessTime(timestamp); } { fNode->GetAccessTime(timestamp); }
void GetCreationTime(struct timespec& timestamp) const
{ fNode->GetCreationTime(timestamp); }
unsigned char XfsModeToFtype() const;
status_t CheckPermissions(int accessMode) const; status_t CheckPermissions(int accessMode) const;
uint32 UserId() const { return fNode->UserId(); } uint32 UserId() const { return fNode->UserId(); }
uint32 GroupId() const { return fNode->GroupId(); } uint32 GroupId() const { return fNode->GroupId(); }

View File

@ -74,7 +74,7 @@ LeafDirectory::IsLeafType()
void void
LeafDirectory::FillMapEntry(int num, ExtentMapEntry* fMap) 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* pointerToMap = (uint64*)((char*)directoryFork + num * EXTENT_SIZE);
uint64 firstHalf = pointerToMap[0]; uint64 firstHalf = pointerToMap[0];

View File

@ -62,7 +62,7 @@ NodeDirectory::IsNodeType()
void void
NodeDirectory::FillMapEntry(int num, ExtentMapEntry* fMap) 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); void* pointerToMap = (void*)((char*)directoryFork + num * EXTENT_SIZE);
uint64 firstHalf = *((uint64*)pointerToMap); uint64 firstHalf = *((uint64*)pointerToMap);
uint64 secondHalf = *((uint64*)pointerToMap + 1); uint64 secondHalf = *((uint64*)pointerToMap + 1);

View File

@ -12,8 +12,7 @@ ShortDirectory::ShortDirectory(Inode* inode)
fInode(inode), fInode(inode),
fTrack(0) fTrack(0)
{ {
fHeader = (ShortFormHeader*)(DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()));
fHeader = (ShortFormHeader*)(DIR_DFORK_PTR(fInode->Buffer()));
} }
@ -72,8 +71,8 @@ size_t
ShortDirectory::EntrySize(int namelen) ShortDirectory::EntrySize(int namelen)
{ {
return sizeof(ShortFormEntry) + namelen return sizeof(ShortFormEntry) + namelen
+ (fInode->HasFileTypeField()? sizeof(uint8) : 0) + (fInode->HasFileTypeField() ? sizeof(uint8) : 0)
+ (fHeader->i8count? sizeof(uint64):sizeof(uint32)); + (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'; name[entry->namelen] = '\0';
*length = entry->namelen + 1; *length = entry->namelen + 1;
*ino = GetEntryIno(entry); *ino = GetEntryIno(entry);
TRACE("Entry found. Name: (%s), Length: (%" B_PRIuSIZE "),ino: (%" B_PRIu64 ")\n", TRACE("Entry found. Name: (%s), Length: (%" B_PRIuSIZE "),ino: (%" B_PRIu64 ")\n",
name,*length, *ino); name,*length, *ino);
return B_OK; return B_OK;

View File

@ -31,6 +31,8 @@ public:
uint32 blockSize, uint32 sectorSize); uint32 blockSize, uint32 sectorSize);
bool IsValidSuperBlock() const; bool IsValidSuperBlock() const;
bool IsVersion5() const
{ return fSuperBlock.IsVersion5(); }
bool IsReadOnly() const bool IsReadOnly() const
{ return { return
(fFlags & VOLUME_READ_ONLY) != 0; } (fFlags & VOLUME_READ_ONLY) != 0; }
@ -85,6 +87,9 @@ public:
uint32 SuperBlockFeatures2() const uint32 SuperBlockFeatures2() const
{ return fSuperBlock.Features2(); } { return fSuperBlock.Features2(); }
bool XfsHasIncompatFeature() const
{ return fSuperBlock.XfsHasIncompatFeature(); }
#if 0 #if 0
off_t NumBlocks() const off_t NumBlocks() const
{ return fSuperBlock.NumBlocks(); } { return fSuperBlock.NumBlocks(); }

View File

@ -251,11 +251,13 @@ xfs_read_stat(fs_volume *_volume, fs_vnode *_node, struct stat *stat)
inode->GetModificationTime(stat->st_mtim); inode->GetModificationTime(stat->st_mtim);
inode->GetChangeTime(stat->st_ctim); inode->GetChangeTime(stat->st_ctim);
/* TODO: Can we obtain the Creation Time in v4 system? */ // Only version 3 Inodes has creation time
inode->GetChangeTime(stat->st_crtim); if(inode->Version() == 3)
inode->GetCreationTime(stat->st_crtim);
else
inode->GetChangeTime(stat->st_crtim);
return B_OK; return B_OK;
} }

View File

@ -74,6 +74,7 @@ XfsSuperBlock::IsValidFeatureMask() const
return true; return true;
} }
bool bool
XfsSuperBlock::IsValid() const 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 uint16
XfsSuperBlock::Version() const XfsSuperBlock::Version() const
{ {

View File

@ -39,7 +39,6 @@ extern fs_volume_ops gxfsVolumeOps;
#define XFS_OPEN_MODE_USER_MASK 0x7fffffff #define XFS_OPEN_MODE_USER_MASK 0x7fffffff
#define XFS_SB_VERSION_NUMBITS 0x000f #define XFS_SB_VERSION_NUMBITS 0x000f
#define XFS_SB_VERSION_ALLFBITS 0xfff0 #define XFS_SB_VERSION_ALLFBITS 0xfff0
#define XFS_SB_VERSION_NUM(sb) ((sb)->sb_versionnum & XFS_SB_VERSION_NUMBITS)
/* /*
Inode minimum and maximum sizes. Inode minimum and maximum sizes.
@ -73,6 +72,8 @@ public:
bool IsValid() const; bool IsValid() const;
bool IsValidVersion() const; bool IsValidVersion() const;
bool IsValidFeatureMask() const; bool IsValidFeatureMask() const;
bool IsVersion5() const;
bool XfsHasIncompatFeature() const;
const char* Name() const; const char* Name() const;
uint32 BlockSize() const; uint32 BlockSize() const;
uint8 BlockLog() const; uint8 BlockLog() const;