/*
 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */
#ifndef EXT2_H
#define EXT2_H
 
 
#include <sys/stat.h>
 
#include <ByteOrder.h>
#include <fs_interface.h>
#include <KernelExport.h>
 
 
typedef uint64 fileblock_t;		// file block number
typedef uint64 fsblock_t;		// filesystem block number
 
 
#define EXT2_SUPER_BLOCK_OFFSET	1024
 
struct ext2_super_block {
	uint32	num_inodes;
	uint32	num_blocks;
	uint32	reserved_blocks;
	uint32	free_blocks;
	uint32	free_inodes;
	uint32	first_data_block;
	uint32	block_shift;
	uint32	fragment_shift;
	uint32	blocks_per_group;
	uint32	fragments_per_group;
	uint32	inodes_per_group;
	uint32	mount_time;
	uint32	write_time;
	uint16	mount_count;
	uint16	max_mount_count;
	uint16	magic;
	uint16	state;
	uint16	error_handling;
	uint16	minor_revision_level;
	uint32	last_check_time;
	uint32	check_interval;
	uint32	creator_os;
	uint32	revision_level;
	uint16	reserved_blocks_uid;
	uint16	reserved_blocks_gid;
	uint32	first_inode;
	uint16	inode_size;
	uint16	block_group;
	uint32	compatible_features;
	uint32	incompatible_features;
	uint32	read_only_features;
	uint8	uuid[16];
	char	name[16];
	char	last_mount_point[64];
	uint32	algorithm_usage_bitmap;
	uint8	preallocated_blocks;
	uint8	preallocated_directory_blocks;
	uint16	reserved_gdt_blocks;
 
	// journaling ext3 support
	uint8	journal_uuid[16];
	uint32	journal_inode;
	uint32	journal_device;
	uint32	last_orphan;
	uint32	hash_seed[4];
	uint8	default_hash_version;
	uint8	_reserved1;
	uint16	group_descriptor_size;
	uint32	default_mount_options;
	uint32	first_meta_block_group;
	uint32	fs_creation_time;
	uint32	journal_inode_backup[17];
 
	// ext4 support
	uint32	num_blocks_high;
	uint32	reserved_blocks_high;
	uint32	free_blocks_high;
	uint16	min_inode_size;
	uint16	want_inode_size;
	uint32	flags;
	uint16	raid_stride;
	uint16	mmp_interval;
	uint64	mmp_block;
	uint32	raid_stripe_width;
	uint8	groups_per_flex_shift;
	uint8	_reserved3;
	uint16	_reserved4;
	uint32	_reserved5[162];
 
	uint16 Magic() const { return B_LENDIAN_TO_HOST_INT16(magic); }
	uint16 State() const { return B_LENDIAN_TO_HOST_INT16(state); }
	uint32 RevisionLevel() const { return B_LENDIAN_TO_HOST_INT16(revision_level); }
	uint32 BlockShift() const { return B_LENDIAN_TO_HOST_INT32(block_shift) + 10; }
	uint32 NumInodes() const { return B_LENDIAN_TO_HOST_INT32(num_inodes); }
	uint64 NumBlocks(bool has64bits) const
	{
		uint64 blocks = B_LENDIAN_TO_HOST_INT32(num_blocks);
		if (has64bits)
			blocks |= ((uint64)B_LENDIAN_TO_HOST_INT32(num_blocks_high) << 32);
		return blocks;
	}
	uint32 FreeInodes() const { return B_LENDIAN_TO_HOST_INT32(free_inodes); }
	uint64 FreeBlocks(bool has64bits) const
	{
		uint64 blocks = B_LENDIAN_TO_HOST_INT32(free_blocks);
		if (has64bits)
			blocks |= ((uint64)B_LENDIAN_TO_HOST_INT32(free_blocks_high) << 32);
		return blocks;
	}
	uint16 InodeSize() const { return B_LENDIAN_TO_HOST_INT16(inode_size); }
	uint32 FirstDataBlock() const
		{ return B_LENDIAN_TO_HOST_INT32(first_data_block); }
	uint32 BlocksPerGroup() const
		{ return B_LENDIAN_TO_HOST_INT32(blocks_per_group); }
	uint32 InodesPerGroup() const
		{ return B_LENDIAN_TO_HOST_INT32(inodes_per_group); }
	uint32 FirstMetaBlockGroup() const
		{ return B_LENDIAN_TO_HOST_INT32(first_meta_block_group); }
	uint32 CompatibleFeatures() const
		{ return B_LENDIAN_TO_HOST_INT32(compatible_features); }
	uint32 ReadOnlyFeatures() const
		{ return B_LENDIAN_TO_HOST_INT32(read_only_features); }
	uint32 IncompatibleFeatures() const
		{ return B_LENDIAN_TO_HOST_INT32(incompatible_features); }
	uint16 ReservedGDTBlocks() const
		{ return B_LENDIAN_TO_HOST_INT16(reserved_gdt_blocks); }
	ino_t  JournalInode() const
		{ return B_LENDIAN_TO_HOST_INT32(journal_inode); }
	ino_t  LastOrphan() const
		{ return (ino_t)B_LENDIAN_TO_HOST_INT32(last_orphan); }
	uint32 HashSeed(uint8 i) const
		{ return B_LENDIAN_TO_HOST_INT32(hash_seed[i]); }
	uint16 GroupDescriptorSize() const
		{ return B_LENDIAN_TO_HOST_INT16(group_descriptor_size); }
 
	void SetFreeInodes(uint32 freeInodes)
		{ free_inodes = B_HOST_TO_LENDIAN_INT32(freeInodes); }
	void SetFreeBlocks(uint64 freeBlocks, bool has64bits)
	{
		free_blocks = B_HOST_TO_LENDIAN_INT32(freeBlocks & 0xffffffff);
		if (has64bits)
			free_blocks_high = B_HOST_TO_LENDIAN_INT32(freeBlocks >> 32);
	}
	void SetLastOrphan(ino_t id)
		{ last_orphan = B_HOST_TO_LENDIAN_INT32((uint32)id); }
	void SetReadOnlyFeatures(uint32 readOnlyFeatures) const
		{ readOnlyFeatures = B_HOST_TO_LENDIAN_INT32(readOnlyFeatures); }
 
	bool IsValid();
		// implemented in Volume.cpp
} _PACKED;
 
#define EXT2_OLD_REVISION		0
#define EXT2_DYNAMIC_REVISION	1
 
#define EXT2_MAX_REVISION		EXT2_DYNAMIC_REVISION
 
#define EXT2_FS_STATE_VALID		1	// File system was cleanly unmounted
#define EXT2_FS_STATE_ERROR		2	// File system has errors
#define EXT2_FS_STATE_ORPHAN	3	// Orphans are being recovered
 
// compatible features
#define EXT2_FEATURE_DIRECTORY_PREALLOCATION	0x0001
#define EXT2_FEATURE_IMAGIC_INODES				0x0002
#define EXT2_FEATURE_HAS_JOURNAL				0x0004
#define EXT2_FEATURE_EXT_ATTR					0x0008
#define EXT2_FEATURE_RESIZE_INODE				0x0010
#define EXT2_FEATURE_DIRECTORY_INDEX			0x0020
 
// read-only compatible features
#define EXT2_READ_ONLY_FEATURE_SPARSE_SUPER		0x0001
#define EXT2_READ_ONLY_FEATURE_LARGE_FILE		0x0002
#define EXT2_READ_ONLY_FEATURE_BTREE_DIRECTORY	0x0004
#define EXT2_READ_ONLY_FEATURE_HUGE_FILE		0x0008
#define EXT2_READ_ONLY_FEATURE_GDT_CSUM			0x0010
#define EXT2_READ_ONLY_FEATURE_DIR_NLINK		0x0020
#define EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE		0x0040
 
// incompatible features
#define EXT2_INCOMPATIBLE_FEATURE_COMPRESSION	0x0001
#define EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE		0x0002
#define EXT2_INCOMPATIBLE_FEATURE_RECOVER		0x0004
#define EXT2_INCOMPATIBLE_FEATURE_JOURNAL		0x0008
#define EXT2_INCOMPATIBLE_FEATURE_META_GROUP	0x0010
#define EXT2_INCOMPATIBLE_FEATURE_EXTENTS		0x0040
#define EXT2_INCOMPATIBLE_FEATURE_64BIT			0x0080
#define EXT2_INCOMPATIBLE_FEATURE_MMP			0x0100
#define EXT2_INCOMPATIBLE_FEATURE_FLEX_GROUP	0x0200
 
// states
#define EXT2_STATE_VALID						0x01
#define	EXT2_STATE_INVALID						0x02
 
#define EXT2_BLOCK_GROUP_NORMAL_SIZE			32
#define EXT2_BLOCK_GROUP_64BIT_SIZE				64
 
// block group flags
#define EXT2_BLOCK_GROUP_INODE_UNINIT	0x1
#define EXT2_BLOCK_GROUP_BLOCK_UNINIT	0x2
 
 
struct ext2_block_group {
	uint32	block_bitmap;
	uint32	inode_bitmap;
	uint32	inode_table;
	uint16	free_blocks;
	uint16	free_inodes;
	uint16	used_directories;
	uint16	flags;
	uint32	_reserved[2];
	uint16	unused_inodes;
	uint16	checksum;
 
	// ext4
	uint32	block_bitmap_high;
	uint32	inode_bitmap_high;
	uint32	inode_table_high;
	uint16	free_blocks_high;
	uint16	free_inodes_high;
	uint16	used_directories_high;
	uint16	unused_inodes_high;
	uint32	_reserved2[3];
 
	fsblock_t BlockBitmap(bool has64bits) const
	{
		uint64 block = B_LENDIAN_TO_HOST_INT32(block_bitmap);
		if (has64bits)
			block |=
				((uint64)B_LENDIAN_TO_HOST_INT32(block_bitmap_high) << 32);
		return block;
	}
	fsblock_t InodeBitmap(bool has64bits) const
	{
		uint64 bitmap = B_LENDIAN_TO_HOST_INT32(inode_bitmap);
		if (has64bits)
			bitmap |=
				((uint64)B_LENDIAN_TO_HOST_INT32(inode_bitmap_high) << 32);
		return bitmap;
	}
	uint64 InodeTable(bool has64bits) const
	{
		uint64 table = B_LENDIAN_TO_HOST_INT32(inode_table);
		if (has64bits)
			table |= ((uint64)B_LENDIAN_TO_HOST_INT32(inode_table_high) << 32);
		return table;
	}
	uint32 FreeBlocks(bool has64bits) const
	{
		uint32 blocks = B_LENDIAN_TO_HOST_INT16(free_blocks);
		if (has64bits)
			blocks |=
				((uint32)B_LENDIAN_TO_HOST_INT16(free_blocks_high) << 16);
		return blocks;
	}
	uint32 FreeInodes(bool has64bits) const
	{
		uint32 inodes = B_LENDIAN_TO_HOST_INT16(free_inodes);
		if (has64bits)
			inodes |=
				((uint32)B_LENDIAN_TO_HOST_INT16(free_inodes_high) << 16);
		return inodes;
	}
	uint32 UsedDirectories(bool has64bits) const
	{
		uint32 dirs = B_LENDIAN_TO_HOST_INT16(used_directories);
		if (has64bits)
			dirs |=
				((uint32)B_LENDIAN_TO_HOST_INT16(used_directories_high) << 16);
		return dirs;
	}
	uint16 Flags() const { return B_LENDIAN_TO_HOST_INT16(flags); }
	uint32 UnusedInodes(bool has64bits) const
	{
		uint32 inodes = B_LENDIAN_TO_HOST_INT16(unused_inodes);
		if (has64bits)
			inodes |=
				((uint32)B_LENDIAN_TO_HOST_INT16(unused_inodes_high) << 16);
		return inodes;
	}
 
 
	void SetFreeBlocks(uint32 freeBlocks, bool has64bits)
	{
		free_blocks = B_HOST_TO_LENDIAN_INT16(freeBlocks) & 0xffff;
		if (has64bits)
			free_blocks_high = B_HOST_TO_LENDIAN_INT16(freeBlocks >> 16);
	}
 
	void SetFreeInodes(uint32 freeInodes, bool has64bits)
	{
		free_inodes = B_HOST_TO_LENDIAN_INT16(freeInodes) & 0xffff;
		if (has64bits)
			free_inodes_high = B_HOST_TO_LENDIAN_INT16(freeInodes >> 16);
	}
 
	void SetUsedDirectories(uint16 usedDirectories, bool has64bits)
	{
		used_directories = B_HOST_TO_LENDIAN_INT16(usedDirectories& 0xffff);
		if (has64bits)
			used_directories_high =
				B_HOST_TO_LENDIAN_INT16(usedDirectories >> 16);
	}
 
	void SetFlags(uint16 newFlags)
	{
		flags = B_HOST_TO_LENDIAN_INT16(newFlags);
	}
 
	void SetUnusedInodes(uint32 unusedInodes, bool has64bits)
	{
		unused_inodes = B_HOST_TO_LENDIAN_INT16(unusedInodes) & 0xffff;
		if (has64bits)
			unused_inodes_high = B_HOST_TO_LENDIAN_INT16(unusedInodes >> 16);
	}
} _PACKED;
 
#define EXT2_DIRECT_BLOCKS			12
#define EXT2_ROOT_NODE				2
#define EXT2_SHORT_SYMLINK_LENGTH	60
 
struct ext2_data_stream {
	uint32 direct[EXT2_DIRECT_BLOCKS];
	uint32 indirect;
	uint32 double_indirect;
	uint32 triple_indirect;
} _PACKED;
 
#define EXT2_EXTENT_MAGIC			0xf30a
#define EXT2_EXTENT_MAX_LENGTH		0x8000
 
struct ext2_extent_header {
	uint16 magic;
	uint16 num_entries;
	uint16 max_entries;
	uint16 depth;
	uint32 generation;
	bool IsValid() const
	{
		return B_LENDIAN_TO_HOST_INT16(magic) == EXT2_EXTENT_MAGIC;
	}
	uint16 NumEntries() const { return B_LENDIAN_TO_HOST_INT16(num_entries); }
	uint16 MaxEntries() const { return B_LENDIAN_TO_HOST_INT16(max_entries); }
	uint16 Depth() const { return B_LENDIAN_TO_HOST_INT16(depth); }
	uint32 Generation() const { return B_LENDIAN_TO_HOST_INT32(generation); }
	void SetNumEntries(uint16 num)
		{ num_entries = B_HOST_TO_LENDIAN_INT16(num); }
	void SetMaxEntries(uint16 max)
		{ max_entries = B_HOST_TO_LENDIAN_INT16(max); }
	void SetDepth(uint16 _depth)
		{ depth = B_HOST_TO_LENDIAN_INT16(_depth); }
	void SetGeneration(uint32 _generation)
		{ generation = B_HOST_TO_LENDIAN_INT32(_generation); }
} _PACKED;
 
struct ext2_extent_index {
	uint32 logical_block;
	uint32 physical_block;
	uint16 physical_block_high;
	uint16 _reserved;
	uint32 LogicalBlock() const
		{ return B_LENDIAN_TO_HOST_INT32(logical_block); }
	uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block)
		| ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); }
	void SetLogicalBlock(uint32 block) {
		logical_block = B_HOST_TO_LENDIAN_INT32(block); }
	void SetPhysicalBlock(uint64 block) {
		physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff);
		physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); }
} _PACKED;
 
struct ext2_extent_entry {
	uint32 logical_block;
	uint16 length;
	uint16 physical_block_high;
	uint32 physical_block;
	uint32 LogicalBlock() const
		{ return B_LENDIAN_TO_HOST_INT32(logical_block); }
	uint16 Length() const { return B_LENDIAN_TO_HOST_INT16(length) == 0x8000
		? 0x8000 : B_LENDIAN_TO_HOST_INT16(length) & 0x7fff; }
	uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block)
		| ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); }
	void SetLogicalBlock(uint32 block) {
		logical_block = B_HOST_TO_LENDIAN_INT32(block); }
	void SetLength(uint16 _length) {
		length = B_HOST_TO_LENDIAN_INT16(_length) & 0x7fff; }
	void SetPhysicalBlock(uint64 block) {
		physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff);
		physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); }
} _PACKED;
 
struct ext2_extent_stream {
	ext2_extent_header extent_header;
	union {
		ext2_extent_entry extent_entries[4];
		ext2_extent_index extent_index[4];
	};
} _PACKED;
 
#define EXT2_INODE_NORMAL_SIZE		128
#define EXT2_INODE_MAX_LINKS		65000
 
struct ext2_inode {
	uint16	mode;
	uint16	uid;
	uint32	size;
	uint32	access_time;
	uint32	change_time;
	uint32	modification_time;
	uint32	deletion_time;
	uint16	gid;
	uint16	num_links;
	uint32	num_blocks;
	uint32	flags;
	uint32	version;
	union {
		ext2_data_stream stream;
		char symlink[EXT2_SHORT_SYMLINK_LENGTH];
		ext2_extent_stream extent_stream;
	};
	uint32	generation;
	uint32	file_access_control;
	union {
		// for directories vs. files
		uint32	directory_access_control;
		uint32	size_high;
	};
	uint32	fragment;
	union {
		struct {
			uint8	fragment_number;
			uint8	fragment_size;
		};
		uint16 num_blocks_high;
	};
	uint16	_padding;
	uint16	uid_high;
	uint16	gid_high;
	uint32	_reserved2;
 
	// extra attributes
	uint16	extra_inode_size;
	uint16	_padding2;
	uint32	change_time_extra;
	uint32	modification_time_extra;
	uint32	access_time_extra;
	uint32	creation_time;
	uint32	creation_time_extra;
	uint32	version_high;
 
	uint16 Mode() const { return B_LENDIAN_TO_HOST_INT16(mode); }
	uint32 Flags() const { return B_LENDIAN_TO_HOST_INT32(flags); }
	uint16 NumLinks() const { return B_LENDIAN_TO_HOST_INT16(num_links); }
	uint32 NumBlocks() const { return B_LENDIAN_TO_HOST_INT32(num_blocks); }
	uint64 NumBlocks64() const { return B_LENDIAN_TO_HOST_INT32(num_blocks)
		| ((uint64)B_LENDIAN_TO_HOST_INT32(num_blocks_high) << 32); }
 
	static void _DecodeTime(struct timespec *timespec, uint32 time,
		uint32 time_extra, bool extra)
	{
		timespec->tv_sec = B_LENDIAN_TO_HOST_INT32(time);
		if (extra && sizeof(timespec->tv_sec) > 4)
			timespec->tv_sec |=
				(uint64)(B_LENDIAN_TO_HOST_INT32(time_extra) & 0x2) << 32;
		if (extra)
			timespec->tv_nsec = B_LENDIAN_TO_HOST_INT32(time_extra) >> 2;
		else
			timespec->tv_nsec = 0;
	}
 
	void GetModificationTime(struct timespec *timespec, bool extra) const
		{ _DecodeTime(timespec, modification_time, modification_time_extra,
			extra); }
	void GetAccessTime(struct timespec *timespec, bool extra) const
		{ _DecodeTime(timespec, access_time, access_time_extra, extra); }
	void GetChangeTime(struct timespec *timespec, bool extra) const
		{ _DecodeTime(timespec, change_time, change_time_extra, extra); }
	void GetCreationTime(struct timespec *timespec, bool extra) const
	{
		if (extra)
			_DecodeTime(timespec, creation_time, creation_time_extra, extra);
		else {
			timespec->tv_sec = 0;
			timespec->tv_nsec = 0;
		}
	}
	time_t DeletionTime() const
		{ return B_LENDIAN_TO_HOST_INT32(deletion_time); }
 
	static uint32 _EncodeTime(const struct timespec *timespec)
	{
		uint32 time = (timespec->tv_nsec << 2) & 0xfffffffc;
		if (sizeof(timespec->tv_sec) > 4)
			time |= (uint64)timespec->tv_sec >> 32;
		return B_HOST_TO_LENDIAN_INT32(time);
	}
 
	void SetModificationTime(const struct timespec *timespec, bool extra)
	{
		modification_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec);
		if (extra)
			modification_time_extra = _EncodeTime(timespec);
	}
	void SetAccessTime(const struct timespec *timespec, bool extra)
	{
		access_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec);
		if (extra)
			access_time_extra = _EncodeTime(timespec);
	}
	void SetChangeTime(const struct timespec *timespec, bool extra)
	{
		change_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec);
		if (extra)
			change_time_extra = _EncodeTime(timespec);
	}
	void SetCreationTime(const struct timespec *timespec, bool extra)
	{
		if (extra) {
			creation_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec);
			creation_time_extra =
				B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_nsec);
		}
	}
	void SetDeletionTime(time_t deletionTime)
	{
		deletion_time = B_HOST_TO_LENDIAN_INT32((uint32)deletionTime);
	}
 
	ino_t  NextOrphan() const { return (ino_t)DeletionTime(); }
 
	off_t Size() const
	{
		if (S_ISREG(Mode())) {
			return B_LENDIAN_TO_HOST_INT32(size)
				| ((off_t)B_LENDIAN_TO_HOST_INT32(size_high) << 32);
		}
 
		return B_LENDIAN_TO_HOST_INT32(size);
	}
 
	uint32 ExtendedAttributesBlock() const
	{	return B_LENDIAN_TO_HOST_INT32(file_access_control);}
 
	uint16 ExtraInodeSize() const
		{ return B_LENDIAN_TO_HOST_INT16(extra_inode_size); }
 
	uint32 UserID() const
	{
		return B_LENDIAN_TO_HOST_INT16(uid)
			| (B_LENDIAN_TO_HOST_INT16(uid_high) << 16);
	}
 
	uint32 GroupID() const
	{
		return B_LENDIAN_TO_HOST_INT16(gid)
			| (B_LENDIAN_TO_HOST_INT16(gid_high) << 16);
	}
 
	void SetMode(uint16 newMode)
	{
		mode = B_LENDIAN_TO_HOST_INT16(newMode);
	}
 
	void UpdateMode(uint16 newMode, uint16 mask)
	{
		SetMode((Mode() & ~mask) | (newMode & mask));
	}
 
	void ClearFlag(uint32 mask)
	{
		flags &= ~B_HOST_TO_LENDIAN_INT32(mask);
	}
 
	void SetFlag(uint32 mask)
	{
		flags |= B_HOST_TO_LENDIAN_INT32(mask);
	}
 
	void SetFlags(uint32 newFlags)
	{
		flags = B_HOST_TO_LENDIAN_INT32(newFlags);
	}
 
	void SetNumLinks(uint16 numLinks)
	{
		num_links = B_HOST_TO_LENDIAN_INT16(numLinks);
	}
 
	void SetNumBlocks(uint32 numBlocks)
	{
		num_blocks = B_HOST_TO_LENDIAN_INT32(numBlocks);
	}
 
	void SetNumBlocks64(uint64 numBlocks)
	{
		num_blocks = B_HOST_TO_LENDIAN_INT32(numBlocks & 0xffffffff);
		num_blocks_high = B_HOST_TO_LENDIAN_INT32(numBlocks >> 32);
	}
 
	void SetNextOrphan(ino_t id)
	{
		deletion_time = B_HOST_TO_LENDIAN_INT32((uint32)id);
	}
 
	void SetSize(off_t newSize)
	{
		size = B_HOST_TO_LENDIAN_INT32(newSize & 0xFFFFFFFF);
		if (S_ISREG(Mode()))
			size_high = B_HOST_TO_LENDIAN_INT32(newSize >> 32);
	}
 
	void SetUserID(uint32 newUID)
	{
		uid = B_HOST_TO_LENDIAN_INT16(newUID & 0xFFFF);
		uid_high = B_HOST_TO_LENDIAN_INT16(newUID >> 16);
	}
 
	void SetGroupID(uint32 newGID)
	{
		gid = B_HOST_TO_LENDIAN_INT16(newGID & 0xFFFF);
		gid_high = B_HOST_TO_LENDIAN_INT16(newGID >> 16);
	}
 
	void SetExtendedAttributesBlock(uint32 block)
	{
		file_access_control = B_HOST_TO_LENDIAN_INT32(block);
	}
 
	void SetExtraInodeSize(uint16 newSize)
	{
		extra_inode_size = B_HOST_TO_LENDIAN_INT16(newSize);
	}
} _PACKED;
 
#define EXT2_SUPER_BLOCK_MAGIC			0xef53
 
// flags
#define EXT2_INODE_SECURE_DELETION		0x00000001
#define EXT2_INODE_UNDELETE				0x00000002
#define EXT2_INODE_COMPRESSED			0x00000004
#define EXT2_INODE_SYNCHRONOUS			0x00000008
#define EXT2_INODE_IMMUTABLE			0x00000010
#define EXT2_INODE_APPEND_ONLY			0x00000020
#define EXT2_INODE_NO_DUMP				0x00000040
#define EXT2_INODE_NO_ACCESS_TIME		0x00000080
#define EXT2_INODE_DIRTY				0x00000100
#define EXT2_INODE_COMPRESSED_BLOCKS	0x00000200
#define EXT2_INODE_DO_NOT_COMPRESS		0x00000400
#define EXT2_INODE_COMPRESSION_ERROR	0x00000800
#define EXT2_INODE_BTREE				0x00001000
#define EXT2_INODE_INDEXED				0x00001000
#define EXT2_INODE_JOURNAL_DATA			0x00004000
#define EXT2_INODE_NO_MERGE_TAIL		0x00008000
#define EXT2_INODE_DIR_SYNCH			0x00010000
#define EXT2_INODE_HUGE_FILE			0x00040000
#define EXT2_INODE_EXTENTS				0x00080000
#define EXT2_INODE_LARGE_EA				0x00200000
#define EXT2_INODE_EOF_BLOCKS			0x00400000
#define EXT2_INODE_INLINE_DATA			0x10000000
#define EXT2_INODE_RESERVED				0x80000000
 
#define EXT2_INODE_INHERITED (EXT2_INODE_SECURE_DELETION | EXT2_INODE_UNDELETE \
	| EXT2_INODE_COMPRESSED | EXT2_INODE_SYNCHRONOUS | EXT2_INODE_IMMUTABLE \
	| EXT2_INODE_APPEND_ONLY | EXT2_INODE_NO_DUMP | EXT2_INODE_NO_ACCESS_TIME \
	| EXT2_INODE_DO_NOT_COMPRESS | EXT2_INODE_JOURNAL_DATA \
	| EXT2_INODE_NO_MERGE_TAIL | EXT2_INODE_DIR_SYNCH)
 
#define EXT2_NAME_LENGTH	255
 
struct ext2_dir_entry {
	uint32	inode_id;
	uint16	length;
	uint8	name_length;
	uint8	file_type;
	char	name[EXT2_NAME_LENGTH];
 
	uint32	InodeID() const { return B_LENDIAN_TO_HOST_INT32(inode_id); }
	uint16	Length() const { return B_LENDIAN_TO_HOST_INT16(length); }
	uint8	NameLength() const { return name_length; }
	uint8	FileType() const { return file_type; }
 
	void	SetInodeID(uint32 id) { inode_id = B_HOST_TO_LENDIAN_INT32(id); }
 
	void	SetLength(uint16 newLength/*uint8 nameLength*/)
	{
		length = B_HOST_TO_LENDIAN_INT16(newLength);
		/*name_length = nameLength;
 
		if (nameLength % 4 == 0) {
			length = B_HOST_TO_LENDIAN_INT16(
				(short)(nameLength + MinimumSize()));
		} else {
			length = B_HOST_TO_LENDIAN_INT16(
				(short)(nameLength % 4 + 1 + MinimumSize()));
		}*/
	}
 
	bool IsValid() const
	{
		return Length() > MinimumSize();
			// There is no maximum size, as the last entry spans until the
			// end of the block
	}
 
	static size_t MinimumSize()
	{
		return sizeof(ext2_dir_entry) - EXT2_NAME_LENGTH;
	}
} _PACKED;
 
// file types
#define EXT2_TYPE_UNKNOWN		0
#define EXT2_TYPE_FILE			1
#define EXT2_TYPE_DIRECTORY		2
#define EXT2_TYPE_CHAR_DEVICE	3
#define EXT2_TYPE_BLOCK_DEVICE	4
#define EXT2_TYPE_FIFO			5
#define EXT2_TYPE_SOCKET		6
#define EXT2_TYPE_SYMLINK		7
 
#define EXT2_XATTR_MAGIC		0xea020000
#define EXT2_XATTR_ROUND		((1 << 2) - 1)
#define EXT2_XATTR_NAME_LENGTH	255
 
#define EXT2_XATTR_INDEX_USER	1
 
struct ext2_xattr_header {
	uint32	magic;
	uint32	refcount;
	uint32	blocks;		// must be 1 for ext2
	uint32	hash;
	uint32	reserved[4];	// zero
 
	bool IsValid() const
	{
		return B_LENDIAN_TO_HOST_INT32(magic) == EXT2_XATTR_MAGIC
			&& B_LENDIAN_TO_HOST_INT32(blocks) == 1
			&& refcount <= 1024;
	}
 
	void Dump() const {
		for (unsigned int i = 0; i < Length(); i++)
			dprintf("%02x ", ((uint8 *)this)[i]);
		dprintf("\n");
	}
 
	static size_t Length()
	{
		return sizeof(ext2_xattr_header);
	}
};
 
struct ext2_xattr_entry {
	uint8	name_length;
	uint8	name_index;
	uint16	value_offset;
	uint32	value_block;	// must be zero for ext2
	uint32	value_size;
	uint32	hash;
	char	name[EXT2_XATTR_NAME_LENGTH];
 
	uint8 NameLength() const { return name_length; }
	uint8 NameIndex() const { return name_index; }
	uint16 ValueOffset() const { return
			B_LENDIAN_TO_HOST_INT16(value_offset); }
	uint32 ValueSize() const { return
			B_LENDIAN_TO_HOST_INT32(value_size); }
 
	// padded sizes
	uint32 Length() const { return (MinimumSize() + NameLength()
		+ EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND; }
 
	bool IsValid() const
	{
		return NameLength() > 0 && value_block == 0;
			// There is no maximum size, as the last entry spans until the
			// end of the block
	}
 
	void Dump(bool full=false) const {
		for (unsigned int i = 0; i < (full ? sizeof(this) : MinimumSize()); i++)
			dprintf("%02x ", ((uint8 *)this)[i]);
		dprintf("\n");
	}
 
	static size_t MinimumSize()
	{
		return sizeof(ext2_xattr_entry) - EXT2_XATTR_NAME_LENGTH;
	}
} _PACKED;
 
 
struct file_cookie {
	bigtime_t	last_notification;
	off_t		last_size;
	int			open_mode;
};
 
 
#define EXT2_OPEN_MODE_USER_MASK		0x7fffffff
 
#define INODE_NOTIFICATION_INTERVAL		10000000LL
 
 
extern fs_volume_ops gExt2VolumeOps;
extern fs_vnode_ops gExt2VnodeOps;
 
#endif	// EXT2_H

V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'this' class object.

V570 The 'readOnlyFeatures' variable is assigned to itself.