summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-02-27 12:32:53 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-02-27 12:32:53 +0100
commit1d2529b4a13a18900d36f9d3b914b1bca68d962f (patch)
treed3be7621ba3c925321aabe609d6622f41887cfe8
parent711e2b2651505da91cef983df130118bc6b709d8 (diff)
downloadserenity-1d2529b4a13a18900d36f9d3b914b1bca68d962f.zip
Add chown() syscall and a simple /bin/chown program.
-rw-r--r--Kernel/Ext2FileSystem.cpp11
-rw-r--r--Kernel/Ext2FileSystem.h1
-rw-r--r--Kernel/FileSystem.h1
-rw-r--r--Kernel/ProcFS.cpp5
-rw-r--r--Kernel/ProcFS.h1
-rw-r--r--Kernel/Process.cpp12
-rw-r--r--Kernel/Process.h3
-rw-r--r--Kernel/SyntheticFileSystem.cpp5
-rw-r--r--Kernel/SyntheticFileSystem.h1
-rw-r--r--Kernel/Syscall.cpp2
-rw-r--r--Kernel/Syscall.h1
-rw-r--r--Kernel/VirtualFileSystem.cpp32
-rw-r--r--Kernel/VirtualFileSystem.h1
-rwxr-xr-xKernel/sync.sh1
-rw-r--r--LibC/unistd.cpp8
-rw-r--r--LibC/unistd.h1
-rw-r--r--Userland/.gitignore1
-rw-r--r--Userland/Makefile5
-rw-r--r--Userland/chown.cpp43
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;
+}