diff --git a/kernel/include/kernel/fs/ramfs.h b/kernel/include/kernel/fs/ramfs.h index 30df6af..08d08c7 100644 --- a/kernel/include/kernel/fs/ramfs.h +++ b/kernel/include/kernel/fs/ramfs.h @@ -30,6 +30,8 @@ int seek (inode* node, file* f, size_t offset, int whence); int getdents (inode* node, file* f, void* buf, size_t count); int istat (inode* node, stat* buf); int fstat (inode* node, file* f, stat* buf); +int close (inode* node, file* f); +int unlink (inode* node); typedef struct { char* c_name; diff --git a/kernel/include/kernel/fs/vfs.h b/kernel/include/kernel/fs/vfs.h index 5067977..937752a 100644 --- a/kernel/include/kernel/fs/vfs.h +++ b/kernel/include/kernel/fs/vfs.h @@ -51,6 +51,7 @@ typedef struct { int (*create) (char*, inode**, inode*); int (*mkdir) (char*, inode**, inode*); int (*stat) (inode*, stat*); + int (*unlink) (inode*); } inode_operations; typedef struct { @@ -93,6 +94,7 @@ int do_chdir (const char* path); int do_getcwd (char* buf, size_t size); int do_create (char* filename, inode** result, inode* parent); int do_lookup (char* filename, inode** result, inode* root, inode* cwd); +int do_unlink (const char* path); int do_read (struct file* f, void* buf, size_t size); int do_seek (struct file* f, size_t offset, int whence); @@ -116,6 +118,7 @@ uint64_t sys_getcwd (uint64_t buf, uint64_t size); uint64_t sys_fstat (uint64_t fd, uint64_t buf); uint64_t sys_stat (uint64_t path, uint64_t buf); uint64_t sys_ioctl (uint64_t fd, uint64_t req, uint64_t arg); +uint64_t sys_unlink (uint64_t path); inode* get_absolute_root (void); void init_vfs (inode* absolute_root); diff --git a/kernel/src/kernel/fs/ramfs/ramfs.c b/kernel/src/kernel/fs/ramfs/ramfs.c index f3b2279..6b5f720 100644 --- a/kernel/src/kernel/fs/ramfs/ramfs.c +++ b/kernel/src/kernel/fs/ramfs/ramfs.c @@ -28,12 +28,13 @@ static inode_operations i_ops = {.lookup = lookup, .lookup_by_ino = lookup_by_ino, .mkdir = mkdir, .create = create, - .stat = istat}; + .stat = istat, + .unlink = unlink}; static file_operations f_ops = {.read = read, .write = write, .seek = seek, .open = nullptr, - .close = nullptr, + .close = close, .getdents = getdents, .fstat = fstat}; @@ -48,6 +49,7 @@ int mkdir (char* dirname, inode** result, inode* root) { new_dir->i_fops = &f_ops; new_dir->i_no = next_inode++; new_dir->i_parent = root; + new_dir->i_cnt = 1; // manually add the '.' and '..' entries ((dir_content_t*)new_dir->i_pvt)->d_count = 2; @@ -85,6 +87,7 @@ int create (char* filename, inode** result, inode* root) { new_file->i_fops = &f_ops; new_file->i_no = next_inode++; new_file->i_parent = root; + new_file->i_cnt = 1; // construct parent replacement structures dir_content_t* parent_pvt = (dir_content_t*)root->i_pvt; @@ -272,6 +275,48 @@ int fstat (inode* node, file* f, stat* buf) { return istat (node, buf); } +static void delete_node (inode* node) { + inode* parent_node = node->i_parent; + if (parent_node == node || !parent_node) goto parent_unlinked; + + dir_content_t* dir_content = (dir_content_t*)parent_node->i_pvt; + for (uint64_t i = 0; i < dir_content->d_count; i++) { + child_t* d_child = &dir_content->d_children[i]; + if (!d_child->c_inode || !d_child->c_name) continue; + + if (d_child->c_inode == node) { + kfree (d_child->c_name); + dir_content->d_children[i] = dir_content->d_children[--dir_content->d_count]; + void* tmp = + krealloc (dir_content->d_children, (dir_content->d_count) * sizeof (child_t)); + if (tmp) + dir_content->d_children = tmp; + else + kmemset (&dir_content->d_children[dir_content->d_count], 0, sizeof (child_t)); + break; + } + } + +parent_unlinked: + if (node->i_pvt) kfree (node->i_pvt); + if (node->i_fsinfo) kfree (node->i_fsinfo); + if (node->i_info.ramfs_info) kfree (node->i_info.ramfs_info); + + kfree (node); +} + +int close (inode* node, file* f) { + (void)f; + if (node->i_cnt == 0 && node->i_type != DIRECTORY) delete_node (node); + return 0; +} + +int unlink (inode* node) { + node->i_cnt--; + if (node->i_cnt == 0) delete_node (node); + return 0; +} + inode* init_ramfs_root (void) { root_inode = kmalloc (sizeof (inode)); kmemset ((void*)root_inode, 0, sizeof (inode)); @@ -281,6 +326,7 @@ inode* init_ramfs_root (void) { root_inode->i_fops = &f_ops; root_inode->i_no = next_inode++; root_inode->i_parent = root_inode; + root_inode->i_cnt = 1; // the single reference is us, COS // manually add the '.' and '..' entries ((dir_content_t*)root_inode->i_pvt)->d_count = 2; diff --git a/kernel/src/kernel/fs/vfs/close.c b/kernel/src/kernel/fs/vfs/close.c index 531d9e5..984ae89 100644 --- a/kernel/src/kernel/fs/vfs/close.c +++ b/kernel/src/kernel/fs/vfs/close.c @@ -27,8 +27,8 @@ int do_close (struct file* fd) { if (!fd) return -EINVAL; if (--fd->f_cnt == 0) { - if (fd->f_fops && fd->f_fops->close) fd->f_fops->close (fd->f_inode, fd); fd->f_inode->i_cnt--; + if (fd->f_fops && fd->f_fops->close) fd->f_fops->close (fd->f_inode, fd); kfree (fd); } return 0; diff --git a/kernel/src/kernel/fs/vfs/unlink.c b/kernel/src/kernel/fs/vfs/unlink.c new file mode 100644 index 0000000..1e55705 --- /dev/null +++ b/kernel/src/kernel/fs/vfs/unlink.c @@ -0,0 +1,43 @@ +/* + * unlink.c + * Copyright (C) 2026 Aditya Kumar + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, see . + */ + +#include +#include +#include +#include +#include +#include + +int do_unlink (const char* path) { + if (!path) return -EINVAL; + + process* current = get_current_process (); + inode* node = nullptr; + + int error = do_lookup ((char*)path, &node, current->p_root, current->p_wd); + if (error != 0) return error; + + if (node->i_type == DIRECTORY) return -EISDIR; + if (!node->i_iops || !node->i_iops->unlink) return -ENOSYS; + return node->i_iops->unlink (node); +} + +uint64_t sys_unlink (uint64_t path) { + const char* path_us = kstrdup ((const char*)path); + int error = do_unlink (path_us); + kfree ((void*)path_us); + return error; +} diff --git a/kernel/src/kernel/fs/vfs/vfs.c b/kernel/src/kernel/fs/vfs/vfs.c index 10a75b7..24dfec7 100644 --- a/kernel/src/kernel/fs/vfs/vfs.c +++ b/kernel/src/kernel/fs/vfs/vfs.c @@ -41,4 +41,5 @@ void init_vfs (inode* absolute_root) { register_syscall (SYSCALL_SYS_CHDIR, SYS1 (sys_chdir)); register_syscall (SYSCALL_SYS_GETCWD, SYS2 (sys_getcwd)); register_syscall (SYSCALL_SYS_IOCTL, SYS3 (sys_ioctl)); + register_syscall (SYSCALL_SYS_UNLINK, SYS1 (sys_unlink)); } \ No newline at end of file diff --git a/lib/cos/src/unlink.c b/lib/cos/src/unlink.c new file mode 100644 index 0000000..511abb9 --- /dev/null +++ b/lib/cos/src/unlink.c @@ -0,0 +1,22 @@ +/* + * unlink.c + * Copyright (C) 2026 Aditya Kumar + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, see . + */ + +#include +#include + +int unlink (const char* __path) { + return (int)syscall_ret ((long)syscall1 (SYSCALL_SYS_UNLINK, (uint64_t)__path)); +} \ No newline at end of file diff --git a/user/cosh/include/builtin.h b/user/cosh/include/builtin.h index ff8a022..2ae2f7c 100644 --- a/user/cosh/include/builtin.h +++ b/user/cosh/include/builtin.h @@ -33,3 +33,4 @@ int builtin_source (int argc, char** argv); int builtin_stat (int argc, char** argv); int builtin_test (int argc, char** argv); int builtin_touch (int argc, char** argv); +int builtin_unlink (int argc, char** argv); diff --git a/user/cosh/src/builtin/dispatch_builtin.c b/user/cosh/src/builtin/dispatch_builtin.c index efe7fb5..67880d3 100644 --- a/user/cosh/src/builtin/dispatch_builtin.c +++ b/user/cosh/src/builtin/dispatch_builtin.c @@ -51,6 +51,8 @@ int dispatch_builtin (size_t argc, char** argv) { return builtin_test (argc, argv); else if (strcmp (argv[0], "clear") == 0) return builtin_clear (argc, argv); + else if (strcmp (argv[0], "unlink") == 0) + return builtin_unlink (argc, argv); return -1; } diff --git a/user/cosh/src/builtin/unlink.c b/user/cosh/src/builtin/unlink.c new file mode 100644 index 0000000..92b8e3b --- /dev/null +++ b/user/cosh/src/builtin/unlink.c @@ -0,0 +1,27 @@ +/* + * unlink.c + * Copyright (C) 2026 Aditya Kumar + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, see . + */ + +#include +#include +#include + +int builtin_unlink (int argc, char** argv) { + if (argc == 1) { + printf ("usage: unlink file\n"); + return 64; + } + return unlink (argv[1]); +} \ No newline at end of file diff --git a/user/cosh/src/repl_main.c b/user/cosh/src/repl_main.c index 2fbb267..6c347a9 100644 --- a/user/cosh/src/repl_main.c +++ b/user/cosh/src/repl_main.c @@ -145,7 +145,7 @@ int repl_loop (void) { cmdbuf[strcspn (cmdbuf, "\n")] = '\0'; if (cmdbuf[0] == '\0') return 0; last_exit = run_line (cmdbuf); - if (last_exit != 0) printf ("\033[31mexited with non-zero status: %i\n", last_exit); + if (last_exit != 0) printf ("\033[31m[%i] ", last_exit); return 0; }