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