Browse Source

Implement basic chmod() syscall and /bin/chmod helper.

Only raw octal modes are supported right now.
This patch also changes mode_t from 32-bit to 16-bit to match the on-disk
type used by Ext2FS.

I also ran into EPERM being errno=0 which was confusing, so I inserted an
ESUCCESS in its place.
Andreas Kling 6 years ago
parent
commit
c30e2c8d44

+ 45 - 0
AK/printf.cpp

@@ -66,6 +66,47 @@ ALWAYS_INLINE int printNumber(PutChFunc putch, char*& bufptr, dword number, bool
     return fieldWidth;
     return fieldWidth;
 }
 }
 
 
+template<typename PutChFunc>
+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<typename PutChFunc>
 template<typename PutChFunc>
 ALWAYS_INLINE int printString(PutChFunc putch, char*& bufptr, const char* str, bool leftPad, dword fieldWidth)
 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);
                     ret += printNumber(putch, bufptr, va_arg(ap, dword), leftPad, zeroPad, fieldWidth);
                     break;
                     break;
 
 
+                case 'o':
+                    ret += print_octal_number(putch, bufptr, va_arg(ap, dword), leftPad, zeroPad, fieldWidth);
+                    break;
+
                 case 'x':
                 case 'x':
                     ret += printHex(putch, bufptr, va_arg(ap, dword), 8);
                     ret += printHex(putch, bufptr, va_arg(ap, dword), 8);
                     break;
                     break;

+ 10 - 0
Kernel/Ext2FileSystem.cpp

@@ -1292,3 +1292,13 @@ size_t Ext2FSInode::directory_entry_count() const
     LOCKER(m_lock);
     LOCKER(m_lock);
     return m_lookup_cache.size();
     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;
+}

+ 1 - 0
Kernel/Ext2FileSystem.h

@@ -41,6 +41,7 @@ private:
     virtual int increment_link_count() override;
     virtual int increment_link_count() override;
     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 bool chmod(mode_t, int& error) override;
 
 
     void populate_lookup_cache() const;
     void populate_lookup_cache() const;
 
 

+ 2 - 0
Kernel/FileSystem.h

@@ -74,6 +74,7 @@ public:
     bool is_symlink() const { return metadata().isSymbolicLink(); }
     bool is_symlink() const { return metadata().isSymbolicLink(); }
     bool is_directory() const { return metadata().isDirectory(); }
     bool is_directory() const { return metadata().isDirectory(); }
     bool is_character_device() const { return metadata().isCharacterDevice(); }
     bool is_character_device() const { return metadata().isCharacterDevice(); }
+    mode_t mode() const { return metadata().mode; }
 
 
     InodeIdentifier identifier() const { return { fsid(), index() }; }
     InodeIdentifier identifier() const { return { fsid(), index() }; }
     virtual InodeMetadata metadata() const = 0;
     virtual InodeMetadata metadata() const = 0;
@@ -89,6 +90,7 @@ public:
     virtual bool remove_child(const String& name, int& error) = 0;
     virtual bool remove_child(const String& name, int& error) = 0;
     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 bool chmod(mode_t, int& error) = 0;
 
 
     bool is_metadata_dirty() const { return m_metadata_dirty; }
     bool is_metadata_dirty() const { return m_metadata_dirty; }
 
 

+ 10 - 0
Kernel/Process.cpp

@@ -2120,3 +2120,13 @@ int Process::sys$read_tsc(dword* lsw, dword* msw)
     read_tsc(*lsw, *msw);
     read_tsc(*lsw, *msw);
     return 0;
     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;
+}

+ 1 - 0
Kernel/Process.h

@@ -196,6 +196,7 @@ public:
     int sys$unlink(const char* pathname);
     int sys$unlink(const char* pathname);
     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 gui$create_window(const GUI_WindowParameters*);
     int gui$create_window(const GUI_WindowParameters*);
     int gui$destroy_window(int window_id);
     int gui$destroy_window(int window_id);

+ 6 - 0
Kernel/SyntheticFileSystem.cpp

@@ -312,3 +312,9 @@ size_t SynthFSInode::directory_entry_count() const
     // NOTE: The 2 is for '.' and '..'
     // NOTE: The 2 is for '.' and '..'
     return m_children.size() + 2;
     return m_children.size() + 2;
 }
 }
+
+bool SynthFSInode::chmod(mode_t, int& error)
+{
+    error = -EPERM;
+    return false;
+}

+ 1 - 0
Kernel/SyntheticFileSystem.h

@@ -65,6 +65,7 @@ private:
     virtual bool remove_child(const String& name, int& error) override;
     virtual bool remove_child(const String& name, int& error) override;
     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 bool chmod(mode_t, int& error) override;
 
 
     SynthFS& fs();
     SynthFS& fs();
     const SynthFS& fs() const;
     const SynthFS& fs() const;

+ 2 - 0
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);
         return current->gui$set_global_cursor_tracking_enabled((int)arg1, (bool)arg2);
     case Syscall::SC_rmdir:
     case Syscall::SC_rmdir:
         return current->sys$rmdir((const char*)arg1);
         return current->sys$rmdir((const char*)arg1);
+    case Syscall::SC_chmod:
+        return current->sys$chmod((const char*)arg1, (mode_t)arg2);
     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;

+ 1 - 0
Kernel/Syscall.h

@@ -83,6 +83,7 @@
     __ENUMERATE_SYSCALL(gui_notify_paint_finished) \
     __ENUMERATE_SYSCALL(gui_notify_paint_finished) \
     __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \
     __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \
     __ENUMERATE_SYSCALL(rmdir) \
     __ENUMERATE_SYSCALL(rmdir) \
+    __ENUMERATE_SYSCALL(chmod) \
 
 
 
 
 #ifdef SERENITY
 #ifdef SERENITY

+ 1 - 1
Kernel/UnixTypes.h

@@ -214,7 +214,7 @@ struct winsize {
 
 
 typedef dword dev_t;
 typedef dword dev_t;
 typedef dword ino_t;
 typedef dword ino_t;
-typedef dword mode_t;
+typedef word mode_t;
 typedef dword nlink_t;
 typedef dword nlink_t;
 typedef dword uid_t;
 typedef dword uid_t;
 typedef dword gid_t;
 typedef dword gid_t;

+ 31 - 0
Kernel/VirtualFileSystem.cpp

@@ -228,6 +228,37 @@ bool VFS::mkdir(const String& path, mode_t mode, InodeIdentifier base, int& erro
     return false;
     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)
 bool VFS::unlink(const String& path, Inode& base, int& error)
 {
 {
     error = -EWHYTHO;
     error = -EWHYTHO;

+ 1 - 0
Kernel/VirtualFileSystem.h

@@ -69,6 +69,7 @@ public:
     bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error);
     bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error);
     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);
+    bool chmod(const String& path, mode_t, Inode& base, int& error);
 
 
     void register_character_device(CharacterDevice&);
     void register_character_device(CharacterDevice&);
 
 

+ 1 - 0
Kernel/sync.sh

@@ -50,6 +50,7 @@ cp -v ../Userland/guitest2 mnt/bin/guitest2
 cp -v ../Userland/sysctl mnt/bin/sysctl
 cp -v ../Userland/sysctl mnt/bin/sysctl
 cp -v ../Terminal/Terminal mnt/bin/Terminal
 cp -v ../Terminal/Terminal mnt/bin/Terminal
 cp -v ../Userland/dmesg mnt/bin/dmesg
 cp -v ../Userland/dmesg mnt/bin/dmesg
+cp -v ../Userland/chmod mnt/bin/chmod
 sh sync-local.sh
 sh sync-local.sh
 cp -v kernel.map mnt/
 cp -v kernel.map mnt/
 ln -s dir_a mnt/dir_cur
 ln -s dir_a mnt/dir_cur

+ 1 - 1
Kernel/types.h

@@ -37,7 +37,7 @@ typedef dword ino_t;
 typedef signed_dword off_t;
 typedef signed_dword off_t;
 
 
 typedef dword dev_t;
 typedef dword dev_t;
-typedef dword mode_t;
+typedef word mode_t;
 typedef dword nlink_t;
 typedef dword nlink_t;
 typedef dword blksize_t;
 typedef dword blksize_t;
 typedef dword blkcnt_t;
 typedef dword blkcnt_t;

+ 1 - 0
LibC/errno_numbers.h

@@ -1,6 +1,7 @@
 #pragma once
 #pragma once
 
 
 #define __ENUMERATE_ALL_ERRORS \
 #define __ENUMERATE_ALL_ERRORS \
+    __ERROR(ESUCCESS,       "Success (not an error)") \
     __ERROR(EPERM,          "Operation not permitted") \
     __ERROR(EPERM,          "Operation not permitted") \
     __ERROR(ENOENT,         "No such file or directory") \
     __ERROR(ENOENT,         "No such file or directory") \
     __ERROR(ESRCH,          "No such process") \
     __ERROR(ESRCH,          "No such process") \

+ 6 - 0
LibC/stat.cpp

@@ -15,5 +15,11 @@ int mkdir(const char* pathname, mode_t mode)
     __RETURN_WITH_ERRNO(rc, rc, -1);
     __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);
+}
+
 }
 }
 
 

+ 1 - 1
LibC/sys/types.h

@@ -24,7 +24,7 @@ typedef uint32_t ino_t;
 typedef int32_t off_t;
 typedef int32_t off_t;
 
 
 typedef uint32_t dev_t;
 typedef uint32_t dev_t;
-typedef uint32_t mode_t;
+typedef uint16_t mode_t;
 typedef uint32_t nlink_t;
 typedef uint32_t nlink_t;
 typedef uint32_t blksize_t;
 typedef uint32_t blksize_t;
 typedef uint32_t blkcnt_t;
 typedef uint32_t blkcnt_t;

+ 1 - 1
LibC/unistd.cpp

@@ -97,7 +97,7 @@ int open(const char* path, int options, ...)
 {
 {
     va_list ap;
     va_list ap;
     va_start(ap, options);
     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);
     va_end(ap);
     __RETURN_WITH_ERRNO(rc, rc, -1);
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }
 }

+ 1 - 0
Userland/.gitignore

@@ -28,3 +28,4 @@ rm
 cp
 cp
 rmdir
 rmdir
 dmesg
 dmesg
+chmod

+ 5 - 0
Userland/Makefile

@@ -25,6 +25,7 @@ OBJS = \
        cp.o \
        cp.o \
        rmdir.o \
        rmdir.o \
        dmesg.o \
        dmesg.o \
+       chmod.o \
        rm.o
        rm.o
 
 
 APPS = \
 APPS = \
@@ -55,6 +56,7 @@ APPS = \
        cp \
        cp \
        rmdir \
        rmdir \
        dmesg \
        dmesg \
+       chmod \
        rm
        rm
 
 
 ARCH_FLAGS =
 ARCH_FLAGS =
@@ -159,6 +161,9 @@ rm: rm.o
 rmdir: rmdir.o
 rmdir: rmdir.o
 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
 
 
+chmod: chmod.o
+	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
+
 .cpp.o:
 .cpp.o:
 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
 
 

+ 27 - 0
Userland/chmod.cpp

@@ -0,0 +1,27 @@
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        printf("usage: chmod <octal-mode> <path>\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;
+}