Parcourir la source

Add chown() syscall and a simple /bin/chown program.

Andreas Kling il y a 6 ans
Parent
commit
1d2529b4a1

+ 11 - 0
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);

+ 1 - 0
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;
 

+ 1 - 0
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(); }

+ 5 - 0
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);
+}

+ 1 - 0
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()); }

+ 12 - 0
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);

+ 3 - 0
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);

+ 5 - 0
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);
+}

+ 1 - 0
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;

+ 2 - 0
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;

+ 1 - 0
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 {

+ 32 - 0
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.

+ 1 - 0
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);

+ 1 - 0
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

+ 3 - 5
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()

+ 1 - 0
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,

+ 1 - 0
Userland/.gitignore

@@ -35,3 +35,4 @@ ln
 df
 su
 env
+chown

+ 5 - 0
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
 

+ 43 - 0
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;
+}