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