| 1 | #include <fs/ext2.h> |
| 2 | #include <stdbool.h> |
| 3 | #include <stdint.h> |
| 4 | #include <string.h> |
| 5 | |
| 6 | static uint32_t ext2_get_block(struct ext2_fs *fs, uint32_t block_num, |
| 7 | uint32_t depth, uint32_t block_index, |
| 8 | uint32_t new_block_num, bool allocate, |
| 9 | bool *was_allocated) { |
| 10 | if (depth == 0) { |
| 11 | // actual data block |
| 12 | return block_num; |
| 13 | } |
| 14 | |
| 15 | uint32_t pointers_per_block = fs->block_size / sizeof(uint32_t); |
| 16 | |
| 17 | bool allocated_this_level = false; |
| 18 | struct bcache_entry *ent; |
| 19 | uint32_t *block = (uint32_t *) ext2_block_read(fs, block_num, out: &ent); |
| 20 | |
| 21 | if (block_num == 0) { |
| 22 | if (!allocate) { |
| 23 | return 0; |
| 24 | } |
| 25 | |
| 26 | block_num = ext2_alloc_block(fs); |
| 27 | if (block_num == 0) { |
| 28 | return 0; |
| 29 | } |
| 30 | |
| 31 | allocated_this_level = true; |
| 32 | |
| 33 | block = (uint32_t *) ext2_block_read(fs, block_num, out: &ent); |
| 34 | } |
| 35 | |
| 36 | if (allocated_this_level) { |
| 37 | memset(block, 0, fs->block_size); |
| 38 | } |
| 39 | |
| 40 | uint32_t index = block_index; |
| 41 | uint32_t divisor = 1; |
| 42 | for (uint32_t i = 1; i < depth; i++) |
| 43 | divisor *= pointers_per_block; |
| 44 | |
| 45 | uint32_t entry_index = index / divisor; |
| 46 | uint32_t entry_offset = index % divisor; |
| 47 | uint32_t bnum = block[entry_index]; |
| 48 | bcache_ent_release(ent); |
| 49 | |
| 50 | uint32_t result; |
| 51 | result = ext2_get_block(fs, block_num: bnum, depth: depth - 1, block_index: entry_offset, new_block_num, |
| 52 | allocate, was_allocated); |
| 53 | bcache_ent_acquire(ent); |
| 54 | |
| 55 | if (result && block[entry_index] == 0 && allocate) { |
| 56 | block[entry_index] = result; |
| 57 | bcache_ent_release(ent); |
| 58 | |
| 59 | ext2_block_write(fs, ent, EXT2_PRIO_DIRENT); |
| 60 | |
| 61 | bcache_ent_acquire(ent); |
| 62 | } else if (allocated_this_level && result == 0) { |
| 63 | ext2_free_block(fs, block_num); |
| 64 | } |
| 65 | |
| 66 | bcache_ent_release(ent); |
| 67 | |
| 68 | return result; |
| 69 | } |
| 70 | |
| 71 | uint32_t ext2_get_or_set_block(struct ext2_fs *fs, struct ext2_inode *inode, |
| 72 | uint32_t block_index, uint32_t new_block_num, |
| 73 | bool allocate, bool *was_allocated) { |
| 74 | if (!fs || !inode) |
| 75 | return (uint32_t) -1; |
| 76 | |
| 77 | if (was_allocated) |
| 78 | *was_allocated = false; |
| 79 | |
| 80 | uint32_t pointers_per_block = fs->block_size / sizeof(uint32_t); |
| 81 | |
| 82 | if (block_index < 12) { |
| 83 | if (inode->block[block_index] == 0 && allocate) { |
| 84 | if (new_block_num == 0) |
| 85 | new_block_num = ext2_alloc_block(fs); |
| 86 | |
| 87 | if (new_block_num == 0) |
| 88 | return 0; |
| 89 | |
| 90 | inode->block[block_index] = new_block_num; |
| 91 | if (was_allocated) |
| 92 | *was_allocated = true; |
| 93 | } |
| 94 | return inode->block[block_index]; |
| 95 | } |
| 96 | |
| 97 | block_index -= 12; |
| 98 | |
| 99 | if (block_index < pointers_per_block) { |
| 100 | return ext2_get_block(fs, block_num: inode->block[12], depth: 1, block_index, |
| 101 | new_block_num, allocate, was_allocated); |
| 102 | } |
| 103 | |
| 104 | block_index -= pointers_per_block; |
| 105 | |
| 106 | if (block_index < pointers_per_block * pointers_per_block) { |
| 107 | return ext2_get_block(fs, block_num: inode->block[13], depth: 2, block_index, |
| 108 | new_block_num, allocate, was_allocated); |
| 109 | } |
| 110 | |
| 111 | block_index -= pointers_per_block * pointers_per_block; |
| 112 | |
| 113 | uint64_t max_triple = |
| 114 | (uint64_t) pointers_per_block * pointers_per_block * pointers_per_block; |
| 115 | if ((uint64_t) block_index < max_triple) { |
| 116 | return ext2_get_block(fs, block_num: inode->block[14], depth: 3, block_index, |
| 117 | new_block_num, allocate, was_allocated); |
| 118 | } |
| 119 | |
| 120 | return (uint32_t) -1; |
| 121 | } |
| 122 | |
| 123 | void ext2_init_inode(struct ext2_inode *new_inode, mode_t mode) { |
| 124 | new_inode->mode = mode; |
| 125 | new_inode->uid = 0; |
| 126 | new_inode->gid = 0; // TODO: these |
| 127 | new_inode->size = 0; |
| 128 | new_inode->atime = time_get_unix(); |
| 129 | new_inode->ctime = new_inode->mtime = new_inode->atime; |
| 130 | new_inode->links_count = ((mode & EXT2_S_IFDIR) ? 2 : 1); |
| 131 | new_inode->blocks = 0; |
| 132 | new_inode->flags = 0; |
| 133 | } |
| 134 | |
| 135 | void ext2_init_dirent(struct ext2_fs *fs, struct ext2_dir_entry *new_entry, |
| 136 | inode_t inode_num, const char *name, uint8_t type) { |
| 137 | new_entry->inode = inode_num; |
| 138 | new_entry->name_len = strlen(str: name); |
| 139 | new_entry->rec_len = fs->block_size; |
| 140 | new_entry->file_type = type; |
| 141 | memcpy(new_entry->name, name, new_entry->name_len); |
| 142 | } |
| 143 | |
| 144 | uint8_t (mode_t mode) { |
| 145 | uint8_t file_type; |
| 146 | if (mode & EXT2_S_IFDIR) |
| 147 | file_type = EXT2_FT_DIR; |
| 148 | else if (mode & EXT2_S_IFREG) |
| 149 | file_type = EXT2_FT_REG_FILE; |
| 150 | else if (mode & EXT2_S_IFLNK) |
| 151 | file_type = EXT2_FT_SYMLINK; |
| 152 | else |
| 153 | file_type = EXT2_FT_UNKNOWN; |
| 154 | |
| 155 | return file_type; |
| 156 | } |
| 157 | |