| 1 | #include <block/bcache.h> |
| 2 | #include <errno.h> |
| 3 | #include <fs/ext2.h> |
| 4 | #include <stdbool.h> |
| 5 | #include <stdint.h> |
| 6 | #include <string.h> |
| 7 | |
| 8 | struct link_ctx { |
| 9 | const char *name; |
| 10 | inode_t inode; |
| 11 | inode_t dir_inode; |
| 12 | uint8_t type; |
| 13 | bool success; |
| 14 | }; |
| 15 | |
| 16 | static bool link_callback(struct ext2_fs *fs, struct ext2_dir_entry *entry, |
| 17 | void *ctx_ptr, uint32_t block_num, uint32_t e, |
| 18 | uint32_t o) { |
| 19 | (void) fs; // dont complain compiler |
| 20 | (void) block_num; |
| 21 | (void) e; |
| 22 | (void) o; |
| 23 | struct link_ctx *ctx = (struct link_ctx *) ctx_ptr; |
| 24 | |
| 25 | uint32_t actual_size = 8 + ((entry->name_len + 3) & ~3); // alignment |
| 26 | uint32_t needed_size = 8 + ((strlen(str: ctx->name) + 3) & ~3U); |
| 27 | |
| 28 | if ((entry->rec_len - actual_size) >= needed_size) { |
| 29 | uint8_t *entry_base = (uint8_t *) entry; |
| 30 | |
| 31 | uint32_t original_rec_len = entry->rec_len; |
| 32 | |
| 33 | entry->rec_len = actual_size; |
| 34 | |
| 35 | struct ext2_dir_entry *new_entry = |
| 36 | (struct ext2_dir_entry *) (entry_base + actual_size); |
| 37 | |
| 38 | new_entry->inode = ctx->inode; |
| 39 | new_entry->name_len = strlen(str: ctx->name); |
| 40 | new_entry->rec_len = original_rec_len - actual_size; |
| 41 | new_entry->file_type = ctx->type; |
| 42 | |
| 43 | memcpy(new_entry->name, ctx->name, new_entry->name_len); |
| 44 | new_entry->name[new_entry->name_len] = '\0'; |
| 45 | ctx->success = true; |
| 46 | return true; |
| 47 | } |
| 48 | |
| 49 | return false; |
| 50 | } |
| 51 | |
| 52 | enum errno ext2_link_file(struct ext2_fs *fs, struct ext2_full_inode *dir, |
| 53 | struct ext2_full_inode *inode, const char *name, |
| 54 | uint8_t type, bool increment_links) { |
| 55 | if (ext2_dir_contains_file(fs, dir_inode: dir, fname: name)) |
| 56 | return ERR_EXIST; |
| 57 | |
| 58 | struct link_ctx ctx = {name, inode->inode_num, dir->inode_num, type, false}; |
| 59 | |
| 60 | ext2_walk_dir(fs, dir, cb: link_callback, ctx: &ctx); |
| 61 | |
| 62 | /* did not need to allocate new block */ |
| 63 | if (ctx.success) |
| 64 | goto done; |
| 65 | |
| 66 | uint32_t new_block = ext2_alloc_block(fs); |
| 67 | if (new_block == 0) |
| 68 | return ERR_NOSPC; |
| 69 | |
| 70 | struct bcache_entry *ent; |
| 71 | |
| 72 | /* this inserts the entry into the block cache */ |
| 73 | struct ext2_dir_entry *new_entry = |
| 74 | (void *) ext2_create_bcache_ent(fs, block: new_block, out: &ent); |
| 75 | if (!new_entry) |
| 76 | return ERR_IO; |
| 77 | |
| 78 | /* no locking here because this is a new entry that |
| 79 | * no one besides us should have access to right now */ |
| 80 | |
| 81 | ext2_init_dirent(fs, new_entry, inode_num: inode->inode_num, name, type); |
| 82 | |
| 83 | if (!ext2_block_write(fs, ent, EXT2_PRIO_DIRENT)) |
| 84 | return ERR_IO; |
| 85 | |
| 86 | /* this sets the first available block to our new block */ |
| 87 | if (!ext2_find_first_available(fs, dir, new_block: &new_block)) { |
| 88 | ext2_free_block(fs, block_num: new_block); |
| 89 | return ERR_IO; |
| 90 | } |
| 91 | |
| 92 | done: |
| 93 | if (increment_links) |
| 94 | dir->node.links_count += 1; |
| 95 | |
| 96 | bool status = ext2_inode_write(fs, inode_num: dir->inode_num, inode: &dir->node); |
| 97 | return status ? ERR_OK : ERR_FS_INTERNAL; |
| 98 | } |
| 99 | |
| 100 | enum errno ext2_create_file(struct ext2_fs *fs, |
| 101 | struct ext2_full_inode *parent_dir, |
| 102 | const char *name, mode_t mode, bool increment) { |
| 103 | inode_t new_inode_num = ext2_alloc_inode(fs); |
| 104 | if (new_inode_num == 0) |
| 105 | return ERR_NOSPC; |
| 106 | |
| 107 | struct ext2_inode new_inode = {0}; |
| 108 | ext2_init_inode(new_inode: &new_inode, mode); |
| 109 | |
| 110 | if (!ext2_inode_write(fs, inode_num: new_inode_num, inode: &new_inode)) |
| 111 | return ERR_IO; |
| 112 | |
| 113 | struct ext2_full_inode tmp = { |
| 114 | .node = new_inode, |
| 115 | .inode_num = new_inode_num, |
| 116 | }; |
| 117 | |
| 118 | uint8_t ft = ext2_extract_ftype(mode); |
| 119 | |
| 120 | enum errno err = ext2_link_file(fs, dir: parent_dir, inode: &tmp, name, type: ft, increment_links: increment); |
| 121 | |
| 122 | if (err != ERR_OK) { |
| 123 | ext2_free_inode(fs, inode_num: new_inode_num); |
| 124 | return err; |
| 125 | } |
| 126 | |
| 127 | return ERR_OK; |
| 128 | } |
| 129 | |