diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-02-27 12:32:53 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-02-27 12:32:53 +0100 |
commit | 1d2529b4a13a18900d36f9d3b914b1bca68d962f (patch) | |
tree | d3be7621ba3c925321aabe609d6622f41887cfe8 | |
parent | 711e2b2651505da91cef983df130118bc6b709d8 (diff) | |
download | serenity-1d2529b4a13a18900d36f9d3b914b1bca68d962f.zip |
Add chown() syscall and a simple /bin/chown program.
-rw-r--r-- | Kernel/Ext2FileSystem.cpp | 11 | ||||
-rw-r--r-- | Kernel/Ext2FileSystem.h | 1 | ||||
-rw-r--r-- | Kernel/FileSystem.h | 1 | ||||
-rw-r--r-- | Kernel/ProcFS.cpp | 5 | ||||
-rw-r--r-- | Kernel/ProcFS.h | 1 | ||||
-rw-r--r-- | Kernel/Process.cpp | 12 | ||||
-rw-r--r-- | Kernel/Process.h | 3 | ||||
-rw-r--r-- | Kernel/SyntheticFileSystem.cpp | 5 | ||||
-rw-r--r-- | Kernel/SyntheticFileSystem.h | 1 | ||||
-rw-r--r-- | Kernel/Syscall.cpp | 2 | ||||
-rw-r--r-- | Kernel/Syscall.h | 1 | ||||
-rw-r--r-- | Kernel/VirtualFileSystem.cpp | 32 | ||||
-rw-r--r-- | Kernel/VirtualFileSystem.h | 1 | ||||
-rwxr-xr-x | Kernel/sync.sh | 1 | ||||
-rw-r--r-- | LibC/unistd.cpp | 8 | ||||
-rw-r--r-- | LibC/unistd.h | 1 | ||||
-rw-r--r-- | Userland/.gitignore | 1 | ||||
-rw-r--r-- | Userland/Makefile | 5 | ||||
-rw-r--r-- | Userland/chown.cpp | 43 |
19 files changed, 130 insertions, 5 deletions
diff --git a/Kernel/Ext2FileSystem.cpp b/Kernel/Ext2FileSystem.cpp index 0de3c4fbe0..8880b5f7d0 100644 --- a/Kernel/Ext2FileSystem.cpp +++ b/Kernel/Ext2FileSystem.cpp @@ -1346,6 +1346,17 @@ KResult Ext2FSInode::chmod(mode_t mode) return KSuccess; } +KResult Ext2FSInode::chown(uid_t uid, gid_t gid) +{ + LOCKER(m_lock); + if (m_raw_inode.i_uid == uid && m_raw_inode.i_gid == gid) + return KSuccess; + m_raw_inode.i_uid = uid; + m_raw_inode.i_gid = gid; + set_metadata_dirty(true); + return KSuccess; +} + unsigned Ext2FS::total_block_count() const { LOCKER(m_lock); diff --git a/Kernel/Ext2FileSystem.h b/Kernel/Ext2FileSystem.h index 737ff65dee..eddabe946a 100644 --- a/Kernel/Ext2FileSystem.h +++ b/Kernel/Ext2FileSystem.h @@ -42,6 +42,7 @@ private: virtual int decrement_link_count() override; virtual size_t directory_entry_count() const override; virtual KResult chmod(mode_t) override; + virtual KResult chown(uid_t, gid_t) override; void populate_lookup_cache() const; diff --git a/Kernel/FileSystem.h b/Kernel/FileSystem.h index 44414de856..e11c0ad6a3 100644 --- a/Kernel/FileSystem.h +++ b/Kernel/FileSystem.h @@ -98,6 +98,7 @@ public: virtual RetainPtr<Inode> parent() const = 0; virtual size_t directory_entry_count() const = 0; virtual KResult chmod(mode_t) = 0; + virtual KResult chown(uid_t, gid_t) = 0; LocalSocket* socket() { return m_socket.ptr(); } const LocalSocket* socket() const { return m_socket.ptr(); } diff --git a/Kernel/ProcFS.cpp b/Kernel/ProcFS.cpp index 5e50163f90..a46036845e 100644 --- a/Kernel/ProcFS.cpp +++ b/Kernel/ProcFS.cpp @@ -1124,3 +1124,8 @@ ProcFS::ProcFSDirectoryEntry* ProcFS::get_directory_entry(InodeIdentifier identi return const_cast<ProcFSDirectoryEntry*>(&m_entries[proc_file_type]); return nullptr; } + +KResult ProcFSInode::chown(uid_t, gid_t) +{ + return KResult(-EPERM); +} diff --git a/Kernel/ProcFS.h b/Kernel/ProcFS.h index bc4d139cb8..89b1635e65 100644 --- a/Kernel/ProcFS.h +++ b/Kernel/ProcFS.h @@ -88,6 +88,7 @@ private: virtual RetainPtr<Inode> parent() const override; virtual size_t directory_entry_count() const override; virtual KResult chmod(mode_t) override; + virtual KResult chown(uid_t, gid_t) override; ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); } const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 252bed6945..e2e05805f6 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -72,6 +72,11 @@ Vector<Process*> Process::all_processes() return processes; } +bool Process::in_group(gid_t gid) const +{ + return m_gids.contains(gid); +} + Region* Process::allocate_region(LinearAddress laddr, size_t size, String&& name, bool is_readable, bool is_writable, bool commit) { size = PAGE_ROUND_UP(size); @@ -2157,6 +2162,13 @@ int Process::sys$chmod(const char* pathname, mode_t mode) return VFS::the().chmod(String(pathname), mode, cwd_inode()); } +int Process::sys$chown(const char* pathname, uid_t uid, gid_t gid) +{ + if (!validate_read_str(pathname)) + return -EFAULT; + return VFS::the().chown(String(pathname), uid, gid, cwd_inode()); +} + void Process::finalize() { ASSERT(current == g_finalizer); diff --git a/Kernel/Process.h b/Kernel/Process.h index 08e6d85f99..182ca1ce7d 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -122,6 +122,8 @@ public: mode_t umask() const { return m_umask; } + bool in_group(gid_t) const; + const FarPtr& far_ptr() const { return m_far_ptr; } FileDescriptor* file_descriptor(int fd); @@ -216,6 +218,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$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); int sys$listen(int sockfd, int backlog); diff --git a/Kernel/SyntheticFileSystem.cpp b/Kernel/SyntheticFileSystem.cpp index 51a4c52ebe..999615d66d 100644 --- a/Kernel/SyntheticFileSystem.cpp +++ b/Kernel/SyntheticFileSystem.cpp @@ -314,3 +314,8 @@ KResult SynthFSInode::chmod(mode_t) { return KResult(-EPERM); } + +KResult SynthFSInode::chown(uid_t, gid_t) +{ + return KResult(-EPERM); +} diff --git a/Kernel/SyntheticFileSystem.h b/Kernel/SyntheticFileSystem.h index 65f27e1f2e..128584526c 100644 --- a/Kernel/SyntheticFileSystem.h +++ b/Kernel/SyntheticFileSystem.h @@ -68,6 +68,7 @@ private: virtual RetainPtr<Inode> parent() const override; virtual size_t directory_entry_count() const override; virtual KResult chmod(mode_t) override; + virtual KResult chown(uid_t, gid_t) override; SynthFS& fs(); const SynthFS& fs() const; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 37f8b8976d..1e4c62a83c 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -215,6 +215,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return (dword)current->sys$get_shared_buffer((int)arg1); case Syscall::SC_release_shared_buffer: return current->sys$release_shared_buffer((int)arg1); + case Syscall::SC_chown: + return current->sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3); default: kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 5a05b82c6e..661d7b5158 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -82,6 +82,7 @@ __ENUMERATE_SYSCALL(get_shared_buffer) \ __ENUMERATE_SYSCALL(release_shared_buffer) \ __ENUMERATE_SYSCALL(link) \ + __ENUMERATE_SYSCALL(chown) \ namespace Syscall { diff --git a/Kernel/VirtualFileSystem.cpp b/Kernel/VirtualFileSystem.cpp index 8f5cc6dd62..21b604159a 100644 --- a/Kernel/VirtualFileSystem.cpp +++ b/Kernel/VirtualFileSystem.cpp @@ -300,6 +300,7 @@ KResult VFS::chmod(const String& path, mode_t mode, Inode& base) if (inode->fs().is_readonly()) return KResult(-EROFS); + // FIXME: Superuser should always be allowed to chmod. if (current->euid() != inode->metadata().uid) return KResult(-EPERM); @@ -310,6 +311,37 @@ KResult VFS::chmod(const String& path, mode_t mode, Inode& base) return inode->chmod(mode); } +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); + 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); + + uid_t new_uid = inode->metadata().uid; + gid_t new_gid = inode->metadata().gid; + + if (a_uid != (uid_t)-1) { + if (current->euid() != a_uid && !current->is_superuser()) + return KResult(-EPERM); + new_uid = a_uid; + } + if (a_gid != (gid_t)-1) { + if (!current->in_group(a_gid) && !current->is_superuser()) + return KResult(-EPERM); + new_gid = a_gid; + } + + dbgprintf("VFS::chown(): inode %u:%u <- uid:%d, gid:%d\n", inode->fsid(), inode->index(), new_uid, new_gid); + return inode->chown(new_uid, new_gid); +} + KResultOr<RetainPtr<Inode>> VFS::resolve_path_to_inode(const String& path, Inode& base, RetainPtr<Inode>* parent_inode) { // FIXME: This won't work nicely across mount boundaries. diff --git a/Kernel/VirtualFileSystem.h b/Kernel/VirtualFileSystem.h index 895acf87b8..fdaa06d5ad 100644 --- a/Kernel/VirtualFileSystem.h +++ b/Kernel/VirtualFileSystem.h @@ -70,6 +70,7 @@ public: bool unlink(const String& path, Inode& base, int& error); bool rmdir(const String& path, Inode& base, int& error); KResult chmod(const String& path, mode_t, Inode& base); + 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&); KResult utime(const String& path, Inode& base, time_t atime, time_t mtime); diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 7f63733dca..3156ad1880 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -66,6 +66,7 @@ cp -v ../Userland/sysctl mnt/bin/sysctl cp -v ../Userland/pape mnt/bin/pape cp -v ../Userland/dmesg mnt/bin/dmesg cp -v ../Userland/chmod mnt/bin/chmod +cp -v ../Userland/chown mnt/bin/chown cp -v ../Userland/top mnt/bin/top cp -v ../Userland/ln mnt/bin/ln cp -v ../Userland/df mnt/bin/df diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 1f24340cde..51d734b3b2 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -14,12 +14,10 @@ extern "C" { -int chown(const char* pathname, uid_t owner, gid_t group) +int chown(const char* pathname, uid_t uid, gid_t gid) { - (void)pathname; - (void)owner; - (void)group; - assert(false); + int rc = syscall(SC_chown, pathname, uid, gid); + __RETURN_WITH_ERRNO(rc, rc, -1); } pid_t fork() diff --git a/LibC/unistd.h b/LibC/unistd.h index 73b4db67a8..930866ade5 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -74,6 +74,7 @@ int mknod(const char* pathname, mode_t, dev_t); long fpathconf(int fd, int name); long pathconf(const char *path, int name); char* getlogin(); +int chown(const char* pathname, uid_t, gid_t); enum { _PC_NAME_MAX, diff --git a/Userland/.gitignore b/Userland/.gitignore index ccbf5ecba9..e09f5ed784 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -35,3 +35,4 @@ ln df su env +chown diff --git a/Userland/Makefile b/Userland/Makefile index 18f07dc9bc..a764af6dec 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -26,6 +26,7 @@ OBJS = \ rmdir.o \ dmesg.o \ chmod.o \ + chown.o \ top.o \ df.o \ ln.o \ @@ -62,6 +63,7 @@ APPS = \ rmdir \ dmesg \ chmod \ + chown \ top \ ln \ df \ @@ -173,6 +175,9 @@ rmdir: rmdir.o chmod: chmod.o $(LD) -o $@ $(LDFLAGS) $< -lc +chown: chown.o + $(LD) -o $@ $(LDFLAGS) $< -lc + top: top.o $(LD) -o $@ $(LDFLAGS) $< -lc diff --git a/Userland/chown.cpp b/Userland/chown.cpp new file mode 100644 index 0000000000..e5b2be2a36 --- /dev/null +++ b/Userland/chown.cpp @@ -0,0 +1,43 @@ +#include <unistd.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <AK/AKString.h> + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: chown <uid[:gid]> <path>\n"); + return 0; + } + + uid_t new_uid = -1; + gid_t new_gid = -1; + + auto parts = String(argv[1]).split(':'); + if (parts.is_empty()) { + fprintf(stderr, "Empty uid/gid spec\n"); + return 1; + } + bool ok; + new_uid = parts[0].to_uint(ok); + if (!ok) { + fprintf(stderr, "Invalid uid: '%s'\n", parts[0].characters()); + return 1; + } + if (parts.size() == 2) { + new_gid = parts[1].to_uint(ok); + if (!ok) { + fprintf(stderr, "Invalid gid: '%s'\n", parts[1].characters()); + return 1; + } + } + + int rc = chown(argv[2], new_uid, new_gid); + if (rc < 0) { + perror("chown"); + return 1; + } + + return 0; +} |