⛏️ index : haiku.git

/*
 * 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	checksum_type;
	uint16	_reserved4;
	uint64	kb_written;
	uint32	_reserved5[60];
	uint32	checksum_seed;
	uint32	_reserved6[98];
	uint32	checksum;

	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;
	}
	uint64 ReservedBlocks(bool has64bits) const
	{
		uint64 blocks = B_LENDIAN_TO_HOST_INT32(reserved_blocks);
		if (has64bits)
			blocks |= ((uint64)B_LENDIAN_TO_HOST_INT32(reserved_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 FragmentsPerGroup() const
		{ return B_LENDIAN_TO_HOST_INT32(fragments_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)
		{ read_only_features = B_HOST_TO_LENDIAN_INT32(readOnlyFeatures); }

	bool IsMagicValid();
	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		0x1	// File system was cleanly unmounted
#define EXT2_FS_STATE_ERROR		0x2	// File system has errors
#define EXT2_FS_STATE_ORPHAN	0x4	// 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
#define EXT2_FEATURE_SPARSESUPER2				0x0200

// 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
#define EXT2_READ_ONLY_FEATURE_QUOTA			0x0100
#define EXT2_READ_ONLY_FEATURE_BIGALLOC			0x0200
#define EXT4_READ_ONLY_FEATURE_METADATA_CSUM	0x0400
#define EXT4_READ_ONLY_FEATURE_READONLY			0x1000
#define EXT4_READ_ONLY_FEATURE_PROJECT			0x2000

// 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
#define EXT2_INCOMPATIBLE_FEATURE_EA_INODE		0x0400
#define EXT2_INCOMPATIBLE_FEATURE_DIR_DATA		0x1000
#define EXT2_INCOMPATIBLE_FEATURE_CSUM_SEED		0x2000
#define EXT2_INCOMPATIBLE_FEATURE_LARGEDIR		0x4000
#define EXT2_INCOMPATIBLE_FEATURE_INLINE_DATA	0x8000
#define EXT2_INCOMPATIBLE_FEATURE_ENCRYPT		0x10000

// states
#define EXT2_STATE_VALID						0x01
#define	EXT2_STATE_INVALID						0x02

#define EXT2_BLOCK_GROUP_NORMAL_SIZE			32

// 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	exclude_bitmap;
	uint16	block_bitmap_csum;
	uint16	inode_bitmap_csum;
	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	exclude_bitmap_high;
	uint16	block_bitmap_csum_high;
	uint16	inode_bitmap_csum_high;
	uint32	_reserved;

	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_tail {
	uint32 checksum;
} _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	file_access_control_high;
	uint16	uid_high;
	uint16	gid_high;
	uint16	checksum;
	uint16	reserved;

	// extra attributes
	uint16	extra_inode_size;
	uint16	checksum_high;
	uint32	change_time_extra;
	uint32	modification_time_extra;
	uint32	access_time_extra;
	uint32	creation_time;
	uint32	creation_time_extra;
	uint32	version_high;
	uint32	project_id;

	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_INT16(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_INT16(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

#define EXT2_DIR_PAD		4
#define EXT2_DIR_ROUND		(EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(namelen)	(((namelen) + 8 + EXT2_DIR_ROUND) \
										& ~EXT2_DIR_ROUND)

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;

struct ext2_dir_entry_tail {
	uint32	zero1;
	uint16	twelve;
	uint8	zero2;
	uint8	hexade;
	uint32	checksum;
} _PACKED;

struct ext2_htree_tail {
	uint32	reserved;
	uint32	checksum;
} _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	checksum;
	uint32	reserved[3];	// 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