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 "errno.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) {
32 (void) 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 (void) b;
68 (void) e;
69
70 if (!ext2_dirent_valid(entry))
71 return false;
72
73 if (offset + entry->rec_len > fs->block_size)
74 return false;
75
76 if (memcmp(entry->name, ctx->target, entry->name_len) == 0 &&
77 ctx->target[entry->name_len] == '\0') {
78 ctx->found = true;
79 return true;
80 }
81
82 ctx->found = false;
83 return false;
84}
85
86static bool readdir_callback(struct ext2_fs *fs, struct ext2_dir_entry *entry,
87 void *ctx_ptr, uint32_t block, uint32_t entry_num,
88 uint32_t entry_offset) {
89 (void) fs, (void) block, (void) entry_num, (void) entry_offset;
90
91 struct readdir_ctx *ctx = ctx_ptr;
92
93 if (!ext2_dirent_valid(entry))
94 return false;
95
96 if (ctx->count == ctx->entry_offset) {
97 memcpy(ctx->out, entry, sizeof(struct ext2_dir_entry));
98 ctx->found = true;
99 return true;
100 }
101
102 ctx->count++;
103 return false;
104}
105
106struct ext2_full_inode *ext2_find_file_in_dir(struct ext2_fs *fs,
107 struct ext2_full_inode *dir_inode,
108 const char *fname,
109 uint8_t *type_out) {
110
111 /* TODO: handle this allocation failure case */
112 struct ext2_full_inode *out_node =
113 kmalloc(sizeof(struct ext2_full_inode), ALLOC_FLAGS_ZERO);
114 struct search_ctx ctx = {.target = fname, .result = out_node, .type = 0};
115 ext2_walk_dir(fs, dir: dir_inode, cb: search_callback, ctx: &ctx);
116
117 if (type_out)
118 *type_out = ctx.type;
119
120 if (ctx.found) {
121 return ctx.result;
122 } else {
123 kfree(out_node);
124 return NULL;
125 }
126}
127
128bool ext2_dir_contains_file(struct ext2_fs *fs,
129 struct ext2_full_inode *dir_inode,
130 const char *fname) {
131 struct contains_ctx ctx = {.target = fname, .found = false};
132
133 ext2_walk_dir(fs, dir: dir_inode, cb: contains_callback, ctx: &ctx);
134
135 return ctx.found;
136}
137
138enum errno ext2_readdir(struct ext2_fs *fs, struct ext2_full_inode *dir_inode,
139 struct ext2_dir_entry *out, uint32_t entry_offset) {
140 if (!fs || !dir_inode || !out)
141 return ERR_INVAL;
142
143 struct readdir_ctx ctx = {
144 .out = out, .entry_offset = entry_offset, .count = 0};
145
146 ext2_walk_dir(fs, dir: dir_inode, cb: readdir_callback, ctx: &ctx);
147
148 return ctx.found ? ERR_OK : ERR_NO_ENT;
149}
150