diff --git a/apps/rm.cpp b/apps/rm.cpp index 546572db..096336e1 100644 --- a/apps/rm.cpp +++ b/apps/rm.cpp @@ -4,12 +4,16 @@ Result luna_main(int argc, char** argv) { StringView path; + bool recursive; os::ArgumentParser parser; parser.add_positional_argument(path, "path"_sv, true); + parser.add_switch_argument(recursive, 'r', "recursive"_sv); parser.parse(argc, argv); - TRY(os::FileSystem::remove(path)); + if (!recursive) TRY(os::FileSystem::remove(path)); + else + TRY(os::FileSystem::remove_tree(path)); return 0; } diff --git a/libos/include/os/FileSystem.h b/libos/include/os/FileSystem.h index 19899567..2e2c1d2c 100644 --- a/libos/include/os/FileSystem.h +++ b/libos/include/os/FileSystem.h @@ -14,6 +14,10 @@ namespace os Result create_directory(StringView path, mode_t mode); Result remove(StringView path); + Result removeat(int dirfd, StringView path); + + Result remove_tree(StringView path); + Result remove_tree_at(int dirfd, StringView path); Result working_directory(); Result home_directory(); diff --git a/libos/src/FileSystem.cpp b/libos/src/FileSystem.cpp index 66e4c6aa..ba6f3102 100644 --- a/libos/src/FileSystem.cpp +++ b/libos/src/FileSystem.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -38,11 +39,50 @@ namespace os::FileSystem Result remove(StringView path) { - long rc = syscall(SYS_unlinkat, AT_FDCWD, path.chars(), 0); + return removeat(AT_FDCWD, path); + } + + Result removeat(int dirfd, StringView path) + { + long rc = syscall(SYS_unlinkat, dirfd, path.chars(), 0); return Result::from_syscall(rc); } + Result remove_tree(StringView path) + { + return remove_tree_at(AT_FDCWD, path); + } + + Result remove_tree_at(int dirfd, StringView path) + { + auto rc = removeat(dirfd, path); + if (!rc.has_error()) return {}; + if (rc.error() != ENOTEMPTY) return rc.release_error(); + + DIR* dp = opendir(path.chars()); + if (!dp) return err(errno); + + Vector entries; + + // FIXME: This is done because the kernel doesn't appreciate us deleting entries while iterating over + // directories. This means that we have to iterate first, then delete. + + struct dirent* ent; + while ((ent = readdir(dp))) + { + if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue; + auto entry = TRY(String::from_cstring(ent->d_name)); + TRY(entries.try_append(move(entry))); + } + + for (const auto& entry : entries) { TRY(remove_tree_at(dp->_fd, entry.view())); } + + closedir(dp); + + return removeat(dirfd, path); + } + Result working_directory() { char* ptr = getcwd(NULL, 0);