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
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 (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
52enum 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
92done:
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
100enum 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