diff --git a/AK/printf.cpp b/AK/printf.cpp index 4b8cf89172..63a028cefc 100644 --- a/AK/printf.cpp +++ b/AK/printf.cpp @@ -66,6 +66,47 @@ ALWAYS_INLINE int printNumber(PutChFunc putch, char*& bufptr, dword number, bool return fieldWidth; } +template +ALWAYS_INLINE int print_octal_number(PutChFunc putch, char*& bufptr, dword number, bool leftPad, bool zeroPad, dword fieldWidth) +{ + dword divisor = 134217728; + char ch; + char padding = 1; + char buf[32]; + char* p = buf; + + for (;;) { + ch = '0' + (number / divisor); + number %= divisor; + if (ch != '0') + padding = 0; + if (!padding || divisor == 1) + *(p++) = ch; + if (divisor == 1) + break; + divisor /= 8; + } + + size_t numlen = p - buf; + if (!fieldWidth || fieldWidth < numlen) + fieldWidth = numlen; + if (!leftPad) { + for (unsigned i = 0; i < fieldWidth - numlen; ++i) { + putch(bufptr, zeroPad ? '0' : ' '); + } + } + for (unsigned i = 0; i < numlen; ++i) { + putch(bufptr, buf[i]); + } + if (leftPad) { + for (unsigned i = 0; i < fieldWidth - numlen; ++i) { + putch(bufptr, ' '); + } + } + + return fieldWidth; +} + template ALWAYS_INLINE int printString(PutChFunc putch, char*& bufptr, const char* str, bool leftPad, dword fieldWidth) { @@ -145,6 +186,10 @@ one_more: ret += printNumber(putch, bufptr, va_arg(ap, dword), leftPad, zeroPad, fieldWidth); break; + case 'o': + ret += print_octal_number(putch, bufptr, va_arg(ap, dword), leftPad, zeroPad, fieldWidth); + break; + case 'x': ret += printHex(putch, bufptr, va_arg(ap, dword), 8); break; diff --git a/Kernel/Ext2FileSystem.cpp b/Kernel/Ext2FileSystem.cpp index 1b996960ac..0ebac398f6 100644 --- a/Kernel/Ext2FileSystem.cpp +++ b/Kernel/Ext2FileSystem.cpp @@ -1292,3 +1292,13 @@ size_t Ext2FSInode::directory_entry_count() const LOCKER(m_lock); return m_lookup_cache.size(); } + +bool Ext2FSInode::chmod(mode_t mode, int& error) +{ + error = 0; + if (m_raw_inode.i_mode == mode) + return true; + m_raw_inode.i_mode = mode; + set_metadata_dirty(true); + return true; +} diff --git a/Kernel/Ext2FileSystem.h b/Kernel/Ext2FileSystem.h index b6425a3658..d8d29dd21f 100644 --- a/Kernel/Ext2FileSystem.h +++ b/Kernel/Ext2FileSystem.h @@ -41,6 +41,7 @@ private: virtual int increment_link_count() override; virtual int decrement_link_count() override; virtual size_t directory_entry_count() const override; + virtual bool chmod(mode_t, int& error) override; void populate_lookup_cache() const; diff --git a/Kernel/FileSystem.h b/Kernel/FileSystem.h index c7411f558e..d1b4fe533e 100644 --- a/Kernel/FileSystem.h +++ b/Kernel/FileSystem.h @@ -74,6 +74,7 @@ public: bool is_symlink() const { return metadata().isSymbolicLink(); } bool is_directory() const { return metadata().isDirectory(); } bool is_character_device() const { return metadata().isCharacterDevice(); } + mode_t mode() const { return metadata().mode; } InodeIdentifier identifier() const { return { fsid(), index() }; } virtual InodeMetadata metadata() const = 0; @@ -89,6 +90,7 @@ public: virtual bool remove_child(const String& name, int& error) = 0; virtual RetainPtr parent() const = 0; virtual size_t directory_entry_count() const = 0; + virtual bool chmod(mode_t, int& error) = 0; bool is_metadata_dirty() const { return m_metadata_dirty; } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 70ed4d49d3..61e388cc41 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2120,3 +2120,13 @@ int Process::sys$read_tsc(dword* lsw, dword* msw) read_tsc(*lsw, *msw); return 0; } + +int Process::sys$chmod(const char* pathname, mode_t mode) +{ + if (!validate_read_str(pathname)) + return -EFAULT; + int error; + if (!VFS::the().chmod(String(pathname), mode, *cwd_inode(), error)) + return error; + return 0; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index d60ef223bd..a92e4effec 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -196,6 +196,7 @@ public: int sys$unlink(const char* pathname); int sys$rmdir(const char* pathname); int sys$read_tsc(dword* lsw, dword* msw); + int sys$chmod(const char* pathname, mode_t); int gui$create_window(const GUI_WindowParameters*); int gui$destroy_window(int window_id); diff --git a/Kernel/SyntheticFileSystem.cpp b/Kernel/SyntheticFileSystem.cpp index 54925f84a5..1e6c90655d 100644 --- a/Kernel/SyntheticFileSystem.cpp +++ b/Kernel/SyntheticFileSystem.cpp @@ -312,3 +312,9 @@ size_t SynthFSInode::directory_entry_count() const // NOTE: The 2 is for '.' and '..' return m_children.size() + 2; } + +bool SynthFSInode::chmod(mode_t, int& error) +{ + error = -EPERM; + return false; +} diff --git a/Kernel/SyntheticFileSystem.h b/Kernel/SyntheticFileSystem.h index c40697546e..765eb6a146 100644 --- a/Kernel/SyntheticFileSystem.h +++ b/Kernel/SyntheticFileSystem.h @@ -65,6 +65,7 @@ private: virtual bool remove_child(const String& name, int& error) override; virtual RetainPtr parent() const override; virtual size_t directory_entry_count() const override; + virtual bool chmod(mode_t, int& error) override; SynthFS& fs(); const SynthFS& fs() const; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index e9df83d45a..c29298f363 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -221,6 +221,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->gui$set_global_cursor_tracking_enabled((int)arg1, (bool)arg2); case Syscall::SC_rmdir: return current->sys$rmdir((const char*)arg1); + case Syscall::SC_chmod: + return current->sys$chmod((const char*)arg1, (mode_t)arg2); 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 184ccda385..b1c7e47ea4 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -83,6 +83,7 @@ __ENUMERATE_SYSCALL(gui_notify_paint_finished) \ __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \ __ENUMERATE_SYSCALL(rmdir) \ + __ENUMERATE_SYSCALL(chmod) \ #ifdef SERENITY diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index c896a61c89..b469d57f59 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -214,7 +214,7 @@ struct winsize { typedef dword dev_t; typedef dword ino_t; -typedef dword mode_t; +typedef word mode_t; typedef dword nlink_t; typedef dword uid_t; typedef dword gid_t; diff --git a/Kernel/VirtualFileSystem.cpp b/Kernel/VirtualFileSystem.cpp index 46f2ad99db..5b187f9232 100644 --- a/Kernel/VirtualFileSystem.cpp +++ b/Kernel/VirtualFileSystem.cpp @@ -228,6 +228,37 @@ bool VFS::mkdir(const String& path, mode_t mode, InodeIdentifier base, int& erro return false; } +bool VFS::chmod(const String& path, mode_t mode, Inode& base, int& error) +{ + error = -EWHYTHO; + // FIXME: This won't work nicely across mount boundaries. + FileSystemPath p(path); + if (!p.is_valid()) { + error = -EINVAL; + return false; + } + + InodeIdentifier parent_dir; + auto inode_id = resolve_path(path, base.identifier(), error, 0, &parent_dir); + if (!inode_id.is_valid()) { + error = -ENOENT; + return false; + } + + auto inode = get_inode(inode_id); + + // FIXME: Permission checks. + + // Only change the permission bits. + mode = (inode->mode() & ~04777) | (mode & 04777); + + kprintf("VFS::chmod(): %u:%u mode %o\n", inode_id.fsid(), inode_id.index(), mode); + if (!inode->chmod(mode, error)) + return false; + error = 0; + return true; +} + bool VFS::unlink(const String& path, Inode& base, int& error) { error = -EWHYTHO; diff --git a/Kernel/VirtualFileSystem.h b/Kernel/VirtualFileSystem.h index 0832207956..23d9c37a96 100644 --- a/Kernel/VirtualFileSystem.h +++ b/Kernel/VirtualFileSystem.h @@ -69,6 +69,7 @@ public: bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error); bool unlink(const String& path, Inode& base, int& error); bool rmdir(const String& path, Inode& base, int& error); + bool chmod(const String& path, mode_t, Inode& base, int& error); void register_character_device(CharacterDevice&); diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 429515b955..ae578d2fdd 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -50,6 +50,7 @@ cp -v ../Userland/guitest2 mnt/bin/guitest2 cp -v ../Userland/sysctl mnt/bin/sysctl cp -v ../Terminal/Terminal mnt/bin/Terminal cp -v ../Userland/dmesg mnt/bin/dmesg +cp -v ../Userland/chmod mnt/bin/chmod sh sync-local.sh cp -v kernel.map mnt/ ln -s dir_a mnt/dir_cur diff --git a/Kernel/types.h b/Kernel/types.h index 605a5e6a8c..2f20a750e2 100644 --- a/Kernel/types.h +++ b/Kernel/types.h @@ -37,7 +37,7 @@ typedef dword ino_t; typedef signed_dword off_t; typedef dword dev_t; -typedef dword mode_t; +typedef word mode_t; typedef dword nlink_t; typedef dword blksize_t; typedef dword blkcnt_t; diff --git a/LibC/errno_numbers.h b/LibC/errno_numbers.h index 4de6c0c184..3d5a80dde0 100644 --- a/LibC/errno_numbers.h +++ b/LibC/errno_numbers.h @@ -1,6 +1,7 @@ #pragma once #define __ENUMERATE_ALL_ERRORS \ + __ERROR(ESUCCESS, "Success (not an error)") \ __ERROR(EPERM, "Operation not permitted") \ __ERROR(ENOENT, "No such file or directory") \ __ERROR(ESRCH, "No such process") \ diff --git a/LibC/stat.cpp b/LibC/stat.cpp index a210a7b240..47bfe01e6a 100644 --- a/LibC/stat.cpp +++ b/LibC/stat.cpp @@ -15,5 +15,11 @@ int mkdir(const char* pathname, mode_t mode) __RETURN_WITH_ERRNO(rc, rc, -1); } +int chmod(const char* pathname, mode_t mode) +{ + int rc = syscall(SC_chmod, pathname, mode); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/LibC/sys/types.h b/LibC/sys/types.h index 9b1370ec85..7d283480a9 100644 --- a/LibC/sys/types.h +++ b/LibC/sys/types.h @@ -24,7 +24,7 @@ typedef uint32_t ino_t; typedef int32_t off_t; typedef uint32_t dev_t; -typedef uint32_t mode_t; +typedef uint16_t mode_t; typedef uint32_t nlink_t; typedef uint32_t blksize_t; typedef uint32_t blkcnt_t; diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 3a22cb371d..6f3b16598c 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -97,7 +97,7 @@ int open(const char* path, int options, ...) { va_list ap; va_start(ap, options); - int rc = syscall(SC_open, path, options, va_arg(ap, mode_t)); + int rc = syscall(SC_open, path, options, (mode_t)va_arg(ap, unsigned)); va_end(ap); __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/.gitignore b/Userland/.gitignore index fa2a9cc58e..61deb7d2ef 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -28,3 +28,4 @@ rm cp rmdir dmesg +chmod diff --git a/Userland/Makefile b/Userland/Makefile index 80bb0ee77c..d4b28b9fec 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -25,6 +25,7 @@ OBJS = \ cp.o \ rmdir.o \ dmesg.o \ + chmod.o \ rm.o APPS = \ @@ -55,6 +56,7 @@ APPS = \ cp \ rmdir \ dmesg \ + chmod \ rm ARCH_FLAGS = @@ -159,6 +161,9 @@ rm: rm.o rmdir: rmdir.o $(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a +chmod: chmod.o + $(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a + .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Userland/chmod.cpp b/Userland/chmod.cpp new file mode 100644 index 0000000000..7fb32e2fee --- /dev/null +++ b/Userland/chmod.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 3) { + printf("usage: chmod \n"); + return 1; + } + + mode_t mode; + int rc = sscanf(argv[1], "%o", &mode); + if (rc != 1) { + perror("sscanf"); + return 1; + } + + rc = chmod(argv[2], mode); + if (rc < 0) { + perror("chmod"); + return 1; + } + + return 0; +}