mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 12:32:43 +00:00 
			
		
		
		
	Add chown() syscall and a simple /bin/chown program.
This commit is contained in:
		
							parent
							
								
									711e2b2651
								
							
						
					
					
						commit
						1d2529b4a1
					
				
					 19 changed files with 130 additions and 5 deletions
				
			
		|  | @ -1346,6 +1346,17 @@ KResult Ext2FSInode::chmod(mode_t mode) | ||||||
|     return KSuccess; |     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 | unsigned Ext2FS::total_block_count() const | ||||||
| { | { | ||||||
|     LOCKER(m_lock); |     LOCKER(m_lock); | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ private: | ||||||
|     virtual int decrement_link_count() override; |     virtual int decrement_link_count() override; | ||||||
|     virtual size_t directory_entry_count() const override; |     virtual size_t directory_entry_count() const override; | ||||||
|     virtual KResult chmod(mode_t) override; |     virtual KResult chmod(mode_t) override; | ||||||
|  |     virtual KResult chown(uid_t, gid_t) override; | ||||||
| 
 | 
 | ||||||
|     void populate_lookup_cache() const; |     void populate_lookup_cache() const; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -98,6 +98,7 @@ public: | ||||||
|     virtual RetainPtr<Inode> parent() const = 0; |     virtual RetainPtr<Inode> parent() const = 0; | ||||||
|     virtual size_t directory_entry_count() const = 0; |     virtual size_t directory_entry_count() const = 0; | ||||||
|     virtual KResult chmod(mode_t) = 0; |     virtual KResult chmod(mode_t) = 0; | ||||||
|  |     virtual KResult chown(uid_t, gid_t) = 0; | ||||||
| 
 | 
 | ||||||
|     LocalSocket* socket() { return m_socket.ptr(); } |     LocalSocket* socket() { return m_socket.ptr(); } | ||||||
|     const LocalSocket* socket() const { return m_socket.ptr(); } |     const LocalSocket* socket() const { return m_socket.ptr(); } | ||||||
|  |  | ||||||
|  | @ -1124,3 +1124,8 @@ ProcFS::ProcFSDirectoryEntry* ProcFS::get_directory_entry(InodeIdentifier identi | ||||||
|         return const_cast<ProcFSDirectoryEntry*>(&m_entries[proc_file_type]); |         return const_cast<ProcFSDirectoryEntry*>(&m_entries[proc_file_type]); | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | KResult ProcFSInode::chown(uid_t, gid_t) | ||||||
|  | { | ||||||
|  |     return KResult(-EPERM); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -88,6 +88,7 @@ private: | ||||||
|     virtual RetainPtr<Inode> parent() const override; |     virtual RetainPtr<Inode> parent() const override; | ||||||
|     virtual size_t directory_entry_count() const override; |     virtual size_t directory_entry_count() const override; | ||||||
|     virtual KResult chmod(mode_t) override; |     virtual KResult chmod(mode_t) override; | ||||||
|  |     virtual KResult chown(uid_t, gid_t) override; | ||||||
| 
 | 
 | ||||||
|     ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); } |     ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); } | ||||||
|     const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); } |     const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); } | ||||||
|  |  | ||||||
|  | @ -72,6 +72,11 @@ Vector<Process*> Process::all_processes() | ||||||
|     return 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) | Region* Process::allocate_region(LinearAddress laddr, size_t size, String&& name, bool is_readable, bool is_writable, bool commit) | ||||||
| { | { | ||||||
|     size = PAGE_ROUND_UP(size); |     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()); |     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() | void Process::finalize() | ||||||
| { | { | ||||||
|     ASSERT(current == g_finalizer); |     ASSERT(current == g_finalizer); | ||||||
|  |  | ||||||
|  | @ -122,6 +122,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     mode_t umask() const { return m_umask; } |     mode_t umask() const { return m_umask; } | ||||||
| 
 | 
 | ||||||
|  |     bool in_group(gid_t) const; | ||||||
|  | 
 | ||||||
|     const FarPtr& far_ptr() const { return m_far_ptr; } |     const FarPtr& far_ptr() const { return m_far_ptr; } | ||||||
| 
 | 
 | ||||||
|     FileDescriptor* file_descriptor(int fd); |     FileDescriptor* file_descriptor(int fd); | ||||||
|  | @ -216,6 +218,7 @@ public: | ||||||
|     int sys$rmdir(const char* pathname); |     int sys$rmdir(const char* pathname); | ||||||
|     int sys$read_tsc(dword* lsw, dword* msw); |     int sys$read_tsc(dword* lsw, dword* msw); | ||||||
|     int sys$chmod(const char* pathname, mode_t); |     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$socket(int domain, int type, int protocol); | ||||||
|     int sys$bind(int sockfd, const sockaddr* addr, socklen_t); |     int sys$bind(int sockfd, const sockaddr* addr, socklen_t); | ||||||
|     int sys$listen(int sockfd, int backlog); |     int sys$listen(int sockfd, int backlog); | ||||||
|  |  | ||||||
|  | @ -314,3 +314,8 @@ KResult SynthFSInode::chmod(mode_t) | ||||||
| { | { | ||||||
|     return KResult(-EPERM); |     return KResult(-EPERM); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | KResult SynthFSInode::chown(uid_t, gid_t) | ||||||
|  | { | ||||||
|  |     return KResult(-EPERM); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -68,6 +68,7 @@ private: | ||||||
|     virtual RetainPtr<Inode> parent() const override; |     virtual RetainPtr<Inode> parent() const override; | ||||||
|     virtual size_t directory_entry_count() const override; |     virtual size_t directory_entry_count() const override; | ||||||
|     virtual KResult chmod(mode_t) override; |     virtual KResult chmod(mode_t) override; | ||||||
|  |     virtual KResult chown(uid_t, gid_t) override; | ||||||
| 
 | 
 | ||||||
|     SynthFS& fs(); |     SynthFS& fs(); | ||||||
|     const SynthFS& fs() const; |     const SynthFS& fs() const; | ||||||
|  |  | ||||||
|  | @ -215,6 +215,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, | ||||||
|         return (dword)current->sys$get_shared_buffer((int)arg1); |         return (dword)current->sys$get_shared_buffer((int)arg1); | ||||||
|     case Syscall::SC_release_shared_buffer: |     case Syscall::SC_release_shared_buffer: | ||||||
|         return current->sys$release_shared_buffer((int)arg1); |         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: |     default: | ||||||
|         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); |         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|  | @ -82,6 +82,7 @@ | ||||||
|     __ENUMERATE_SYSCALL(get_shared_buffer) \ |     __ENUMERATE_SYSCALL(get_shared_buffer) \ | ||||||
|     __ENUMERATE_SYSCALL(release_shared_buffer) \ |     __ENUMERATE_SYSCALL(release_shared_buffer) \ | ||||||
|     __ENUMERATE_SYSCALL(link) \ |     __ENUMERATE_SYSCALL(link) \ | ||||||
|  |     __ENUMERATE_SYSCALL(chown) \ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace Syscall { | namespace Syscall { | ||||||
|  |  | ||||||
|  | @ -300,6 +300,7 @@ KResult VFS::chmod(const String& path, mode_t mode, Inode& base) | ||||||
|     if (inode->fs().is_readonly()) |     if (inode->fs().is_readonly()) | ||||||
|         return KResult(-EROFS); |         return KResult(-EROFS); | ||||||
| 
 | 
 | ||||||
|  |     // FIXME: Superuser should always be allowed to chmod.
 | ||||||
|     if (current->euid() != inode->metadata().uid) |     if (current->euid() != inode->metadata().uid) | ||||||
|         return KResult(-EPERM); |         return KResult(-EPERM); | ||||||
| 
 | 
 | ||||||
|  | @ -310,6 +311,37 @@ KResult VFS::chmod(const String& path, mode_t mode, Inode& base) | ||||||
|     return inode->chmod(mode); |     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) | 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.
 |     // FIXME: This won't work nicely across mount boundaries.
 | ||||||
|  |  | ||||||
|  | @ -70,6 +70,7 @@ public: | ||||||
|     bool unlink(const String& path, Inode& base, int& error); |     bool unlink(const String& path, Inode& base, int& error); | ||||||
|     bool rmdir(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 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); |     KResult access(const String& path, int mode, Inode& base); | ||||||
|     bool stat(const String& path, int& error, int options, Inode& base, struct stat&); |     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); |     KResult utime(const String& path, Inode& base, time_t atime, time_t mtime); | ||||||
|  |  | ||||||
|  | @ -66,6 +66,7 @@ cp -v ../Userland/sysctl mnt/bin/sysctl | ||||||
| cp -v ../Userland/pape mnt/bin/pape | cp -v ../Userland/pape mnt/bin/pape | ||||||
| cp -v ../Userland/dmesg mnt/bin/dmesg | cp -v ../Userland/dmesg mnt/bin/dmesg | ||||||
| cp -v ../Userland/chmod mnt/bin/chmod | cp -v ../Userland/chmod mnt/bin/chmod | ||||||
|  | cp -v ../Userland/chown mnt/bin/chown | ||||||
| cp -v ../Userland/top mnt/bin/top | cp -v ../Userland/top mnt/bin/top | ||||||
| cp -v ../Userland/ln mnt/bin/ln | cp -v ../Userland/ln mnt/bin/ln | ||||||
| cp -v ../Userland/df mnt/bin/df | cp -v ../Userland/df mnt/bin/df | ||||||
|  |  | ||||||
|  | @ -14,12 +14,10 @@ | ||||||
| 
 | 
 | ||||||
| extern "C" { | 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; |     int rc = syscall(SC_chown, pathname, uid, gid); | ||||||
|     (void)owner; |     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||||
|     (void)group; |  | ||||||
|     assert(false); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pid_t fork() | pid_t fork() | ||||||
|  |  | ||||||
|  | @ -74,6 +74,7 @@ int mknod(const char* pathname, mode_t, dev_t); | ||||||
| long fpathconf(int fd, int name); | long fpathconf(int fd, int name); | ||||||
| long pathconf(const char *path, int name); | long pathconf(const char *path, int name); | ||||||
| char* getlogin(); | char* getlogin(); | ||||||
|  | int chown(const char* pathname, uid_t, gid_t); | ||||||
| 
 | 
 | ||||||
| enum { | enum { | ||||||
|     _PC_NAME_MAX, |     _PC_NAME_MAX, | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								Userland/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								Userland/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -35,3 +35,4 @@ ln | ||||||
| df | df | ||||||
| su | su | ||||||
| env | env | ||||||
|  | chown | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ OBJS = \ | ||||||
|        rmdir.o \
 |        rmdir.o \
 | ||||||
|        dmesg.o \
 |        dmesg.o \
 | ||||||
|        chmod.o \
 |        chmod.o \
 | ||||||
|  |        chown.o \
 | ||||||
|        top.o \
 |        top.o \
 | ||||||
|        df.o \
 |        df.o \
 | ||||||
|        ln.o \
 |        ln.o \
 | ||||||
|  | @ -62,6 +63,7 @@ APPS = \ | ||||||
|        rmdir \
 |        rmdir \
 | ||||||
|        dmesg \
 |        dmesg \
 | ||||||
|        chmod \
 |        chmod \
 | ||||||
|  |        chown \
 | ||||||
|        top \
 |        top \
 | ||||||
|        ln \
 |        ln \
 | ||||||
|        df \
 |        df \
 | ||||||
|  | @ -173,6 +175,9 @@ rmdir: rmdir.o | ||||||
| chmod: chmod.o | chmod: chmod.o | ||||||
| 	$(LD) -o $@ $(LDFLAGS) $< -lc | 	$(LD) -o $@ $(LDFLAGS) $< -lc | ||||||
| 
 | 
 | ||||||
|  | chown: chown.o | ||||||
|  | 	$(LD) -o $@ $(LDFLAGS) $< -lc | ||||||
|  | 
 | ||||||
| top: top.o | top: top.o | ||||||
| 	$(LD) -o $@ $(LDFLAGS) $< -lc | 	$(LD) -o $@ $(LDFLAGS) $< -lc | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										43
									
								
								Userland/chown.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Userland/chown.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling