⛏️ index : haiku.git

/*
 * 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.
 */
#ifndef _INODE_H_
#define _INODE_H_


#include "system_dependencies.h"
#include "Volume.h"
#include "xfs_types.h"


#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(Inode::Dinode, 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(Inode::Dinode) : offsetof(Inode::Dinode, 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()))
	// Gets AG number from inode number
#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()))
	// Gets the offset into the block from the inode number

// 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, DATA_FORK_OFFSET, forkoff) \
					(void*)((char*)DIR_DFORK_PTR(dir_ino_ptr, DATA_FORK_OFFSET) + \
					(((uint32)forkoff)<<3))

#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))


// 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_naextents)

#define DFORK_MAXEXT(ino, volume, w) \
	(DFORK_SIZE(ino, volume, w) / (2 * sizeof(uint64)))


struct LongBlock;  // Forward declaration to remove cyclic dependency


// xfs_bmdr_block
struct BlockInDataFork {
			uint16				Levels()
									{ return
										B_BENDIAN_TO_HOST_INT16(bb_level); }
			uint16				NumRecords()
									{ return
										B_BENDIAN_TO_HOST_INT16(bb_numrecs); }
			uint16				bb_level;
			uint16				bb_numrecs;
};


//xfs_da_blkinfo_t
struct BlockInfo {
			uint32				forw;
			uint32				back;
			uint16				magic;
			uint16				pad;
};


// xfs_da3_blkinfo_t
struct BlockInfoV5 {
			uint32				forw;
			uint32				back;
			uint16				magic;
			uint16				pad;

			// version 5

			uint32				crc;
			uint64				blkno;
			uint64				lsn;
			uuid_t				uuid;
			uint64				owner;
};

#define XFS_BLOCK_CRC_OFF offsetof(struct BlockInfoV5, crc)


struct ExtentMapEntry {
			xfs_fileoff_t		br_startoff;
				// logical file block offset
			xfs_fsblock_t		br_startblock;
				// absolute block number
			xfs_filblks_t		br_blockcount;
				// # of blocks
			uint8				br_state;
				// state of the extent
};


struct xfs_timestamp_t {
		int32				t_sec;
		int32				t_nsec;
};


enum xfs_dinode_fmt_t {
		XFS_DINODE_FMT_DEV,
			// For devices
		XFS_DINODE_FMT_LOCAL,
			// For Directories and links
		XFS_DINODE_FMT_EXTENTS,
			// For large number of extents
		XFS_DINODE_FMT_BTREE,
			// Extents and Directories
		XFS_DINODE_FMT_UUID,
			// Not used
};


/*
	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 dinode is the same for all types of inodes, the data and attribute
 * fork might be different and that is to be handled accordingly.
 */
class Inode {
public:
	typedef struct Dinode{
	public:
		uint16				di_magic;
		uint16				di_mode;
		// uses standard S_Ixxx
		int8				di_version;
		//This either would be 1 or 2
		int8				di_format;
		uint16				di_onlink;
		uint32				di_uid;
		uint32				di_gid;
		uint32				di_nlink;
		uint16				di_projid;
		uint8				di_pad[8];
		uint16				di_flushiter;
		xfs_timestamp_t		di_atime;
		xfs_timestamp_t		di_mtime;
		xfs_timestamp_t		di_ctime;
		xfs_fsize_t			di_size;
		// size in bytes or length if link
		xfs_rfsblock_t		di_nblocks;
		// blocks used including metadata
		// extended attributes not included
		xfs_extlen_t		di_extsize;
		// extent size
		xfs_extnum_t		di_nextents;
		// number of data extents
		xfs_aextnum_t		di_naextents;
		// number of EA extents
		uint8				di_forkoff;
		// decides where di_a starts
		int8				di_aformat;
		// similar to di_format
		uint32				di_dmevmask;
		uint16				di_dmstate;
		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;
	};

								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()); }

			bool				IsFile() const
									{ return S_ISREG(Mode()); }

			bool				IsSymLink() const
									{ return S_ISLNK(Mode()); }

			mode_t				Mode() const { return fNode->di_mode; }

			Volume*				GetVolume() { return fVolume;}

			int8				Format() const { return fNode->di_format; }

			int8				AttrFormat() const { return fNode->di_aformat; }

			bool				IsLocal() const
									{ return Format() == XFS_DINODE_FMT_LOCAL; }

			uint32				NLink() const { return fNode->di_nlink; }

			int8				Version() const { return fNode->di_version; }

			xfs_rfsblock_t		BlockCount() const
									{ return fNode->di_nblocks; }

			char*				Buffer() { return fBuffer; }

			int16				Flags() const { return fNode->di_flags; }

			xfs_fsize_t			Size() const { return fNode->di_size; }

			uint32				DirBlockSize() const
									{ return fVolume->DirBlockSize(); }

			uint32				BlockSize() const
									{ return fVolume->BlockSize(); }

			uint32				CoreInodeSize() const;

			void				GetChangeTime(struct timespec& timestamp) const;

			void				GetModificationTime(struct timespec& timestamp) const;

			void				GetAccessTime(struct timespec& timestamp) const;

			void				GetCreationTime(struct timespec& timestamp) const;

			unsigned char		XfsModeToFtype() const;
			status_t			CheckPermissions(int accessMode) const;
			uint32				UserId() const { return fNode->di_uid; }
			uint32				GroupId() const { return fNode->di_gid; }
			bool				HasFileTypeField() const;
			xfs_extnum_t		DataExtentsCount() const
									{ return fNode->di_nextents; }
			xfs_extnum_t		AttrExtentsCount() const
									{ return fNode->di_naextents; }
			uint64				FileSystemBlockToAddr(uint64 block);
			uint8				ForkOffset() const
									{ return fNode->di_forkoff; }
			status_t			ReadExtents();
			status_t			ReadAt(off_t pos, uint8* buffer, size_t* length);
			status_t			GetNodefromTree(uint16& levelsInTree,
									Volume* volume, ssize_t& len,
									size_t DirBlockSize, char* block);
			int					SearchMapInAllExtent(uint64 blockNo);
			void				UnWrapExtentFromWrappedEntry(
									uint64 wrappedExtent[2],
									ExtentMapEntry* entry);
			status_t			ReadExtentsFromExtentBasedInode();
			status_t			ReadExtentsFromTreeInode();
			size_t				MaxRecordsPossibleInTreeRoot();
			size_t				MaxRecordsPossibleNode();
			TreePointer*		GetPtrFromRoot(int pos);
			TreePointer*		GetPtrFromNode(int pos, void* buffer);
			size_t				GetPtrOffsetIntoRoot(int pos);
			size_t				GetPtrOffsetIntoNode(int pos);
			uint32				SizeOfLongBlock();
private:
			void				SwapEndian();
private:
			status_t			GetFromDisk();
			Dinode*				fNode;
			xfs_ino_t			fId;
			Volume*				fVolume;
			char*				fBuffer;
				// Contains the disk inode in BE format
			ExtentMapEntry*		fExtents;
};


uint32 hashfunction(const char* name, int length);


// A common function to return given hash lowerbound
template<class T> void
hashLowerBound(T* entry, int& left, int& right, uint32 hashValueOfRequest)
{
	int mid;

	/*
	* Trying to find the lowerbound of hashValueOfRequest
	* This is slightly different from bsearch(), as we want the first
	* instance of hashValueOfRequest and not any instance.
	*/
	while (left < right) {
		mid = (left + right) / 2;
		uint32 hashval = B_BENDIAN_TO_HOST_INT32(entry[mid].hashval);
		if (hashval >= hashValueOfRequest) {
			right = mid;
			continue;
		}
		if (hashval < hashValueOfRequest)
			left = mid+1;
	}
	TRACE("left:(%" B_PRId32 "), right:(%" B_PRId32 ")\n", left, right);
}

#endif