Max OS 0.3
Loading...
Searching...
No Matches
ext2.cpp
Go to the documentation of this file.
1
10
11using namespace MaxOS;
12using namespace MaxOS::common;
13using namespace MaxOS::filesystem;
14using namespace MaxOS::filesystem::format::ext2;
15using namespace MaxOS::drivers;
16using namespace MaxOS::drivers::disk;
17using namespace MaxOS::drivers::clock;
18
28: disk(disk),
29 partition_offset(partition_offset),
30 superblock({})
31{
32
33 // Read superblock
34 buffer_t superblock_buffer(&superblock, 1024);
35 disk->read(partition_offset + 2, &superblock_buffer, 512);
36 disk->read(partition_offset + 3, &superblock_buffer, 512);
37
38 // Validate signature
39 ASSERT(superblock.signature == 0xEF53, "Ext2 Filesystem doesnt have a valid signature\n");
40
41 // Version 0 has constant inode info
42 if(superblock.version_major < 1) {
43 superblock.first_inode = 11;
44 superblock.inode_size = 128;
45 }
46
47 // Parse the superblock
48 block_size = 1024 << superblock.block_size;
49 total_block_groups = (superblock.total_blocks + superblock.blocks_per_group - 1) / superblock.blocks_per_group;
50 block_group_descriptor_table_block = superblock.starting_block + 1;
51 block_group_descriptor_table_size = total_block_groups * sizeof(block_group_descriptor_t);
52 pointers_per_block = block_size / sizeof(uint32_t);
53 inodes_per_block = block_size / superblock.inode_size;
54 sectors_per_block = block_size / 512;
55
56 // Read the block groups
57 block_groups = new block_group_descriptor_t* [total_block_groups] { nullptr };
58 uint32_t bgdt_lba = partition_offset + block_group_descriptor_table_block * sectors_per_block;
59 uint32_t sectors_to_read = (block_group_descriptor_table_size + block_size - 1) / block_size * sectors_per_block;
61 for(uint32_t i = 0; i < sectors_to_read; ++i)
62 disk->read(bgdt_lba + i, &bg_buffer, 512);
63
64 // Store the block groups
65 for(uint32_t i = 0; i < total_block_groups; ++i) {
66 block_groups[i] = new block_group_descriptor_t;
67 memcpy(block_groups[i], bg_buffer.raw() + i * sizeof(block_group_descriptor_t),
69 }
70}
71
72Ext2Volume::~Ext2Volume() = default;
73
81
82 // Ensure the buffer is in the right format
83 buffer->set_offset(0);
84 bool old = buffer->update_offset;
85 buffer->update_offset = true;
86
87 // Read each sector of the block
88 for(size_t i = 0; i < sectors_per_block; ++i)
90
91 // Reset buffer
92 buffer->set_offset(0);
93 buffer->update_offset = old;
94}
95
103
104 // Locate the inode
105 uint32_t group = (inode_num - 1) / superblock.inodes_per_group;
106 uint32_t index = (inode_num - 1) % superblock.inodes_per_group;
107
108 // Locate the block
109 uint32_t inode_table = block_groups[group]->inode_table_address;
110 uint32_t offset = index * superblock.inode_size;
111 uint32_t block = offset / block_size;
113
114 // Read the inode
115 buffer_t buffer(block_size);
116 read_block(inode_table + block, &buffer);
117 buffer.copy_from(inode, sizeof(inode_t), in_block_offset);
118
119 // Modify the block
120 write_block(inode_table + block, &buffer);
121}
122
130
131 // Ensure the buffer is in the right format
132 buffer->set_offset(0);
133
134 // Read each sector of the block
135 for(size_t i = 0; i < sectors_per_block; ++i)
137
138 // Reset buffer
139 buffer->set_offset(0);
140
141}
142
150
151 inode_t inode;
152
153 // Locate the inode
154 uint32_t group = (inode_num - 1) / superblock.inodes_per_group;
155 uint32_t index = (inode_num - 1) % superblock.inodes_per_group;
156
157 // Locate the block
158 uint32_t inode_table = block_groups[group]->inode_table_address;
159 uint32_t offset = index * superblock.inode_size;
160 uint32_t block = offset / block_size;
162
163 // Read the block
164 buffer_t buffer(block_size);
165 read_block(inode_table + block, &buffer);
166
167 // Read the inode from the block
168 buffer.copy_to(&inode, sizeof(inode_t), in_block_offset);
169 return inode;
170}
171
181
189
190 // No blocks to allocate
191 if(!amount)
192 return { 1, 0 };
193
194 // Find the block group with enough free blocks
197 if(block_group->free_blocks >= amount)
198 return allocate_group_blocks(bg_index, amount);
199
200 // No block group can contain the block so split across multiple
202 while(amount > 0) {
203
204 // Find the block group with most free blocks
206 uint32_t bg_index = 0;
208 if(block_groups[bg_index]->free_blocks > block_group->free_blocks)
210
211 // No space
212 if(block_group->free_blocks == 0)
213 return { 1, 0 };
214
215 // Allocate the remaining blocks
216 auto allocated = allocate_group_blocks(bg_index, 1);
217 amount -= allocated.size();
218 for(auto block : allocated)
219 result.push_back(block);
220 }
221
222 return result;
223}
224
232common::Vector<uint32_t> Ext2Volume::allocate_group_blocks(uint32_t block_group, uint32_t amount) {
233
234 // Ensure enough space
236 if(amount > descriptor->free_blocks)
237 return { 1, 0 };
238
239 // Prepare
242 zeros.clear();
243
244 // Read bitmap
246 read_block(descriptor->block_usage_bitmap, &bitmap);
247
248 // Allocate the blocks
249 for(uint32_t i = 0; i < superblock.blocks_per_group; ++i) {
250
251 // Block is already used
252 if((bitmap.raw()[i / 8] & (1u << (i % 8))) != 0)
253 continue;
254
255 // Mark as used
256 descriptor->free_blocks--;
257 superblock.unallocated_blocks--;
258 bitmap.raw()[i / 8] |= (uint8_t) (1u << (i % 8));
259
260 // Zero out data
261 uint32_t block = block_group * superblock.blocks_per_group + superblock.starting_block + i;
263 result.push_back(block);
264
265 // All done
266 amount--;
267 if(!amount)
268 break;
269 }
270
271 // Save the changed metadata
272 write_block(descriptor->block_usage_bitmap, &bitmap);
273 write_back_block_groups();
274 write_back_superblock();
275
276 return result;
277}
278
287void Ext2Volume::free_group_blocks(uint32_t block_group, uint32_t amount, uint32_t start) {
288
289 // Read bitmap
292 read_block(descriptor->block_usage_bitmap, &bitmap);
293
294 // Convert start to be index based on the group instead of global
295 start -= (block_group * superblock.blocks_per_group + superblock.starting_block);
296
297 // Free the blocks
298 for(uint32_t i = start; i < start + amount; ++i) {
299
300 // Block is already free (shouldn't happen)
301 if((bitmap.raw()[i / 8] & (1u << (i % 8))) == 0)
302 continue;
303
304 // Mark as free
305 descriptor->free_blocks++;
306 superblock.unallocated_blocks++;
307 bitmap.raw()[i / 8] &= ~(1u << (i % 8));
308 }
309
310 // Save the changed metadata
311 write_block(descriptor->block_usage_bitmap, &bitmap);
312 write_back_block_groups();
313 write_back_superblock();
314
315}
316
317
321void Ext2Volume::write_back_block_groups() const {
322
323 // Locate the block groups
327
328 // Copy the block groups into the buffer
330 for(uint32_t i = 0; i < total_block_groups; ++i)
331 bg_buffer.copy_from(block_groups[i], sizeof(block_group_descriptor_t));
332
333 // Write the buffer to disk
334 bg_buffer.set_offset(0);
335 for(uint32_t i = 0; i < sectors_to_write; ++i)
336 disk->write(bgdt_lba + i, &bg_buffer, 512);
337}
338
342void Ext2Volume::write_back_superblock() {
343
344 // Store superblock
345 buffer_t buffer(1024);
346 buffer.copy_from(&superblock, sizeof(superblock_t));
347 buffer.set_offset(0);
348
349 // Write to disk
350 disk->write(partition_offset + 2, &buffer, 512);
351 disk->write(partition_offset + 3, &buffer, 512);
352}
353
361 return (bytes + block_size - 1) / block_size;
362}
363
371
372 ext2_lock.lock();
373
374 // Find the block group with enough free inodes
376 uint32_t bg_index = 0;
378 if(block_group->free_inodes >= 1)
379 break;
380
381 // Read bitmap
383 read_block(block_group->block_inode_bitmap, &bitmap);
384
385 // First group contains reserved inodes
387 if(bg_index == 0 && superblock.first_inode > 1)
388 inode_index = superblock.first_inode - 1;
389
390 // Find a free inode
391 for(; inode_index < superblock.inodes_per_group; ++inode_index) {
392
393 // Block is already used
394 if((bitmap.raw()[inode_index / 8] & (1u << (inode_index % 8))) != 0)
395 continue;
396
397 // Mark as used
398 block_group->free_inodes--;
399 superblock.unallocated_inodes--;
400 bitmap.raw()[inode_index / 8] |= (uint8_t) (1u << (inode_index % 8));
401
402 break;
403 }
404
405 // Convert into the 1-based inode index in the group
406 inode_index += bg_index * superblock.inodes_per_group + 1;
407
408 // Save the changed metadata
409 write_block(block_group->block_inode_bitmap, &bitmap);
410 write_back_block_groups();
411 write_back_superblock();
412
413 // Create the inode
414 inode_t inode { };
415 inode.creation_time = time_to_epoch(Clock::active_clock()->get_time());
416 inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time());
417 inode.block_pointers[0] = allocate_block();
418 inode.hard_links = is_directory ? 2 : 1;
419 inode.type = ((uint16_t) (is_directory ? InodeType::DIRECTORY : InodeType::FILE) >> 12) & 0xF;
420 inode.permissions =
421 (uint16_t) (is_directory ? InodePermissionsDefaults::DIRECTORY : InodePermissionsDefaults::FILE) & 0x0FFF;
422 write_inode(inode_index, &inode);
423
425 return inode_index;
426}
427
435
436 // Find the block group containing the inode
437 uint32_t bg_index = (inode - 1) / superblock.inodes_per_group;
439
440 // Read bitmap
442 read_block(block_group->block_inode_bitmap, &bitmap);
443
444 // First group contains reserved inodes
445 uint32_t inode_index = (inode - 1) % superblock.inodes_per_group;
446 if(bg_index == 0 && (inode_index < (superblock.first_inode - 1)))
447 return;
448
449 // Mark as used
450 block_group->free_inodes++;
451 superblock.unallocated_inodes++;
452 bitmap.raw()[inode_index / 8] &= (uint8_t) ~(1u << (inode_index % 8));
453
454 // Save the changed metadata
455 write_block(block_group->block_inode_bitmap, &bitmap);
456 write_back_block_groups();
457 write_back_superblock();
458}
459
465
466 // No blocks to free
467 if(blocks.empty())
468 return;
469
470 uint32_t start = blocks[0];
472 uint32_t amount = 1;
473
474 // Free each adjacent set of blocks
475 for(auto& block : blocks) {
476
477 // First is already accounted for
478 if(block == start)
479 continue;
480
481 // Is this block adjacent
482 if((previous + 1) == block) {
483 previous = block;
484 amount += 1;
485 continue;
486 }
487
488 // Adjacent set has ended
489 uint32_t group = (start - superblock.starting_block) / superblock.blocks_per_group;
490 free_group_blocks(group, amount, start);
491
492 // Reset
493 start = block;
494 previous = start;
495 amount = 1;
496 }
497
498 // Account for the last set of blocks in the loop
499 uint32_t group = (start - superblock.starting_block) / superblock.blocks_per_group;
500 free_group_blocks(group, amount, start);
501}
502
510 : m_volume(volume),
511 inode_number(inode_index),
512 inode(m_volume->read_inode(inode_number)) {
513
514 // Read the block pointers
516 if(inode.block_pointers[direct_pointer])
517 block_cache.push_back(inode.block_pointers[direct_pointer]);
518
519 buffer_t buffer(m_volume->block_size);
520 parse_indirect(1, inode.l1_indirect, &buffer);
521 parse_indirect(2, inode.l2_indirect, &buffer);
522 parse_indirect(3, inode.l3_indirect, &buffer);
523}
524
529size_t InodeHandler::size() const {
530 return ((size_t) inode.size_upper << 32) | (size_t) inode.size_lower;
531}
532
537void InodeHandler::set_size(size_t size) {
538
539 inode.size_lower = (uint32_t) (size & 0xFFFFFFFFULL);
540 inode.size_upper = (uint32_t) (size >> 32);
541
542}
543
551void InodeHandler::parse_indirect(uint32_t level, uint32_t block, buffer_t* buffer) {
552
553 // Invalid
554 if(block == 0)
555 return;
556
557 // Read the block
558 m_volume->read_block(block, buffer);
559 auto* pointers = (uint32_t*) (buffer->raw());
560
561 // Parse the pointers
562 for(size_t i = 0; i < m_volume->pointers_per_block; ++i) {
563 uint32_t pointer = pointers[i];
564
565 // Invaild
566 if(pointer == 0)
567 break;
568
569 // Has indirect sub entries
570 if(level > 1) {
571 parse_indirect(level - 1, pointer, buffer);
572 continue;
573 }
574
575 // Parse the entry
576 block_cache.push_back(pointer);
577 }
578}
579
588void InodeHandler::write_indirect(uint32_t level, uint32_t& block, size_t& index) {
589
590 // Nothing left to write
591 size_t remaining = block_cache.size() - index;
592 if(remaining == 0 || index >= block_cache.size())
593 return;
594
595 // Level hasn't been set yet
596 if(block == 0)
597 block = m_volume->allocate_block();
598
599 // Allocate a local buffer for this recursion level
600 buffer_t buffer(m_volume->block_size);
601 buffer.clear();
602 auto* pointers = (uint32_t*) buffer.raw();
603
604 // Write the pointers
605 for(size_t i = 0; i < m_volume->pointers_per_block; ++i) {
606
607 // Invalid
608 if(index >= block_cache.size())
609 break;
610
611 // Has indirect
612 if(level > 1) {
613 write_indirect(level - 1, pointers[i], index);
614 continue;
615 }
616
617 // Save the pointer
618 pointers[i] = block_cache[index++];
619 }
620
621 m_volume->write_block(block, &buffer);
622}
623
629void InodeHandler::store_blocks(Vector<uint32_t> const& blocks) {
630
631 Logger::DEBUG() << "STORING BLOCKS\n";
632
633 // Store in cache
634 for(auto block : blocks)
635 block_cache.push_back(block);
636
637 // Direct blocks
638 for(uint32_t i = 0; i < 12; ++i)
639 inode.block_pointers[i] = i < block_cache.size() ? block_cache[i] : 0;
640
641 // No need to do any indirects
642 if(block_cache.size() < 12)
643 return;
644
645 // Setup Recursive blocks
646 size_t index = 12;
647
648 // Write the blocks
649 uint32_t indirect_blocks[3] = { inode.l1_indirect, inode.l2_indirect, inode.l3_indirect };
650 for(int i = 0; i < 3; ++i) {
651
652 // Have to use temp because of packed field
654 write_indirect(i + 1, temp, index);
656 }
657
658 // Save the new blocks
659 inode.l1_indirect = indirect_blocks[0];
660 inode.l2_indirect = indirect_blocks[1];
661 inode.l3_indirect = indirect_blocks[2];
662
663 // NOTE: Blocks get allocated when writing indirects. This is then saved later in the write() function
664}
665
675size_t InodeHandler::grow(size_t amount, bool flush) {
676
677 // Nothing to grow
678 if(amount <= 0)
679 return size();
680
681 // Allocate new blocks
682 auto blocks = m_volume->allocate_blocks(m_volume->bytes_to_blocks(amount));
683 ASSERT(blocks[0] != 0, "Failed to allocate new blocks for file");
684
685 // Save the changes
686 store_blocks(blocks);
687 set_size(size() + amount);
688 if(flush)
689 save();
690
691 return size() + amount;
692}
693
698
699 m_volume->write_inode(inode_number, &inode);
700}
701
706
707 m_volume->ext2_lock.lock();
708
709 // Free the inode
710 m_volume->free_blocks(block_cache);
711 m_volume->free_inode(inode_number);
712
713 m_volume->ext2_lock.unlock();
714}
715
716InodeHandler::~InodeHandler() = default;
717
725Ext2File::Ext2File(Ext2Volume* volume, uint32_t inode, string const& name)
726 : m_volume(volume),
727 m_inode(volume, inode) {
728
729 // Set up the base information
730 m_name = name;
731 m_size = m_inode.size();
732
733}
734
741void Ext2File::write(buffer_t* data, size_t amount) {
742
743 // Nothing to write
744 if(amount == 0)
745 return;
746
747 // Prepare for writing
748 m_volume->ext2_lock.lock();
749 const uint32_t block_size = m_volume->block_size;
750 buffer_t buffer(block_size);
751
752 // Expand the file
753 if(m_offset + amount > m_size)
754 m_size = m_inode.grow((m_offset + amount) - m_size, false);
755
756 // Save the updated metadata
757 m_inode.inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time());
758 m_inode.save();
759
760 // Convert bytes to blocks
761 uint32_t block_start = m_offset / block_size;
762 uint32_t block_offset = m_offset % block_size;
763
764 // Write each block
765 size_t current_block = block_start;
766 size_t written = 0;
767 while(written < amount) {
768
769 // Read the block
771 m_volume->read_block(block, &buffer);
772
773 // Where in this block to start writing
774 size_t buffer_start = (current_block - 1 == block_start) ? block_offset : 0;
775 size_t writable = (amount - written < block_size - buffer_start) ? (amount - written) : (block_size -
777
778 // Update the block
779 buffer.copy_from(data, writable, buffer_start, written);
780 m_volume->write_block(block, &buffer);
781 written += writable;
782 }
783
784 // Clean up
785 m_offset += amount;
786 m_volume->ext2_lock.unlock();
787}
788
795void Ext2File::read(buffer_t* data, size_t amount) {
796
797 // Nothing to read
798 if(m_size == 0 || amount == 0)
799 return;
800
801 // Prepare for reading
802 m_volume->ext2_lock.lock();
803 const uint32_t block_size = m_volume->block_size;
804 buffer_t buffer(block_size);
805
806 // Force bounds
807 if(m_offset + amount > m_size)
809
810 // Convert bytes to blocks
811 uint32_t block_start = m_offset / block_size;
812 uint32_t block_offset = m_offset % block_size;
813
814 // Read each block
815 size_t current_block = block_start;
816 size_t read = 0;
817 while(read < amount) {
818
819 // Read the block
821 m_volume->read_block(block, &buffer);
822
823 // Where in this block to start reading
824 size_t buffer_start = (current_block - 1 == block_start) ? block_offset : 0;
825 size_t readable = (amount - read < block_size - buffer_start) ? (amount - read) : (block_size - buffer_start);
826
827 // Read the block
828 buffer.copy_to(data, readable, buffer_start, read);
829 read += readable;
830
831 }
832
833 // Clean up
834 m_offset += amount;
835 m_volume->ext2_lock.unlock();
836}
837
842 File::flush();
843}
844
845Ext2File::~Ext2File() = default;
846
855 : m_volume(volume),
856 m_inode(m_volume, inode) {
857 m_name = name;
858}
859
863void Ext2Directory::parse_block(buffer_t* buffer) {
864
865 size_t offset = 0;
866 while(offset < m_volume->block_size) {
867
868 // Read the entry
869 auto* entry = (directory_entry_t*) (buffer->raw() + offset);
870 m_entries.push_back(*entry);
871
872 // Not valid
873 if(entry->inode == 0 || entry->name_length == 0)
874 break;
875
876 // Parse
877 string filename(buffer->raw() + offset + sizeof(directory_entry_t), entry->name_length);
878 m_entry_names.push_back(filename);
879 uint32_t inode = entry->inode;
880
881 // Create the object
882 switch((EntryType) entry->type) {
883
884 case EntryType::FILE:
885 m_files.push_back(new Ext2File(m_volume, inode, filename));
886 break;
887
888 case EntryType::DIRECTORY:
889 m_subdirectories.push_back(new Ext2Directory(m_volume, inode, filename));
890 break;
891
892 default:
893 Logger::WARNING() << "Unknown entry type: " << entry->type << "\n";
894
895 }
896
897 // Go to next
898 offset += entry->size;
899 }
900}
901
909void Ext2Directory::remove_entry(string const& name, bool is_directory, bool clear) {
910
911 // Find the entry
912 uint32_t index = 0;
913 directory_entry_t* entry = nullptr;
914 for(; index < m_entries.size(); ++index)
915 if(m_entry_names[index] == name) {
916 entry = &m_entries[index];
917 break;
918 }
919
920 // No entry found
921 if(!entry || entry->type != (uint8_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE))
922 return;
923
924 // Clear the inode
925 if(clear) {
926 InodeHandler inode(m_volume, entry->inode);
927 inode.free();
928 }
929
930 // Remove the reference from this directory
931 m_entries.erase(entry);
932 m_entry_names.erase(m_entry_names.begin() + index);
933 write_entries();
934}
935
943void Ext2Directory::rename_entry(string const& old_name, string const& new_name, bool is_directory) {
944
945 // Change the name
946 for(uint32_t i = 0; i < m_entry_names.size(); ++i)
947 if(m_entry_names[i] == old_name &&
948 m_entries[i].type == (uint8_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE))
949 m_entry_names[i] = new_name;
950
951 // Save the change
952 write_entries();
953
954}
955
960
961 m_volume->ext2_lock.lock();
962 m_entries.clear();
963 m_entry_names.clear();
964
965 // Clear the old files & Directories
966 for(auto& file : m_files)
967 delete file;
968 m_files.clear();
969
970 for(auto& directory : m_subdirectories)
971 delete directory;
972 m_subdirectories.clear();
973
974 // Read the direct blocks (cant use for( : ))
975 buffer_t buffer(m_volume->block_size);
976 for(unsigned int block_pointer : m_inode.block_cache) {
977
978 // Invalid block
979 if(block_pointer == 0)
980 break;
981
982 // Parse the block
983 m_volume->read_block(block_pointer, &buffer);
984 parse_block(&buffer);
985 }
986
987 m_volume->ext2_lock.unlock();
988}
989
993void Ext2Directory::write_entries() {
994
995 // Calculate the size needed to store the entries and the null entry
996 size_t size_required = sizeof(directory_entry_t);
997 for(uint32_t i = 0; i < m_entries.size(); ++i) {
998 size_t size = sizeof(directory_entry_t) + m_entry_names[i].length() + 1;
999 size += (size % 4) ? 4 - size % 4 : 0;
1001 }
1002
1003 // Expand the directory
1004 size_t blocks_required = m_volume->bytes_to_blocks(size_required);
1005 if(blocks_required > m_inode.block_cache.size())
1006 m_inode.grow((blocks_required - m_inode.block_cache.size()) * m_volume->block_size, false);
1007
1008 // Prepare for writing
1009 m_volume->ext2_lock.lock();
1010 const uint32_t block_size = m_volume->block_size;
1011 buffer_t buffer(block_size, false);
1012 buffer.clear();
1013
1014 // Save the updated metadata
1015 m_inode.set_size(blocks_required * block_size);
1016 m_inode.inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time());
1017 m_inode.save();
1018
1019 // Write each entry
1020 size_t current_block = 0;
1021 size_t buffer_offset = 0;
1022 for(uint32_t i = 0; i < m_entries.size(); ++i) {
1023
1024 // Get the current entry
1025 directory_entry_t& entry = m_entries[i];
1026 char* name = m_entry_names[i].c_str();
1027
1028 // Update the size
1029 entry.name_length = m_entry_names[i].length();
1030 entry.size = sizeof(directory_entry_t) + entry.name_length + 1;
1031 entry.size += (entry.size % 4) ? 4 - (entry.size % 4) : 0;
1032
1033 // Entry needs to be stored in the next block
1034 if(entry.size + buffer_offset > block_size) {
1035 m_volume->write_block(m_inode.block_cache[current_block], &buffer);
1036 buffer.clear();
1037 current_block++;
1038 buffer_offset = 0;
1039 }
1040
1041 // If it is the last entry it takes up the rest of the block
1042 if(i == m_entries.size() - 1)
1043 entry.size = block_size - buffer_offset;
1044
1045 // Copy the entry and the name
1046 buffer.copy_from(&entry, sizeof(entry), buffer_offset);
1047 buffer.copy_from(name, entry.name_length + 1, buffer_offset + sizeof(entry));
1048 buffer_offset += entry.size;
1049 }
1050
1051 // Save the last block
1052 m_volume->write_block(m_inode.block_cache[current_block], &buffer);
1053
1054 // Clean up
1055 m_volume->ext2_lock.unlock();
1056}
1057
1066directory_entry_t Ext2Directory::create_entry(const string& name, uint32_t inode, bool is_directory) {
1067
1068 // Create the inode
1069 directory_entry_t entry { };
1070 entry.inode = inode ? inode : m_volume->create_inode(is_directory);
1071 entry.type = (uint32_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE);
1072 entry.name_length = name.length();
1073 entry.size = sizeof(entry) + entry.name_length;
1074 entry.size += entry.size % 4 ? 4 - entry.size % 4 : 0;
1075
1076 // Save the inode
1077 m_entries.push_back(entry);
1078 m_entry_names.push_back(name);
1079 write_entries();
1080
1081 return entry;
1082}
1083
1090File* Ext2Directory::create_file(string const& name) {
1091
1092 // Check if the file already exists
1093 for(auto& file : m_files)
1094 if(file->name() == name)
1095 return nullptr;
1096
1097 // Create the file
1098 auto file = new Ext2File(m_volume, create_entry(name, 0, false).inode, name);
1099 m_files.push_back(file);
1100 return file;
1101}
1102
1108void Ext2Directory::remove_file(string const& name) {
1109 remove_entry(name, false);
1110}
1111
1118void Ext2Directory::rename_file(string const& old_name, string const& new_name) {
1119 rename_entry(old_name, new_name, false);
1120}
1121
1129
1130 // Check if the directory already exists
1131 for(auto& subdirectory : m_subdirectories)
1132 if(subdirectory->name() == name)
1133 return nullptr;
1134
1135 // Store the directory
1136 auto directory = new Ext2Directory(m_volume, create_entry(name, 0, true).inode, name);
1137 m_subdirectories.push_back(directory);
1138
1139 // Create self & parent references
1140 directory->create_entry(".", directory->m_inode.inode_number, true);
1141 directory->create_entry("..", m_inode.inode_number, true);
1142
1143 return directory;
1144}
1145
1151void Ext2Directory::remove_subdirectory(string const& name) {
1152 remove_entry(name, true);
1153}
1154
1161void Ext2Directory::rename_subdirectory(string const& old_name, string const& new_name) {
1162 rename_entry(old_name, new_name, true);
1163}
1164
1165Ext2Directory::~Ext2Directory() = default;
1166
1174 : m_volume(disk, partition_offset) {
1175
1176 // Create the root directory
1177 m_root_directory = new Ext2Directory(&m_volume, 2, "/");
1179
1180}
1181
static Logger DEBUG()
Gets active logger set to DEBUG level.
Definition logger.cpp:193
static Logger WARNING()
Gets active logger set to WARNING level.
Definition logger.cpp:205
Wrapper class for a region of bytes in memor in an attempt to add some memory safety....
Definition buffer.h:25
void set_offset(size_t offset)
Set the offset for where operations should begin from.
Definition buffer.cpp:128
uint8_t * raw() const
The raw pointer to the bytes stored in memory, use is not recommended.
Definition buffer.cpp:57
bool update_offset
Should operations write/read/copy update the offset in the buffer?
Definition buffer.h:44
void copy_to(Buffer *buffer)
Copies all the bytes from this buffer into another buffer.
Definition buffer.cpp:282
void copy_from(const Buffer *buffer)
Copies all the bytes from another buffer into this buffer.
Definition buffer.cpp:195
void clear()
Fulls the buffer with 0's and resets the offset.
Definition buffer.cpp:65
Stores the left, top, width and height of a rectangle.
Definition rectangle.h:22
void lock()
Lock the spinlock once it is available.
Definition spinlock.cpp:24
void unlock()
Unlock the spinlock.
Definition spinlock.cpp:32
uint32_t size() const
Returns the number of elements in the Vector.
Definition vector.h:273
void clear()
Removes all elements from the Vector.
Definition vector.h:461
iterator push_back(Type)
Adds an element to the end of the vector and returns the iterator of the element.
Definition vector.h:333
iterator begin() const
Returns the first element of the Vector.
Definition vector.h:283
void erase(Type)
Removes all elements from the Vector that are equal to the element.
Definition vector.h:415
static Clock * active_clock()
Gets the currently active clock.
Definition clock.cpp:208
Generic Disk, handles the reading and writing of data to the hard drive.
Definition disk.h:24
void write(uint32_t sector, common::buffer_t *data)
write data to the disk from a buffer (max capacity 512 bytes)
Definition disk.cpp:50
void read(uint32_t sector, common::buffer_t *data_buffer)
read data from the disk into a buffer (max capacity 512 bytes)
Definition disk.cpp:26
Handles a group of files (directory)
Definition filesystem.h:57
common::Vector< Directory * > m_subdirectories
The subdirectories in this directory.
Definition filesystem.h:60
string m_name
The name of this directory.
Definition filesystem.h:62
size_t size()
Get the size of the directory.
common::Vector< File * > m_files
The files in this directory.
Definition filesystem.h:59
virtual void read_from_disk()
read the directory from the disk
string name()
Get the name of the directory.
Directory * m_root_directory
The fist directory in the file system (not be confused with the system root)
Definition filesystem.h:98
Handles file operations and information.
Definition filesystem.h:31
size_t m_size
The size of the file.
Definition filesystem.h:36
string m_name
The name of the file.
Definition filesystem.h:35
uint32_t m_offset
The current offset in the file.
Definition filesystem.h:34
string name()
Get the name of the file.
virtual void flush()
Flush the file to the disk.
Handles the directory operations on the ext2 filesystem.
Definition ext2.h:420
Directory * create_subdirectory(const string &name) final
Create a new directory in the directory.
Definition ext2.cpp:1128
void remove_subdirectory(const string &name) final
Remove a directory entry from the directory.
Definition ext2.cpp:1151
void read_from_disk() final
read the directory from the inode on the disk
Definition ext2.cpp:959
File * create_file(const string &name) final
Create a new file in the directory.
Definition ext2.cpp:1090
void remove_file(const string &name) final
Delete a file from this directory.
Definition ext2.cpp:1108
Ext2Directory(Ext2Volume *volume, uint32_t inode, const string &name)
Construct a new Ext2 Directory object.
Definition ext2.cpp:854
void rename_file(const string &old_name, const string &new_name) final
Renames the file from the old name to the new name if it exists.
Definition ext2.cpp:1118
void rename_subdirectory(const string &old_name, const string &new_name) final
Renames the directory from the old name to the new name if it exists.
Definition ext2.cpp:1161
Ext2FileSystem(drivers::disk::Disk *disk, uint32_t partition_offset)
Construct a new Ext2 File System object.
Definition ext2.cpp:1173
~Ext2FileSystem() final
Destroy the Ext2 File System object and free the root directory.
Definition ext2.cpp:1185
Handles the file operations on the ext2 filesystem.
Definition ext2.h:402
void write(common::buffer_t *data, size_t amount) final
write data to the file (at the current seek position, updated to be += amount)
Definition ext2.cpp:741
void read(common::buffer_t *data, size_t amount) final
read data from the file (at the current seek position, updated to be += amount)
Definition ext2.cpp:795
Ext2File(Ext2Volume *volume, uint32_t inode, const string &name)
Construct a new Ext2 File object.
Definition ext2.cpp:725
void flush() final
Flush the file to the disk.
Definition ext2.cpp:841
Common operations for an ext2 volume that are used by both files & directories (like block & inode al...
Definition ext2.h:322
common::Spinlock ext2_lock
Lock for synchronised access to the volume (.
Definition ext2.h:350
size_t block_size
How large each block is (in bytes)
Definition ext2.h:342
block_group_descriptor_t ** block_groups
The block group descriptors.
Definition ext2.h:340
inode_t read_inode(uint32_t inode_num) const
read an inode from the filesystem
Definition ext2.cpp:149
void free_inode(uint32_t inode)
Mark an inode as free. Note: does NOT unallocated the inodes blocks, use free_group_blocks().
Definition ext2.cpp:434
Ext2Volume(drivers::disk::Disk *disk, lba_t partition_offset)
Construct a new Ext2 Volume object, reads the superblock and block group descriptors.
Definition ext2.cpp:27
uint32_t block_group_descriptor_table_block
Where the block group descriptor table starts.
Definition ext2.h:343
uint32_t create_inode(bool is_directory)
Allocates a new inode and sets the base metadata. Also allocates block 0 of the inode.
Definition ext2.cpp:370
size_t pointers_per_block
How many block pointers fit in a block.
Definition ext2.h:346
lba_t partition_offset
How far into the disk this partition starts.
Definition ext2.h:337
void write_block(uint32_t block_num, common::buffer_t *buffer) const
write a single block from a buffer into onto the disk
Definition ext2.cpp:80
uint32_t block_group_descriptor_table_size
How many block groups are in the block group descriptor table.
Definition ext2.h:344
superblock_t superblock
The superblock of the ext2 filesystem.
Definition ext2.h:339
uint32_t sectors_per_block
How many sectors does a block take.
Definition ext2.h:348
uint32_t total_block_groups
How many block groups are in the filesystem.
Definition ext2.h:345
void write_inode(uint32_t inode_num, inode_t *inode) const
write an inode to the filesystem
Definition ext2.cpp:102
common::Vector< uint32_t > allocate_blocks(uint32_t amount)
Allocates a set of blocks to be used by an inode.
Definition ext2.cpp:188
uint32_t allocate_block()
Allocates a single block to be used by an inode.
Definition ext2.cpp:177
void read_block(uint32_t block_num, common::buffer_t *buffer) const
Reads a single block from the disk into a buffer.
Definition ext2.cpp:129
drivers::disk::Disk * disk
The disk that this volume is on.
Definition ext2.h:336
uint32_t bytes_to_blocks(size_t bytes) const
How many blocks are needed to contain a set amount of bytes.
Definition ext2.cpp:360
void free_blocks(const common::Vector< uint32_t > &blocks)
Frees a group of blocks. Preferably with adjacent blocks apearing next to each other but not enforced...
Definition ext2.cpp:464
Simplfies the management of an inode & its blocks.
Definition ext2.h:372
uint32_t inode_number
The index of the inode.
Definition ext2.h:385
void free()
Marks this inode's blocks as free and then the inode as free.
Definition ext2.cpp:705
inode_t inode
The inode metadata for this file/directory.
Definition ext2.h:386
size_t grow(size_t amount, bool flush=true)
Increase the size of the inode's storage capacity by allocating new blocks.
Definition ext2.cpp:675
size_t size() const
read the size upper and lower into a single size_t
Definition ext2.cpp:529
InodeHandler(Ext2Volume *volume, uint32_t inode)
Construct a new Inode Handler object. Reads the inode and caches the block pointers.
Definition ext2.cpp:509
common::Vector< uint32_t > block_cache
All the blocks used by this inode.
Definition ext2.h:387
void set_size(size_t size)
store the size of the inode data. (does not write to disk)
Definition ext2.cpp:537
void save()
Writes the inode meta data to disk.
Definition ext2.cpp:697
Defines structures and enums for the Extended 2 (ext2) filesystem format and relevant Filesystem,...
struct PACKED MaxOS::filesystem::format::ext2::SuperBlock superblock_t
Alias for SuperBlock struct.
EntryType
The type of a directory entry.
Definition ext2.h:305
struct PACKED MaxOS::filesystem::format::ext2::DirectoryEntry directory_entry_t
Alias for DirectoryEntry struct.
struct PACKED MaxOS::filesystem::format::ext2::BlockGroupDescriptor block_group_descriptor_t
Alias for BlockGroupDescriptor struct.
struct PACKED MaxOS::filesystem::format::ext2::Inode inode_t
Alias for Inode struct.
uint32_t lba_t
Logical Block Addressing type.
Definition filesystem.h:24
#define ASSERT(condition, format,...)
If the specified condition is not met then the kernel will crash with the specified message.
Definition logger.h:100
void * memcpy(void *destination, const void *source, uint64_t num)
Copies a block of memory from one location to another.
Definition memoryIO.cpp:165