Kernel: Ext2Inode now supports indirect blocks through for_each_block()
This commit is contained in:
		
							parent
							
								
									80006ea137
								
							
						
					
					
						commit
						efaca469ee
					
				|  | @ -131,9 +131,12 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 		virtual BAN::StringView name() const override { return m_name; } | 		virtual BAN::StringView name() const override { return m_name; } | ||||||
| 
 | 
 | ||||||
| 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() const override; | 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() override; | ||||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const override; | 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() override; | ||||||
| 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const override; | 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) override; | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		BAN::ErrorOr<void> for_each_block(BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)>&); | ||||||
| 
 | 
 | ||||||
| 	private: | 	private: | ||||||
| 		Ext2Inode() {} | 		Ext2Inode() {} | ||||||
|  |  | ||||||
|  | @ -20,9 +20,9 @@ namespace Kernel | ||||||
| 
 | 
 | ||||||
| 		virtual BAN::StringView name() const = 0; | 		virtual BAN::StringView name() const = 0; | ||||||
| 
 | 
 | ||||||
| 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() const = 0; | 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() = 0; | ||||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const = 0; | 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() = 0; | ||||||
| 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const = 0; | 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) = 0; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -148,72 +148,168 @@ namespace Kernel | ||||||
| 		return m_inode.mode & Ext2::Enum::IFREG; | 		return m_inode.mode & Ext2::Enum::IFREG; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2Inode::read_all() const | 	BAN::ErrorOr<void> Ext2Inode::for_each_block(BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)>& func) | ||||||
| 	{ | 	{ | ||||||
| 		return BAN::Error::from_string("not implemented"); | 		uint32_t data_blocks_left = m_inode.blocks / (2 << m_fs->superblock().log_block_size); | ||||||
|  | 		uint32_t block_array_block_count = (1024 << m_fs->superblock().log_block_size) / sizeof(uint32_t); | ||||||
|  | 
 | ||||||
|  | 		// Direct blocks
 | ||||||
|  | 		for (uint32_t i = 0; i < 12; i++) | ||||||
|  | 		{ | ||||||
|  | 			if (m_inode.block[i] == 0) | ||||||
|  | 				continue; | ||||||
|  | 
 | ||||||
|  | 			auto block_data = TRY(m_fs->read_block(m_inode.block[i])); | ||||||
|  | 			if (!TRY(func(block_data))) | ||||||
|  | 				return {}; | ||||||
|  | 			if (--data_blocks_left == 0) | ||||||
|  | 				return {}; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<BAN::RefCounted<Inode>> Ext2Inode::directory_find(BAN::StringView name) const | 		// Singly indirect block
 | ||||||
|  | 		if (m_inode.block[12]) | ||||||
|  | 		{ | ||||||
|  | 			auto block_array = TRY(m_fs->read_block(m_inode.block[12])); | ||||||
|  | 			for (uint32_t i = 0; i < block_array_block_count; i++) | ||||||
|  | 			{ | ||||||
|  | 				uint32_t block = ((uint32_t*)block_array.data())[i]; | ||||||
|  | 				if (block == 0) | ||||||
|  | 					continue; | ||||||
|  | 				auto block_data = TRY(m_fs->read_block(block)); | ||||||
|  | 				if (!TRY(func(block_data))) | ||||||
|  | 					return {}; | ||||||
|  | 				if (--data_blocks_left == 0) | ||||||
|  | 					return {}; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Doubly indirect blocks
 | ||||||
|  | 		if (m_inode.block[13]) | ||||||
|  | 		{ | ||||||
|  | 			auto singly_indirect_array = TRY(m_fs->read_block(m_inode.block[13])); | ||||||
|  | 			for (uint32_t i = 0; i < block_array_block_count; i++) | ||||||
|  | 			{ | ||||||
|  | 				uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[i]; | ||||||
|  | 				auto block_array = TRY(m_fs->read_block(singly_indirect_block)); | ||||||
|  | 				for (uint32_t j = 0; j < block_array_block_count; j++) | ||||||
|  | 				{ | ||||||
|  | 					uint32_t block = ((uint32_t*)block_array.data())[j]; | ||||||
|  | 					if (block == 0) | ||||||
|  | 						continue; | ||||||
|  | 					auto block_data = TRY(m_fs->read_block(block)); | ||||||
|  | 					if (!TRY(func(block_data))) | ||||||
|  | 						return {}; | ||||||
|  | 					if (--data_blocks_left == 0) | ||||||
|  | 						return {}; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Triply indirect blocks
 | ||||||
|  | 		if (m_inode.block[14]) | ||||||
|  | 		{ | ||||||
|  | 			auto doubly_indirect_array = TRY(m_fs->read_block(m_inode.block[14])); | ||||||
|  | 			for (uint32_t i = 0; i < block_array_block_count; i++) | ||||||
|  | 			{ | ||||||
|  | 				uint32_t doubly_indirect_block = ((uint32_t*)doubly_indirect_array.data())[i]; | ||||||
|  | 				auto singly_indirect_array = TRY(m_fs->read_block(doubly_indirect_block)); | ||||||
|  | 				for (uint32_t j = 0; j < block_array_block_count; j++) | ||||||
|  | 				{ | ||||||
|  | 					uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[j]; | ||||||
|  | 					auto block_array = TRY(m_fs->read_block(singly_indirect_block)); | ||||||
|  | 					for (uint32_t k = 0; k < block_array_block_count; k++) | ||||||
|  | 					{ | ||||||
|  | 						uint32_t block = ((uint32_t*)block_array.data())[k]; | ||||||
|  | 						if (block == 0) | ||||||
|  | 							continue; | ||||||
|  | 						auto block_data = TRY(m_fs->read_block(block)); | ||||||
|  | 						if (!TRY(func(block_data))) | ||||||
|  | 							return {}; | ||||||
|  | 						if (--data_blocks_left == 0) | ||||||
|  | 							return {}; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return BAN::Error::from_string("Inode did not contain enough blocks"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2Inode::read_all() | ||||||
|  | 	{ | ||||||
|  | 		if (is_directory()) | ||||||
|  | 			return BAN::Error::from_string("Inode is a directory"); | ||||||
|  | 
 | ||||||
|  | 		BAN::Vector<uint8_t> data_buffer; | ||||||
|  | 		TRY(data_buffer.resize(m_inode.size)); | ||||||
|  | 
 | ||||||
|  | 		uint32_t bytes_done = 0; | ||||||
|  | 		uint32_t bytes_left = m_inode.size; | ||||||
|  | 
 | ||||||
|  | 		BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)> read_func( | ||||||
|  | 			[&](const BAN::Vector<uint8_t>& block_data) | ||||||
|  | 			{ | ||||||
|  | 				uint32_t to_copy = BAN::Math::min<uint32_t>(block_data.size(), bytes_left); | ||||||
|  | 				memcpy(data_buffer.data() + bytes_done, block_data.data(), to_copy); | ||||||
|  | 				bytes_done += to_copy; | ||||||
|  | 				bytes_left -= to_copy; | ||||||
|  | 				return bytes_left > 0; | ||||||
|  | 			} | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 		TRY(for_each_block(read_func)); | ||||||
|  | 		ASSERT(bytes_left == 0); | ||||||
|  | 
 | ||||||
|  | 		return data_buffer; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<BAN::RefCounted<Inode>> Ext2Inode::directory_find(BAN::StringView file_name) | ||||||
| 	{ | 	{ | ||||||
| 		if (!is_directory()) | 		if (!is_directory()) | ||||||
| 			return BAN::Error::from_string("Inode is not a directory"); | 			return BAN::Error::from_string("Inode is not a directory"); | ||||||
| 
 | 
 | ||||||
| 		uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size); | 		BAN::RefCounted<Inode> result; | ||||||
| 		uint32_t data_blocks_found = 0; | 		BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)> function( | ||||||
| 
 | 			[&](const BAN::Vector<uint8_t>& block_data) -> BAN::ErrorOr<bool> | ||||||
| 		for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) |  | ||||||
| 			{ | 			{ | ||||||
| 			if (m_inode.block[0] == 0) | 				uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); | ||||||
| 				continue; | 				uintptr_t entry_addr = (uintptr_t)block_data.data(); | ||||||
| 			data_blocks_found++; | 				while (entry_addr < block_data_end) | ||||||
| 
 |  | ||||||
| 			auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block])); |  | ||||||
| 			uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size(); |  | ||||||
| 			 |  | ||||||
| 			uintptr_t entry_addr = (uintptr_t)inode_data.data(); |  | ||||||
| 			while (entry_addr < inode_data_end) |  | ||||||
| 				{ | 				{ | ||||||
| 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | ||||||
| 				 |  | ||||||
| 					BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); | 					BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); | ||||||
| 				if (entry->inode && name == entry_name) | 					if (entry->inode && file_name == entry_name) | ||||||
| 					{ | 					{ | ||||||
| 						Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode)); | 						Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode)); | ||||||
| 					return BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name)); | 						result = BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name)); | ||||||
|  | 						return false; | ||||||
| 					} | 					} | ||||||
| 
 |  | ||||||
| 					entry_addr += entry->rec_len; | 					entry_addr += entry->rec_len; | ||||||
| 				} | 				} | ||||||
|  | 				return true; | ||||||
| 			} | 			} | ||||||
|  | 		); | ||||||
| 
 | 
 | ||||||
|  | 		TRY(for_each_block(function)); | ||||||
|  | 		if (result) | ||||||
|  | 			return result; | ||||||
| 		return BAN::Error::from_string("Could not find the asked inode"); | 		return BAN::Error::from_string("Could not find the asked inode"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> Ext2Inode::directory_inodes() const | 	BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> Ext2Inode::directory_inodes() | ||||||
| 	{ | 	{ | ||||||
| 		if (!is_directory()) | 		if (!is_directory()) | ||||||
| 			return BAN::Error::from_string("Inode is not a directory"); | 			return BAN::Error::from_string("Inode is not a directory"); | ||||||
| 
 | 
 | ||||||
| 		uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size); |  | ||||||
| 		uint32_t data_blocks_found = 0; |  | ||||||
| 
 |  | ||||||
| 		BAN::Vector<BAN::RefCounted<Inode>> inodes; | 		BAN::Vector<BAN::RefCounted<Inode>> inodes; | ||||||
| 
 | 		BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)> function( | ||||||
| 		// FIXME: implement indirect pointers
 | 			[&](const BAN::Vector<uint8_t>& block_data) -> BAN::ErrorOr<bool> | ||||||
| 		for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) |  | ||||||
| 			{ | 			{ | ||||||
| 			if (m_inode.block[0] == 0) | 				uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); | ||||||
| 				continue; | 				uintptr_t entry_addr = (uintptr_t)block_data.data(); | ||||||
| 			data_blocks_found++; | 				while (entry_addr < block_data_end) | ||||||
| 
 |  | ||||||
| 			auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block])); |  | ||||||
| 			uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size(); |  | ||||||
| 			 |  | ||||||
| 			uintptr_t entry_addr = (uintptr_t)inode_data.data(); |  | ||||||
| 			while (entry_addr < inode_data_end) |  | ||||||
| 				{ | 				{ | ||||||
| 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | ||||||
| 				 |  | ||||||
| 					if (entry->inode) | 					if (entry->inode) | ||||||
| 					{ | 					{ | ||||||
| 						BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); | 						BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); | ||||||
|  | @ -221,13 +317,13 @@ namespace Kernel | ||||||
| 						auto ref_counted_inode = BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(current_inode), entry_name)); | 						auto ref_counted_inode = BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(current_inode), entry_name)); | ||||||
| 						TRY(inodes.push_back(BAN::move(ref_counted_inode))); | 						TRY(inodes.push_back(BAN::move(ref_counted_inode))); | ||||||
| 					} | 					} | ||||||
| 
 |  | ||||||
| 					entry_addr += entry->rec_len; | 					entry_addr += entry->rec_len; | ||||||
| 				} | 				} | ||||||
|  | 				return true; | ||||||
| 			} | 			} | ||||||
|  | 		); | ||||||
| 
 | 
 | ||||||
| 		// FIXME: for now we can just assert that we found everything in direct pointers
 | 		TRY(for_each_block(function)); | ||||||
| 		ASSERT(data_blocks_found == data_block_count); |  | ||||||
| 
 | 
 | ||||||
| 		return inodes; | 		return inodes; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -300,6 +300,32 @@ argument_done: | ||||||
| 				if (!inode->is_directory()) | 				if (!inode->is_directory()) | ||||||
| 					TTY_PRINTLN("  {7} {}", inode->size(), inode->name()); | 					TTY_PRINTLN("  {7} {}", inode->size(), inode->name()); | ||||||
| 		} | 		} | ||||||
|  | 		else if (arguments.front() == "cat") | ||||||
|  | 		{ | ||||||
|  | 			if (!VirtualFileSystem::is_initialized()) | ||||||
|  | 				return TTY_PRINTLN("VFS not initialized :("); | ||||||
|  | 
 | ||||||
|  | 			if (arguments.size() > 2) | ||||||
|  | 				return TTY_PRINTLN("usage: 'cat path'"); | ||||||
|  | 			 | ||||||
|  | 			auto file = VirtualFileSystem::get().root_inode(); | ||||||
|  | 
 | ||||||
|  | 			auto path_parts = MUST(arguments[1].sv().split('/')); | ||||||
|  | 			for (auto part : path_parts) | ||||||
|  | 			{ | ||||||
|  | 				auto inode_or_error = file->directory_find(part); | ||||||
|  | 				if (inode_or_error.is_error()) | ||||||
|  | 					return TTY_PRINTLN("{}", inode_or_error.get_error().get_message()); | ||||||
|  | 				file = inode_or_error.value(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			auto data_or_error = file->read_all(); | ||||||
|  | 			if (data_or_error.is_error()) | ||||||
|  | 				return TTY_PRINTLN("{}", data_or_error.get_error().get_message()); | ||||||
|  | 
 | ||||||
|  | 			auto& data = data_or_error.value(); | ||||||
|  | 			TTY_PRINTLN("{}", BAN::StringView((const char*)data.data(), data.size())); | ||||||
|  | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			TTY_PRINTLN("unrecognized command '{}'", arguments.front()); | 			TTY_PRINTLN("unrecognized command '{}'", arguments.front()); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue