From 37ae00a4dd20e095ca58cf00d666ed5ab373f004 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 7 Apr 2019 23:35:26 +0200 Subject: [PATCH] Kernel+Userland: Add the rename() syscall along with a basic /bin/mv. --- Kernel/FileSystem/VirtualFileSystem.cpp | 44 +++++++++++++++++++++++++ Kernel/FileSystem/VirtualFileSystem.h | 1 + Kernel/Process.cpp | 9 +++++ Kernel/Process.h | 1 + Kernel/Syscall.cpp | 2 ++ Kernel/Syscall.h | 1 + Kernel/sync.sh | 1 + LibC/stdio.cpp | 4 +-- Userland/.gitignore | 1 + Userland/Makefile | 5 +++ Userland/mv.cpp | 32 ++++++++++++++++++ 11 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 Userland/mv.cpp diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index e1665c5efb..bb481bce2d 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -312,6 +312,50 @@ KResult VFS::chmod(const String& path, mode_t mode, Inode& base) return chmod(*inode, mode); } +KResult VFS::rename(const String& old_path, const String& new_path, Inode& base) +{ + RetainPtr old_parent_inode; + auto old_inode_or_error = resolve_path_to_inode(old_path, base, &old_parent_inode); + if (old_inode_or_error.is_error()) + return old_inode_or_error.error(); + auto old_inode = old_inode_or_error.value(); + + RetainPtr new_parent_inode; + auto new_inode_or_error = resolve_path_to_inode(new_path, base, &new_parent_inode); + if (new_inode_or_error.is_error()) { + if (new_inode_or_error.error() != -ENOENT) + return new_inode_or_error.error(); + } + + if (!new_parent_inode->metadata().may_write(current->process())) + return KResult(-EACCES); + + if (!old_parent_inode->metadata().may_write(current->process())) + return KResult(-EACCES); + + if (!new_inode_or_error.is_error()) { + auto new_inode = new_inode_or_error.value(); + // FIXME: Is this really correct? Check what other systems do. + if (new_inode.ptr() == old_inode.ptr()) + return KSuccess; + if (new_inode->is_directory() && !old_inode->is_directory()) + return KResult(-EISDIR); + auto result = new_parent_inode->remove_child(new_parent_inode->reverse_lookup(new_inode->identifier())); + if (result.is_error()) + return result; + } + + auto result = new_parent_inode->add_child(old_inode->identifier(), FileSystemPath(new_path).basename(), 0 /* FIXME: file type? */); + if (result.is_error()) + return result; + + result = old_parent_inode->remove_child(old_parent_inode->reverse_lookup(old_inode->identifier())); + if (result.is_error()) + return result; + + return KSuccess; +} + KResult VFS::chown(const String& path, uid_t a_uid, gid_t a_gid, Inode& base) { auto inode_or_error = resolve_path_to_inode(path, base); diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h index cbedd4c6b7..e59390a1a7 100644 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ b/Kernel/FileSystem/VirtualFileSystem.h @@ -75,6 +75,7 @@ public: KResult access(const String& path, int mode, Inode& base); KResult stat(const String& path, int options, Inode& base, struct stat&); KResult utime(const String& path, Inode& base, time_t atime, time_t mtime); + KResult rename(const String& oldpath, const String& newpath, Inode& base); KResultOr> open_directory(const String& path, Inode& base); void register_device(Device&); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 261062e5f9..dca2c6ad85 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2445,3 +2445,12 @@ int Process::sys$donate(int tid) Scheduler::donate_to(beneficiary, "sys$donate"); return 0; } + +int Process::sys$rename(const char* oldpath, const char* newpath) +{ + if (!validate_read_str(oldpath)) + return -EFAULT; + if (!validate_read_str(newpath)) + return -EFAULT; + return VFS::the().rename(String(oldpath), String(newpath), cwd_inode()); +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 2516abfd06..14b5328cdc 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -180,6 +180,7 @@ public: int sys$setsockopt(const Syscall::SC_setsockopt_params*); int sys$restore_signal_mask(dword mask); int sys$create_thread(int(*)(void*), void*); + int sys$rename(const char* oldpath, const char* newpath); int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer); void* sys$get_shared_buffer(int shared_buffer_id); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 3b91202739..cbba530e9b 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -241,6 +241,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->process().sys$setsockopt((const SC_setsockopt_params*)arg1); case Syscall::SC_create_thread: return current->process().sys$create_thread((int(*)(void*))arg1, (void*)arg2); + case Syscall::SC_rename: + return current->process().sys$rename((const char*)arg1, (const char*)arg2); default: kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 6bb0e3ed05..f609e16553 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -95,6 +95,7 @@ __ENUMERATE_SYSCALL(create_thread) \ __ENUMERATE_SYSCALL(gettid) \ __ENUMERATE_SYSCALL(donate) \ + __ENUMERATE_SYSCALL(rename) \ namespace Syscall { diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 8ba81975f5..111dbebeae 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -79,6 +79,7 @@ cp -v ../Userland/uc mnt/bin/uc cp -v ../Userland/tc mnt/bin/tc cp -v ../Userland/host mnt/bin/host cp -v ../Userland/qs mnt/bin/qs +cp -v ../Userland/mv mnt/bin/mv chmod 4755 mnt/bin/su cp -v ../Applications/Terminal/Terminal mnt/bin/Terminal cp -v ../Applications/FontEditor/FontEditor mnt/bin/FontEditor diff --git a/LibC/stdio.cpp b/LibC/stdio.cpp index 8fcf67e59c..1e05307508 100644 --- a/LibC/stdio.cpp +++ b/LibC/stdio.cpp @@ -418,8 +418,8 @@ int fclose(FILE* stream) int rename(const char* oldpath, const char* newpath) { - dbgprintf("FIXME(LibC): rename(%s, %s)\n", oldpath, newpath); - ASSERT_NOT_REACHED(); + int rc = syscall(SC_rename, oldpath, newpath); + __RETURN_WITH_ERRNO(rc, rc, -1); } char* tmpnam(char*) diff --git a/Userland/.gitignore b/Userland/.gitignore index 3d207bf5d7..25f4c5d2b5 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -42,3 +42,4 @@ uc tc host qs +mv diff --git a/Userland/Makefile b/Userland/Makefile index c79a319a94..66707b88a0 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -38,6 +38,7 @@ OBJS = \ tc.o \ host.o \ qs.o \ + mv.o \ rm.o APPS = \ @@ -81,6 +82,7 @@ APPS = \ tc \ host \ qs \ + mv \ rm ARCH_FLAGS = @@ -223,6 +225,9 @@ host: host.o qs: qs.o $(LD) -o $@ $(LDFLAGS) -L../LibGUI $< -lgui -lc +mv: mv.o + $(LD) -o $@ $(LDFLAGS) -L../LibGUI $< -lgui -lc + .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Userland/mv.cpp b/Userland/mv.cpp new file mode 100644 index 0000000000..486761c00e --- /dev/null +++ b/Userland/mv.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 3) { + printf("usage: mv \n"); + return 1; + } + + String old_path = argv[1]; + String new_path = argv[2]; + + struct stat st; + int rc = lstat(new_path.characters(), &st); + if (rc == 0) { + if (S_ISDIR(st.st_mode)) { + auto old_basename = FileSystemPath(old_path).basename(); + new_path = String::format("%s/%s", new_path.characters(), old_basename.characters()); + } + } + + rc = rename(old_path.characters(), new_path.characters()); + if (rc < 0) { + perror("rename"); + return 1; + } + return 0; +}