1#include <fs/ext2.h>
2#include <mem/alloc.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6
7#include <err.h>
8
9struct search_ctx {
10 const char *target;
11 struct ext2_full_inode *result;
12 uint8_t type;
13 bool found;
14};
15
16struct contains_ctx {
17 const char *target;
18 bool found;
19};
20
21struct readdir_ctx {
22 uint32_t entry_offset;
23 struct ext2_dir_entry *out;
24 bool found;
25 uint32_t count;
26};
27
28/* TODO: Figure out how to return errors here in the case of a failed read */
29static bool search_callback(struct ext2_fs *fs, struct ext2_dir_entry *entry,
30 void *ctx_ptr, uint32_t b, uint32_t e_num,
31 uint32_t offset) TSA_NO_ANALYSIS {
32 cc_ignore(b);
33 struct search_ctx *ctx = (struct search_ctx *) ctx_ptr;
34
35 if (!ext2_dirent_valid(entry))
36 return false;
37
38 if (offset + entry->rec_len > fs->block_size)
39 return false;
40
41 if (memcmp(entry->name, ctx->target, entry->name_len) == 0 &&
42 ctx->target[entry->name_len] == '\0') {
43 ctx->found = true;
44 ctx->result->inode_num = e_num;
45 ctx->type = entry->file_type;
46
47 struct ext2_inode *out = &ctx->result->node;
48 uint32_t ino = entry->inode;
49 struct bcache_entry *ent;
50
51 /* read `acquire()'s the node, we have to unlock but not dec refcount */
52
53 memcpy(out, ext2_inode_read(fs, ino, &ent), sizeof(struct ext2_inode));
54 bcache_ent_unlock(ent);
55 ctx->result->ent = ent;
56
57 return true;
58 }
59
60 return false;
61}
62
63static bool contains_callback(struct ext2_fs *fs, struct ext2_dir_entry *entry,
64 void *ctx_ptr, uint32_t b, uint32_t e,
65 uint32_t offset) {
66 struct contains_ctx *ctx = (struct contains_ctx *) ctx_ptr;
67 cc_ignore(b, e);
68
69 if (!ext2_dirent_valid(entry))
70 return false;
71
72 if (offset + entry->rec_len > fs->block_size)
73 return false;
74
75 if (memcmp(entry->name, ctx->target, entry->name_len) == 0 &&
76 ctx->target[entry->name_len] == '\0') {
77 ctx->found = true;
78 return true;
79 }
80
81 ctx->found = false;
82 return false;
83}
84
85static bool readdir_callback(struct ext2_fs *fs, struct ext2_dir_entry *entry,
86 void *ctx_ptr, uint32_t block, uint32_t entry_num,
87 uint32_t entry_offset) {
88 cc_ignore(fs, block, entry_num, entry_offset);
89
90 struct readdir_ctx *ctx = ctx_ptr;
91
92 if (!ext2_dirent_valid(entry))
93 return false;
94
95 if (ctx->count == ctx->entry_offset) {
96 memcpy(ctx->out, entry, sizeof(struct ext2_dir_entry));
97 ctx->found = true;
98 return true;
99 }
100
101 ctx->count++;
102 return false;
103}
104
105struct ext2_full_inode *ext2_find_file_in_dir(struct ext2_fs *fs,
106 struct ext2_full_inode *dir_inode,
107 const char *fname,
108 uint8_t *type_out) {
109
110 /* TODO: handle this allocation failure case */
111 struct ext2_full_inode *out_node = vnew(out_node, ALLOC_ZERO);
112 struct search_ctx ctx = {.target = fname, .result = out_node, .type = 0};
113 ext2_walk_dir(fs, dir: dir_inode, cb: search_callback, ctx: &ctx);
114
115 if (type_out)
116 *type_out = ctx.type;
117
118 if (ctx.found) {
119 return ctx.result;
120 } else {
121 vfree(out_node);
122 return NULL;
123 }
124}
125
126bool ext2_dir_contains_file(struct ext2_fs *fs,
127 struct ext2_full_inode *dir_inode,
128 const char *fname) {
129 struct contains_ctx ctx = {.target = fname, .found = false};
130
131 ext2_walk_dir(fs, dir: dir_inode, cb: contains_callback, ctx: &ctx);
132
133 return ctx.found;
134}
135
136enum err ext2_readdir(struct ext2_fs *fs, struct ext2_full_inode *dir_inode,
137 struct ext2_dir_entry *out, uint32_t entry_offset) {
138 if (!fs || !dir_inode || !out)
139 return ERR_INVAL;
140
141 struct readdir_ctx ctx = {
142 .out = out, .entry_offset = entry_offset, .count = 0};
143
144 ext2_walk_dir(fs, dir: dir_inode, cb: readdir_callback, ctx: &ctx);
145
146 return ctx.found ? ERR_OK : ERR_NO_ENT;
147}
148