1#include <compiler.h>
2#include <fs/ext2.h>
3#include <fs/vfs.h>
4#include <mem/alloc.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <string.h>
8#include <test/export.h>
9#include <time/time.h>
10
11// TODO: With all VFS impls, make sure to check these are on the same disk and
12// filesystem
13
14// TODO: open/close atomicity or whatever
15
16// TODO: check if files already exist, update times, check if file types are
17// correct
18
19enum errno ext2_vfs_stat(struct vfs_node *v, struct vfs_stat *out);
20enum errno ext2_vfs_rename(struct vfs_node *old_parent, const char *old_name,
21 struct vfs_node *new_parent, const char *new_name);
22enum errno ext2_vfs_chmod(struct vfs_node *n, mode_t mode);
23
24enum errno ext2_vfs_chown(struct vfs_node *n, uid_t uid, gid_t gid);
25enum errno ext2_vfs_utime(struct vfs_node *n, uint64_t atime, uint64_t mtime);
26
27enum errno ext2_vfs_read(struct vfs_node *n, void *buf, uint64_t size,
28 uint64_t offset);
29
30enum errno ext2_vfs_write(struct vfs_node *n, const void *buf, uint64_t size,
31 uint64_t offset);
32
33enum errno ext2_vfs_truncate(struct vfs_node *n, uint64_t length);
34
35enum errno ext2_vfs_link(struct vfs_node *parent, struct vfs_node *target,
36 const char *name);
37
38enum errno ext2_vfs_create(struct vfs_node *n, const char *name, mode_t mode);
39
40enum errno ext2_vfs_unlink(struct vfs_node *n, const char *name);
41
42enum errno ext2_vfs_symlink(struct vfs_node *parent, const char *target,
43 const char *link_name);
44enum errno ext2_vfs_readlink(struct vfs_node *n, char *out_buf, uint64_t size);
45
46enum errno ext2_vfs_finddir(struct vfs_node *node, const char *fname,
47 struct vfs_dirent *out);
48enum errno ext2_vfs_mkdir(struct vfs_node *n, const char *name, mode_t mode);
49enum errno ext2_vfs_rmdir(struct vfs_node *n, const char *name);
50enum errno ext2_vfs_readdir(struct vfs_node *n, struct vfs_dirent *out,
51 uint64_t index);
52
53enum errno vfs_dummy_open(struct vfs_node *a, uint32_t b) {
54 (void) a, (void) b;
55 return ERR_OK;
56}
57
58enum errno vfs_dummy_close(struct vfs_node *a) {
59 (void) a;
60 return ERR_OK;
61}
62
63static const struct vfs_ops ext2_vfs_ops = {
64 .open = vfs_dummy_open,
65 .close = vfs_dummy_close,
66 .stat = ext2_vfs_stat,
67 .rename = ext2_vfs_rename,
68 .chmod = ext2_vfs_chmod,
69 .chown = ext2_vfs_chown,
70 .utime = ext2_vfs_utime,
71 .read = ext2_vfs_read,
72 .write = ext2_vfs_write,
73 .truncate = ext2_vfs_truncate,
74 .link = ext2_vfs_link,
75 .create = ext2_vfs_create,
76 .unlink = ext2_vfs_unlink,
77 .symlink = ext2_vfs_symlink,
78 .readlink = ext2_vfs_readlink,
79 .finddir = ext2_vfs_finddir,
80 .readdir = ext2_vfs_readdir,
81 .mkdir = ext2_vfs_mkdir,
82 .rmdir = ext2_vfs_rmdir,
83 .mount = vfs_mount,
84 .unmount = vfs_unmount,
85};
86
87//
88//
89// Utility functions and things - converting between ext2/vfs versions of stuff
90//
91//
92
93static uint32_t ext2_to_vfs_flags(uint32_t ext2_flags) {
94 uint32_t vfs_flags = 0;
95
96 if (ext2_flags & EXT2_APPEND_FL)
97 vfs_flags |= VFS_NODE_APPENDONLY;
98
99 if (ext2_flags & EXT2_IMMUTABLE_FL)
100 vfs_flags |= VFS_NODE_IMMUTABLE;
101
102 if (ext2_flags & EXT2_NOATIME_FL)
103 vfs_flags |= VFS_NODE_NOATIME;
104
105 if (ext2_flags & EXT2_SYNC_FL)
106 vfs_flags |= VFS_NODE_SYNC;
107
108 if (ext2_flags & EXT2_DIRSYNC_FL)
109 vfs_flags |= VFS_NODE_DIRSYNC;
110
111 return vfs_flags;
112}
113TEST_EXPORT(ext2_to_vfs_flags);
114
115static uint32_t vfs_to_ext2_flags(uint32_t vfs_flags) {
116 uint32_t ext2_flags = 0;
117
118 if (vfs_flags & VFS_NODE_APPENDONLY)
119 ext2_flags |= EXT2_APPEND_FL;
120
121 if (vfs_flags & VFS_NODE_IMMUTABLE)
122 ext2_flags |= EXT2_IMMUTABLE_FL;
123
124 if (vfs_flags & VFS_NODE_NOATIME)
125 ext2_flags |= EXT2_NOATIME_FL;
126
127 if (vfs_flags & VFS_NODE_SYNC)
128 ext2_flags |= EXT2_SYNC_FL;
129
130 if (vfs_flags & VFS_NODE_DIRSYNC)
131 ext2_flags |= EXT2_DIRSYNC_FL;
132
133 return ext2_flags;
134}
135TEST_EXPORT(vfs_to_ext2_flags);
136
137static uint16_t ext2_to_vfs_mode(uint16_t ext2_mode) {
138 uint16_t vfs_mode = 0;
139
140 // File type
141 switch (ext2_mode & EXT2_S_IFMT) {
142 case EXT2_S_IFREG: vfs_mode |= VFS_MODE_FILE; break;
143 case EXT2_S_IFDIR: vfs_mode |= VFS_MODE_DIR; break;
144 case EXT2_S_IFLNK: vfs_mode |= VFS_MODE_SYMLINK; break;
145 case EXT2_S_IFCHR: vfs_mode |= VFS_MODE_CHARDEV; break;
146 case EXT2_S_IFBLK: vfs_mode |= VFS_MODE_BLOCKDEV; break;
147 case EXT2_S_IFIFO: vfs_mode |= VFS_MODE_PIPE; break;
148 case EXT2_S_IFSOCK: vfs_mode |= VFS_MODE_SOCKET; break;
149 default: vfs_mode |= VFS_MODE_FILE; break;
150 }
151
152 // Owner permissions
153 if (ext2_mode & EXT2_S_IRUSR)
154 vfs_mode |= VFS_MODE_O_READ;
155
156 if (ext2_mode & EXT2_S_IWUSR)
157 vfs_mode |= VFS_MODE_O_WRITE;
158
159 if (ext2_mode & EXT2_S_IXUSR)
160 vfs_mode |= VFS_MODE_O_EXEC;
161
162 // Group permissions
163 if (ext2_mode & EXT2_S_IRGRP)
164 vfs_mode |= VFS_MODE_G_READ;
165
166 if (ext2_mode & EXT2_S_IWGRP)
167 vfs_mode |= VFS_MODE_G_WRITE;
168
169 if (ext2_mode & EXT2_S_IXGRP)
170 vfs_mode |= VFS_MODE_G_EXEC;
171
172 // Other permissions
173 if (ext2_mode & EXT2_S_IROTH)
174 vfs_mode |= VFS_MODE_R_READ;
175
176 if (ext2_mode & EXT2_S_IWOTH)
177 vfs_mode |= VFS_MODE_R_WRITE;
178
179 if (ext2_mode & EXT2_S_IXOTH)
180 vfs_mode |= VFS_MODE_R_EXEC;
181
182 return vfs_mode;
183}
184TEST_EXPORT(ext2_to_vfs_mode);
185
186static uint16_t vfs_to_ext2_mode(uint16_t vfs_mode) {
187 uint16_t ext2_mode = 0;
188
189 // File type
190 switch (vfs_mode & VFS_MODE_TYPE_MASK) {
191 case VFS_MODE_DIR: ext2_mode |= EXT2_S_IFDIR; break;
192 case VFS_MODE_SYMLINK: ext2_mode |= EXT2_S_IFLNK; break;
193 case VFS_MODE_CHARDEV: ext2_mode |= EXT2_S_IFCHR; break;
194 case VFS_MODE_BLOCKDEV: ext2_mode |= EXT2_S_IFBLK; break;
195 case VFS_MODE_PIPE: ext2_mode |= EXT2_S_IFIFO; break;
196 case VFS_MODE_SOCKET: ext2_mode |= EXT2_S_IFSOCK; break;
197 case VFS_MODE_FILE:
198 default: ext2_mode |= EXT2_S_IFREG; break;
199 }
200
201 // Owner permissions
202 if (vfs_mode & VFS_MODE_O_READ)
203 ext2_mode |= EXT2_S_IRUSR;
204
205 if (vfs_mode & VFS_MODE_O_WRITE)
206 ext2_mode |= EXT2_S_IWUSR;
207
208 if (vfs_mode & VFS_MODE_O_EXEC)
209 ext2_mode |= EXT2_S_IXUSR;
210
211 // Group permissions
212 if (vfs_mode & VFS_MODE_G_READ)
213 ext2_mode |= EXT2_S_IRGRP;
214
215 if (vfs_mode & VFS_MODE_G_WRITE)
216 ext2_mode |= EXT2_S_IWGRP;
217
218 if (vfs_mode & VFS_MODE_G_EXEC)
219 ext2_mode |= EXT2_S_IXGRP;
220
221 // Other permissions
222 if (vfs_mode & VFS_MODE_R_READ)
223 ext2_mode |= EXT2_S_IROTH;
224
225 if (vfs_mode & VFS_MODE_R_WRITE)
226 ext2_mode |= EXT2_S_IWOTH;
227
228 if (vfs_mode & VFS_MODE_R_EXEC)
229 ext2_mode |= EXT2_S_IXOTH;
230
231 return ext2_mode;
232}
233TEST_EXPORT(vfs_to_ext2_mode);
234
235static enum errno ext2_to_vfs_stat(struct ext2_full_inode *node,
236 struct vfs_stat *out) {
237 if (!out || !node)
238 return ERR_INVAL;
239
240 struct ext2_inode *inode = &node->node;
241
242 out->inode = node->inode_num;
243 out->mode = ext2_to_vfs_mode(ext2_mode: inode->mode);
244 out->nlink = inode->links_count;
245 out->atime = inode->atime;
246 out->mtime = inode->mtime;
247 out->size = inode->size;
248 out->ctime = inode->ctime;
249 out->present_mask = 0xffff;
250
251 return ERR_OK;
252}
253
254static struct vfs_node *make_vfs_node(struct ext2_fs *fs,
255 struct ext2_full_inode *node,
256 const char *fname) {
257 if (!node || !fname || !fs)
258 return NULL;
259
260 struct vfs_node *ret = kmalloc(sizeof(struct vfs_node), ALLOC_FLAGS_ZERO);
261 if (!ret)
262 return NULL;
263
264 if (*fname == '.')
265 ret->flags |= VFS_NODE_HIDDEN;
266
267 ret->flags = ext2_to_vfs_flags(ext2_flags: node->node.flags);
268
269 ret->unique_id = node->inode_num;
270 ret->fs_data = fs;
271 ret->fs_node_data = node;
272 ret->size = node->node.size;
273 ret->mode = ext2_to_vfs_mode(ext2_mode: node->node.mode);
274 ret->ops = &ext2_vfs_ops;
275 ret->fs_type = FS_EXT2;
276
277 return ret;
278}
279
280static struct vfs_dirent *ext2_to_vfs_dirent(struct ext2_dir_entry *ext2) {
281 struct vfs_dirent *dirent =
282 kmalloc(sizeof(struct vfs_dirent), ALLOC_FLAGS_ZERO);
283 if (!dirent)
284 return NULL;
285
286 dirent->mode = ext2_to_vfs_mode(ext2_mode: ext2->file_type);
287 dirent->dirent_data = ext2;
288 memcpy(dirent->name, ext2->name, ext2->name_len);
289 return dirent;
290}
291
292struct rename_ctx {
293 const char *old_name;
294 const char *new_name;
295 bool success;
296};
297
298static bool dir_entry_rename_callback(struct ext2_fs *fs,
299 struct ext2_dir_entry *entry,
300 void *ctx_ptr, uint32_t b, uint32_t e_num,
301 uint32_t c) {
302 (void) c, (void) fs, (void) b, (void) e_num;
303 struct rename_ctx *ctx = (struct rename_ctx *) ctx_ptr;
304
305 if (!ext2_dirent_valid(entry))
306 return false;
307
308 if (entry->inode != 0 &&
309 memcmp(entry->name, ctx->old_name, entry->name_len) == 0 &&
310 ctx->old_name[entry->name_len] == '\0') {
311
312 memcpy(entry->name, ctx->new_name, strlen(ctx->new_name));
313 entry->name_len = strlen(str: ctx->new_name);
314 ctx->success = true;
315 return true;
316 }
317
318 return false;
319}
320
321static enum errno dir_entry_rename(struct ext2_fs *fs,
322 struct ext2_full_inode *node,
323 const char *old, const char *new) {
324 if (!ext2_dir_contains_file(fs, dir_inode: node, fname: old))
325 return ERR_NO_ENT;
326
327 struct rename_ctx ctx = {
328 .old_name = old, .new_name = new, .success = false};
329
330 if (!ext2_walk_dir(fs, dir: node, cb: dir_entry_rename_callback, ctx: &ctx))
331 return ERR_IO;
332
333 return ERR_OK;
334}
335
336//
337//
338//
339// Actual implementations are below this
340//
341//
342//
343
344/* no locking necessary in mounting, duh */
345enum errno ext2_mount(struct partition *p, struct ext2_fs *fs,
346 struct ext2_sblock *sblock, struct vfs_node *out_node) {
347 if (!fs || !sblock)
348 return ERR_INVAL;
349
350 sblock->mtime = time_get_unix();
351 sblock->wtime = time_get_unix();
352 fs->drive = p->disk;
353 fs->partition = p;
354 fs->inodes_count = sblock->inodes_count;
355 fs->inodes_per_group = sblock->inodes_per_group;
356 fs->inode_size = sblock->inode_size;
357 fs->block_size = 1024U << sblock->log_block_size;
358 fs->blocks_per_group = sblock->blocks_per_group;
359
360 spinlock_init(&fs->lock);
361
362 fs->sectors_per_block = fs->block_size / p->disk->sector_size;
363
364 fs->num_groups =
365 (fs->inodes_count + fs->inodes_per_group - 1) / fs->inodes_per_group;
366
367 uint32_t superblock_block = 1024 / fs->block_size;
368 uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
369
370 fs->sblock =
371 (void *) ext2_block_read(fs, block_num: superblock_block, out: &fs->sbcache_ent);
372
373 fs->group_desc =
374 (void *) ext2_block_read(fs, block_num: gdt_block, out: &fs->gdesc_cache_ent);
375
376 struct ext2_inode *inode =
377 kmalloc(sizeof(struct ext2_inode), ALLOC_FLAGS_ZERO);
378 struct ext2_full_inode *f =
379 kmalloc(sizeof(struct ext2_full_inode), ALLOC_FLAGS_ZERO);
380 if (!f || !inode)
381 return ERR_NO_MEM;
382
383 struct bcache_entry *root_ent = NULL;
384
385 ext2_inode_read(fs, EXT2_ROOT_INODE, out_ent: &root_ent);
386
387 if (!root_ent)
388 return ERR_IO;
389
390 if (!out_node) {
391 kfree(f);
392 bcache_ent_release(ent: root_ent);
393 return ERR_OK;
394 }
395
396 memcpy(&f->node, inode, sizeof(struct ext2_inode));
397
398 f->inode_num = EXT2_ROOT_INODE;
399 kfree(inode);
400
401 out_node->open_handles += 1;
402 out_node->flags = ext2_to_vfs_flags(ext2_flags: f->node.flags);
403 out_node->mode = ext2_to_vfs_mode(ext2_mode: f->node.mode);
404 out_node->size = f->node.size;
405 out_node->fs_data = fs;
406 out_node->fs_node_data = f;
407 out_node->fs_type = FS_EXT2;
408 out_node->ops = &ext2_vfs_ops;
409
410 bcache_ent_release(ent: fs->gdesc_cache_ent);
411 bcache_ent_release(ent: fs->sbcache_ent);
412 bcache_ent_release(ent: root_ent);
413 return ERR_OK;
414}
415
416enum errno ext2_vfs_finddir(struct vfs_node *node, const char *fname,
417 struct vfs_dirent *out) {
418 if (!node || !fname || !out)
419 return ERR_INVAL;
420
421 struct ext2_full_inode *full_inode = node->fs_node_data;
422
423 struct ext2_fs *fs = node->fs_data;
424
425 struct ext2_full_inode *found =
426 ext2_find_file_in_dir(fs, dir_inode: full_inode, fname, NULL);
427
428 if (!found)
429 return ERR_NO_ENT;
430
431 struct vfs_node *n = make_vfs_node(fs, node: found, fname);
432
433 struct vfs_dirent ent;
434 ent.mode = n->mode;
435 ent.node = n;
436 ent.dirent_data = fs;
437
438 memcpy(&ent.name, fname, strlen(fname));
439 memcpy(out, &ent, sizeof(struct vfs_dirent));
440
441 return ERR_OK;
442}
443
444enum errno ext2_vfs_readdir(struct vfs_node *node, struct vfs_dirent *out,
445 uint64_t index) {
446 if (!node || !out)
447 return ERR_INVAL;
448
449 struct ext2_full_inode *full_inode = node->fs_node_data;
450
451 struct ext2_fs *fs = node->fs_data;
452
453 struct ext2_dir_entry *ext2_out =
454 kmalloc(sizeof(struct ext2_dir_entry), ALLOC_FLAGS_ZERO);
455 if (unlikely(!ext2_out))
456 return ERR_NO_MEM;
457
458 enum errno e = ext2_readdir(fs, dir_inode: full_inode, out: ext2_out, entry_offset: index);
459
460 if (ERR_IS_FATAL(e))
461 return e;
462
463 struct vfs_dirent *dout = ext2_to_vfs_dirent(ext2: ext2_out);
464
465 memcpy(out, dout, sizeof(struct vfs_dirent));
466
467 return e;
468}
469
470enum errno ext2_vfs_rename(struct vfs_node *old_parent, const char *old_name,
471 struct vfs_node *new_parent, const char *new_name) {
472 if (!old_parent || !old_name || !new_name || !new_name)
473 return ERR_INVAL;
474
475 struct ext2_fs *fs = old_parent->fs_data;
476 struct ext2_full_inode *old_node = old_parent->fs_node_data;
477 struct ext2_full_inode *new_node = new_parent->fs_node_data;
478
479 if (old_node == new_node) {
480 return dir_entry_rename(fs, node: old_node, old: old_name, new: new_name);
481 }
482
483 uint8_t ftype = 0;
484
485 struct ext2_full_inode *this_inode =
486 ext2_find_file_in_dir(fs, dir_inode: old_node, fname: old_name, type_out: &ftype);
487
488 if (!this_inode)
489 return ERR_NO_ENT;
490
491 ext2_unlink_file(fs, dir_inode: old_node, name: old_name, false, false);
492
493 return ext2_link_file(fs, dir_inode: new_node, inode: this_inode, name: new_name, type: ftype, true);
494}
495
496enum errno ext2_vfs_stat(struct vfs_node *v, struct vfs_stat *out) {
497 struct ext2_full_inode *node = v->fs_node_data;
498 return ext2_to_vfs_stat(node, out);
499}
500
501enum errno ext2_vfs_link(struct vfs_node *parent, struct vfs_node *target,
502 const char *name) {
503 if (!parent || !target || !name)
504 return ERR_INVAL;
505
506 if (target->mode & VFS_MODE_DIR)
507 return ERR_IS_DIR;
508
509 // TODO: check if this is a directory
510 struct ext2_full_inode *dir = parent->fs_node_data;
511 struct ext2_full_inode *child = target->fs_node_data;
512 struct ext2_fs *fs = parent->fs_data;
513
514 return ext2_link_file(fs, dir_inode: dir, inode: child, name, type: vfs_to_ext2_mode(vfs_mode: target->mode),
515 true);
516}
517
518enum errno ext2_vfs_symlink(struct vfs_node *parent, const char *target,
519 const char *link_name) {
520 if (!parent || !target || !link_name)
521 return ERR_INVAL;
522
523 struct ext2_full_inode *dir = parent->fs_node_data;
524 struct ext2_fs *fs = parent->fs_data;
525
526 return ext2_symlink_file(fs, dir_inode: dir, name: link_name, target);
527}
528
529enum errno ext2_vfs_readlink(struct vfs_node *n, char *out_buf, uint64_t size) {
530 if (!n || !out_buf || !size)
531 return ERR_INVAL;
532
533 struct ext2_full_inode *node = n->fs_node_data;
534 struct ext2_fs *fs = n->fs_data;
535
536 return ext2_readlink(fs, node, buf: out_buf, size);
537}
538
539enum errno ext2_vfs_chmod(struct vfs_node *n, mode_t mode) {
540 if (!n)
541 return ERR_INVAL;
542
543 struct ext2_fs *fs = n->fs_data;
544 struct ext2_full_inode *node = n->fs_node_data;
545 uint16_t new_mode = vfs_to_ext2_mode(vfs_mode: mode);
546 enum errno e = ext2_chmod(fs, node, new_mode);
547 if (e != ERR_OK)
548 return e;
549
550 n->mode = mode;
551 return ERR_OK;
552}
553
554enum errno ext2_vfs_chown(struct vfs_node *n, uid_t uid, gid_t gid) {
555 if (!n)
556 return ERR_INVAL;
557
558 struct ext2_fs *fs = n->fs_data;
559 struct ext2_full_inode *node = n->fs_node_data;
560 enum errno e = ext2_chown(fs, node, new_uid: uid, new_gid: gid);
561 if (e != ERR_OK)
562 return e;
563
564 n->uid = uid;
565 n->gid = gid;
566 return ERR_OK;
567}
568
569enum errno ext2_vfs_read(struct vfs_node *n, void *buf, uint64_t size,
570 uint64_t offset) {
571 if (!n || !buf)
572 return ERR_INVAL;
573
574 struct ext2_fs *fs = n->fs_data;
575 struct ext2_full_inode *node = n->fs_node_data;
576 return ext2_read_file(fs, inode: node, offset, buffer: buf, length: size);
577}
578
579enum errno ext2_vfs_write(struct vfs_node *n, const void *buf, uint64_t size,
580 uint64_t offset) {
581 if (!n || !buf)
582 return ERR_INVAL;
583
584 struct ext2_fs *fs = n->fs_data;
585 struct ext2_full_inode *node = n->fs_node_data;
586 enum errno e = ext2_write_file(fs, inode: node, offset, src: buf, size);
587
588 if (e != ERR_OK)
589 return e;
590
591 n->size = node->node.size;
592 return ERR_OK;
593}
594
595enum errno ext2_vfs_utime(struct vfs_node *n, uint64_t atime, uint64_t mtime) {
596 if (!n)
597 return ERR_INVAL;
598
599 struct ext2_fs *fs = n->fs_data;
600 struct ext2_full_inode *node = n->fs_node_data;
601 struct ext2_inode *inode = &node->node;
602 inode->atime = atime;
603 inode->mtime = mtime;
604 if (!ext2_inode_write(fs, inode_num: node->inode_num, inode))
605 return ERR_IO;
606
607 n->atime = atime;
608 n->mtime = mtime;
609 return ERR_OK;
610}
611
612enum errno ext2_vfs_truncate(struct vfs_node *n, uint64_t length) {
613 if (!n)
614 return ERR_INVAL;
615
616 struct ext2_fs *fs = n->fs_data;
617 struct ext2_full_inode *node = n->fs_node_data;
618 return ext2_truncate_file(fs, inode: node, new_size: length);
619}
620
621enum errno ext2_vfs_unlink(struct vfs_node *n, const char *name) {
622 if (!n || !name)
623 return ERR_INVAL;
624
625 struct ext2_fs *fs = n->fs_data;
626 struct ext2_full_inode *node = n->fs_node_data;
627 return ext2_unlink_file(fs, dir_inode: node, name, true, false);
628}
629
630enum errno ext2_vfs_create(struct vfs_node *n, const char *name, mode_t mode) {
631 if (!n || !name)
632 return ERR_INVAL;
633
634 uint16_t new_mode = vfs_to_ext2_mode(vfs_mode: mode);
635 struct ext2_fs *fs = n->fs_data;
636 struct ext2_full_inode *node = n->fs_node_data;
637 return ext2_create_file(fs, parent_dir: node, name, mode: new_mode, false);
638}
639
640enum errno ext2_vfs_mkdir(struct vfs_node *n, const char *name, mode_t mode) {
641 if (!n || !name)
642 return ERR_INVAL;
643
644 uint16_t new_mode = vfs_to_ext2_mode(vfs_mode: mode);
645 struct ext2_fs *fs = n->fs_data;
646 struct ext2_full_inode *node = n->fs_node_data;
647 return ext2_mkdir(fs, parent_dir: node, name, mode: new_mode);
648}
649
650enum errno ext2_vfs_rmdir(struct vfs_node *n, const char *name) {
651 if (!n || !name)
652 return ERR_INVAL;
653
654 struct ext2_fs *fs = n->fs_data;
655 struct ext2_full_inode *node = n->fs_node_data;
656 return ext2_rmdir(fs, parent_dir: node, name);
657}
658