forked from Bananymous/banan-os
				
			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::ErrorOr<BAN::Vector<uint8_t>> read_all() const override; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const override; | ||||
| 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const override; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() override; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() 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: | ||||
| 		Ext2Inode() {} | ||||
|  |  | |||
|  | @ -20,9 +20,9 @@ namespace Kernel | |||
| 
 | ||||
| 		virtual BAN::StringView name() const = 0; | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() const = 0; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const = 0; | ||||
| 		virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const = 0; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() = 0; | ||||
| 		virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() = 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; | ||||
| 	} | ||||
| 	 | ||||
| 	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()) | ||||
| 			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; | ||||
| 
 | ||||
| 		for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) | ||||
| 		BAN::RefCounted<Inode> result; | ||||
| 		BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)> function( | ||||
| 			[&](const BAN::Vector<uint8_t>& block_data) -> BAN::ErrorOr<bool> | ||||
| 			{ | ||||
| 			if (m_inode.block[0] == 0) | ||||
| 				continue; | ||||
| 			data_blocks_found++; | ||||
| 
 | ||||
| 			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) | ||||
| 				uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); | ||||
| 				uintptr_t entry_addr = (uintptr_t)block_data.data(); | ||||
| 				while (entry_addr < block_data_end) | ||||
| 				{ | ||||
| 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | ||||
| 				 | ||||
| 					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)); | ||||
| 					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; | ||||
| 				} | ||||
| 				return true; | ||||
| 			} | ||||
| 		); | ||||
| 
 | ||||
| 		TRY(for_each_block(function)); | ||||
| 		if (result) | ||||
| 			return result; | ||||
| 		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()) | ||||
| 			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; | ||||
| 
 | ||||
| 		// FIXME: implement indirect pointers
 | ||||
| 		for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) | ||||
| 		BAN::Function<BAN::ErrorOr<bool>(const BAN::Vector<uint8_t>&)> function( | ||||
| 			[&](const BAN::Vector<uint8_t>& block_data) -> BAN::ErrorOr<bool> | ||||
| 			{ | ||||
| 			if (m_inode.block[0] == 0) | ||||
| 				continue; | ||||
| 			data_blocks_found++; | ||||
| 
 | ||||
| 			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) | ||||
| 				uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); | ||||
| 				uintptr_t entry_addr = (uintptr_t)block_data.data(); | ||||
| 				while (entry_addr < block_data_end) | ||||
| 				{ | ||||
| 					Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; | ||||
| 				 | ||||
| 					if (entry->inode) | ||||
| 					{ | ||||
| 						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)); | ||||
| 						TRY(inodes.push_back(BAN::move(ref_counted_inode))); | ||||
| 					} | ||||
| 
 | ||||
| 					entry_addr += entry->rec_len; | ||||
| 				} | ||||
| 				return true; | ||||
| 			} | ||||
| 		); | ||||
| 
 | ||||
| 		// FIXME: for now we can just assert that we found everything in direct pointers
 | ||||
| 		ASSERT(data_blocks_found == data_block_count); | ||||
| 		TRY(for_each_block(function)); | ||||
| 
 | ||||
| 		return inodes; | ||||
| 	} | ||||
|  |  | |||
|  | @ -300,6 +300,32 @@ argument_done: | |||
| 				if (!inode->is_directory()) | ||||
| 					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 | ||||
| 		{ | ||||
| 			TTY_PRINTLN("unrecognized command '{}'", arguments.front()); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue