From 1b16a2904493c80af95fa70869c814f7161f5fda Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 1 Mar 2019 10:39:19 +0100 Subject: [PATCH] Kernel+Userland: Implement fchmod() syscall and use it to improve /bin/cp. /bin/cp will now copy the permission bits from source to destination. :^) --- Kernel/FileDescriptor.cpp | 7 +++++++ Kernel/FileDescriptor.h | 2 ++ Kernel/Process.cpp | 8 ++++++++ Kernel/Process.h | 1 + Kernel/Syscall.cpp | 2 ++ Kernel/Syscall.h | 1 + Kernel/VirtualFileSystem.cpp | 26 ++++++++++++++------------ Kernel/VirtualFileSystem.h | 1 + LibC/stat.cpp | 4 ++-- Userland/cp.cpp | 16 ++++++++++++++++ 10 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Kernel/FileDescriptor.cpp b/Kernel/FileDescriptor.cpp index 97405e502a9..461e0d9828c 100644 --- a/Kernel/FileDescriptor.cpp +++ b/Kernel/FileDescriptor.cpp @@ -138,6 +138,13 @@ int FileDescriptor::fstat(stat* buffer) return 0; } +KResult FileDescriptor::fchmod(mode_t mode) +{ + if (!m_inode) + return KResult(-EBADF); + return VFS::the().chmod(*m_inode, mode); +} + off_t FileDescriptor::seek(off_t offset, int whence) { ASSERT(!is_fifo()); diff --git a/Kernel/FileDescriptor.h b/Kernel/FileDescriptor.h index f7a6f982d41..3f0690fa2d7 100644 --- a/Kernel/FileDescriptor.h +++ b/Kernel/FileDescriptor.h @@ -34,6 +34,8 @@ public: ssize_t write(Process&, const byte* data, ssize_t); int fstat(stat*); + KResult fchmod(mode_t); + bool can_read(Process&); bool can_write(Process&); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 0add4535539..5a859c0b0f0 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2177,6 +2177,14 @@ int Process::sys$chmod(const char* pathname, mode_t mode) return VFS::the().chmod(String(pathname), mode, cwd_inode()); } +int Process::sys$fchmod(int fd, mode_t mode) +{ + auto* descriptor = file_descriptor(fd); + if (!descriptor) + return -EBADF; + return descriptor->fchmod(mode); +} + int Process::sys$chown(const char* pathname, uid_t uid, gid_t gid) { if (!validate_read_str(pathname)) diff --git a/Kernel/Process.h b/Kernel/Process.h index 47769e9e2d3..7a1085be2a1 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -219,6 +219,7 @@ public: int sys$rmdir(const char* pathname); int sys$read_tsc(dword* lsw, dword* msw); int sys$chmod(const char* pathname, mode_t); + int sys$fchmod(int fd, mode_t); int sys$chown(const char* pathname, uid_t, gid_t); int sys$socket(int domain, int type, int protocol); int sys$bind(int sockfd, const sockaddr* addr, socklen_t); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 1e4c62a83c6..ea983ea87e5 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -199,6 +199,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->sys$rmdir((const char*)arg1); case Syscall::SC_chmod: return current->sys$chmod((const char*)arg1, (mode_t)arg2); + case Syscall::SC_fchmod: + return current->sys$fchmod((int)arg1, (mode_t)arg2); case Syscall::SC_socket: return current->sys$socket((int)arg1, (int)arg2, (int)arg3); case Syscall::SC_bind: diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 661d7b5158e..020463322b9 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -83,6 +83,7 @@ __ENUMERATE_SYSCALL(release_shared_buffer) \ __ENUMERATE_SYSCALL(link) \ __ENUMERATE_SYSCALL(chown) \ + __ENUMERATE_SYSCALL(fchmod) \ namespace Syscall { diff --git a/Kernel/VirtualFileSystem.cpp b/Kernel/VirtualFileSystem.cpp index 4a2edea1050..b3d952db739 100644 --- a/Kernel/VirtualFileSystem.cpp +++ b/Kernel/VirtualFileSystem.cpp @@ -290,24 +290,26 @@ KResult VFS::access(const String& path, int mode, Inode& base) return KSuccess; } +KResult VFS::chmod(Inode& inode, mode_t mode) +{ + if (inode.fs().is_readonly()) + return KResult(-EROFS); + + if (current->euid() != inode.metadata().uid && !current->is_superuser()) + return KResult(-EPERM); + + // Only change the permission bits. + mode = (inode.mode() & ~04777u) | (mode & 04777u); + return inode.chmod(mode); +} + KResult VFS::chmod(const String& path, mode_t mode, Inode& base) { auto inode_or_error = resolve_path_to_inode(path, base); if (inode_or_error.is_error()) return inode_or_error.error(); auto inode = inode_or_error.value(); - - if (inode->fs().is_readonly()) - return KResult(-EROFS); - - if (current->euid() != inode->metadata().uid && !current->is_superuser()) - return KResult(-EPERM); - - // Only change the permission bits. - mode = (inode->mode() & ~04777) | (mode & 04777); - - kprintf("VFS::chmod(): %u:%u mode %o\n", inode->fsid(), inode->index(), mode); - return inode->chmod(mode); + return chmod(*inode, mode); } KResult VFS::chown(const String& path, uid_t a_uid, gid_t a_gid, Inode& base) diff --git a/Kernel/VirtualFileSystem.h b/Kernel/VirtualFileSystem.h index 38b85932269..5fa5538cb33 100644 --- a/Kernel/VirtualFileSystem.h +++ b/Kernel/VirtualFileSystem.h @@ -70,6 +70,7 @@ public: KResult unlink(const String& path, Inode& base); KResult rmdir(const String& path, Inode& base); KResult chmod(const String& path, mode_t, Inode& base); + KResult chmod(Inode&, mode_t); KResult chown(const String& path, uid_t, gid_t, Inode& base); KResult access(const String& path, int mode, Inode& base); bool stat(const String& path, int& error, int options, Inode& base, struct stat&); diff --git a/LibC/stat.cpp b/LibC/stat.cpp index 22414cda4e6..8c1f84458fc 100644 --- a/LibC/stat.cpp +++ b/LibC/stat.cpp @@ -25,8 +25,8 @@ int chmod(const char* pathname, mode_t mode) int fchmod(int fd, mode_t mode) { - dbgprintf("FIXME(LibC): fchmod(%d, %o)\n", fd, mode); - ASSERT_NOT_REACHED(); + int rc = syscall(SC_fchmod, fd, mode); + __RETURN_WITH_ERRNO(rc, rc, -1); } } diff --git a/Userland/cp.cpp b/Userland/cp.cpp index d8d9fdb419a..86def7de343 100644 --- a/Userland/cp.cpp +++ b/Userland/cp.cpp @@ -2,6 +2,7 @@ #include #include #include +#include int main(int argc, char** argv) { @@ -9,6 +10,7 @@ int main(int argc, char** argv) printf("usage: cp \n"); return 0; } + int src_fd = open(argv[1], O_RDONLY); if (src_fd < 0) { perror("open src"); @@ -20,6 +22,13 @@ int main(int argc, char** argv) return 1; } + struct stat src_stat; + int rc = fstat(src_fd, &src_stat); + if (rc < 0) { + perror("stat src"); + return 1; + } + for (;;) { char buffer[BUFSIZ]; ssize_t nread = read(src_fd, buffer, sizeof(buffer)); @@ -42,6 +51,13 @@ int main(int argc, char** argv) bufptr += nwritten; } } + + rc = fchmod(dst_fd, src_stat.st_mode); + if (rc < 0) { + perror("fchmod dst"); + return 1; + } + close(src_fd); close(dst_fd); return 0;