From f70136a32455e59ef4adf9f35031a22c7027c8f6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 22 Jan 2019 00:58:13 +0100 Subject: [PATCH] Kernel: Support open() with O_CREAT. It's now possible to create zero-length files! :^) Also hook up the new functionality in /bin/touch. --- Kernel/KSyms.cpp | 2 +- Kernel/Process.cpp | 16 ++++----- Kernel/Process.h | 2 +- Kernel/Syscall.cpp | 2 +- LibC/unistd.cpp | 3 +- Userland/touch.cpp | 39 ++++++++++++++++++++-- VirtualFileSystem/Ext2FileSystem.cpp | 36 +++++++++++++-------- VirtualFileSystem/VirtualFileSystem.cpp | 43 ++++++++++++++++++++----- VirtualFileSystem/VirtualFileSystem.h | 4 +-- 9 files changed, 108 insertions(+), 39 deletions(-) diff --git a/Kernel/KSyms.cpp b/Kernel/KSyms.cpp index 4168e8a43cd..4a89206fee1 100644 --- a/Kernel/KSyms.cpp +++ b/Kernel/KSyms.cpp @@ -124,7 +124,7 @@ void init_ksyms() void load_ksyms() { int error; - auto descriptor = VFS::the().open("/kernel.map", error); + auto descriptor = VFS::the().open("/kernel.map", error, 0, 0); if (!descriptor) { kprintf("Failed to open /kernel.map\n"); } else { diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 71d5a7f0a1a..df38358ee0b 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -289,7 +289,7 @@ int Process::do_exec(const String& path, Vector&& arguments, Vectoridentifier() : InodeIdentifier()); + auto descriptor = VFS::the().open(path, error, 0, 0, m_cwd ? m_cwd->identifier() : InodeIdentifier()); if (!descriptor) { ASSERT(error != 0); return error; @@ -1117,7 +1117,7 @@ int Process::sys$utime(const char* pathname, const Unix::utimbuf* buf) return -EFAULT; String path(pathname); int error; - auto descriptor = VFS::the().open(move(path), error, 0, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(move(path), error, 0, 0, cwd_inode()->identifier()); if (!descriptor) return error; auto& inode = *descriptor->inode(); @@ -1206,7 +1206,7 @@ int Process::sys$lstat(const char* path, Unix::stat* statbuf) if (!validate_write_typed(statbuf)) return -EFAULT; int error; - auto descriptor = VFS::the().open(move(path), error, O_NOFOLLOW_NOERROR, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(move(path), error, O_NOFOLLOW_NOERROR, 0, cwd_inode()->identifier()); if (!descriptor) return error; descriptor->stat(statbuf); @@ -1218,7 +1218,7 @@ int Process::sys$stat(const char* path, Unix::stat* statbuf) if (!validate_write_typed(statbuf)) return -EFAULT; int error; - auto descriptor = VFS::the().open(move(path), error, 0, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(move(path), error, 0, 0, cwd_inode()->identifier()); if (!descriptor) return error; descriptor->stat(statbuf); @@ -1233,7 +1233,7 @@ int Process::sys$readlink(const char* path, char* buffer, size_t size) return -EFAULT; int error; - auto descriptor = VFS::the().open(path, error, O_RDONLY | O_NOFOLLOW_NOERROR, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(path, error, O_RDONLY | O_NOFOLLOW_NOERROR, 0, cwd_inode()->identifier()); if (!descriptor) return error; @@ -1255,7 +1255,7 @@ int Process::sys$chdir(const char* path) if (!validate_read_str(path)) return -EFAULT; int error; - auto descriptor = VFS::the().open(path, error, 0, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(path, error, 0, 0, cwd_inode()->identifier()); if (!descriptor) return error; if (!descriptor->is_directory()) @@ -1288,7 +1288,7 @@ size_t Process::number_of_open_file_descriptors() const return count; } -int Process::sys$open(const char* path, int options) +int Process::sys$open(const char* path, int options, mode_t mode) { #ifdef DEBUG_IO dbgprintf("%s(%u) sys$open(\"%s\")\n", name().characters(), pid(), path); @@ -1299,7 +1299,7 @@ int Process::sys$open(const char* path, int options) return -EMFILE; int error = -EWHYTHO; ASSERT(cwd_inode()); - auto descriptor = VFS::the().open(path, error, options, cwd_inode()->identifier()); + auto descriptor = VFS::the().open(path, error, options, mode, cwd_inode()->identifier()); if (!descriptor) return error; if (options & O_DIRECTORY && !descriptor->is_directory()) diff --git a/Kernel/Process.h b/Kernel/Process.h index 858a3f09882..c4a57402ac6 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -140,7 +140,7 @@ public: pid_t sys$getpid(); pid_t sys$getppid(); mode_t sys$umask(mode_t); - int sys$open(const char* path, int options); + int sys$open(const char* path, int options, mode_t mode = 0); int sys$close(int fd); ssize_t sys$read(int fd, void* outbuf, size_t nread); ssize_t sys$write(int fd, const void*, size_t); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 9a551b2a374..3e8d5ba9a64 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -73,7 +73,7 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, case Syscall::SC_getcwd: return current->sys$getcwd((char*)arg1, (size_t)arg2); case Syscall::SC_open: - return current->sys$open((const char*)arg1, (int)arg2); + return current->sys$open((const char*)arg1, (int)arg2, (mode_t)arg3); case Syscall::SC_write: return current->sys$write((int)arg1, (const void*)arg2, (size_t)arg3); case Syscall::SC_close: diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index d039c1daefa..0e10e7811be 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include extern "C" { @@ -91,7 +92,7 @@ int open(const char* path, int options, ...) { va_list ap; va_start(ap, options); - int rc = syscall(SC_open, path, options, ap); + int rc = syscall(SC_open, path, options, va_arg(ap, mode_t)); va_end(ap); __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/touch.cpp b/Userland/touch.cpp index 6d857198f64..3d0dcb58bf5 100644 --- a/Userland/touch.cpp +++ b/Userland/touch.cpp @@ -1,6 +1,26 @@ #include +#include #include +#include +#include +#include #include +#include + +static bool file_exists(const char* path) +{ + struct stat st; + int rc = stat(path, &st); + if (rc < 0) { + if (errno == ENOENT) + return false; + } + if (rc == 0) { + return true; + } + perror("stat"); + exit(1); +} int main(int argc, char** argv) { @@ -8,9 +28,22 @@ int main(int argc, char** argv) fprintf(stderr, "usage: touch \n"); return 1; } - int rc = utime(argv[1], nullptr); - if (rc < 0) - perror("utime"); + if (file_exists(argv[1])) { + int rc = utime(argv[1], nullptr); + if (rc < 0) + perror("utime"); + } else { + int fd = open(argv[1], O_CREAT, 0010644); + if (fd < 0) { + perror("open"); + return 1; + } + int rc = close(fd); + if (rc < 0) { + perror("close"); + return 1; + } + } return 0; } diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp index 106b1058ddf..71b20b8dd32 100644 --- a/VirtualFileSystem/Ext2FileSystem.cpp +++ b/VirtualFileSystem/Ext2FileSystem.cpp @@ -435,7 +435,12 @@ bool Ext2FSInode::add_child(InodeIdentifier child_id, const String& name, byte f } entries.append({ name.characters(), name.length(), child_id, file_type }); - return fs().write_directory_inode(index(), move(entries)); + bool success = fs().write_directory_inode(index(), move(entries)); + if (success) { + LOCKER(m_lock); + m_lookup_cache.set(name, child_id.index()); + } + return success; } bool Ext2FS::write_directory_inode(unsigned directoryInode, Vector&& entries) @@ -598,27 +603,29 @@ bool Ext2FS::write_ext2_inode(unsigned inode, const ext2_inode& e2inode) Vector Ext2FS::allocate_blocks(unsigned group, unsigned count) { - dbgprintf("Ext2FS: allocateBlocks(group: %u, count: %u)\n", group, count); + dbgprintf("Ext2FS: allocate_blocks(group: %u, count: %u)\n", group, count); + if (count == 0) + return { }; auto& bgd = group_descriptor(group); if (bgd.bg_free_blocks_count < count) { - kprintf("ExtFS: allocateBlocks can't allocate out of group %u, wanted %u but only %u available\n", group, count, bgd.bg_free_blocks_count); + kprintf("Ext2FS: allocate_blocks can't allocate out of group %u, wanted %u but only %u available\n", group, count, bgd.bg_free_blocks_count); return { }; } // FIXME: Implement a scan that finds consecutive blocks if possible. Vector blocks; - traverse_block_bitmap(group, [&blocks, count] (unsigned firstBlockInBitmap, const Bitmap& bitmap) { + traverse_block_bitmap(group, [&blocks, count] (unsigned first_block_in_bitmap, const Bitmap& bitmap) { for (unsigned i = 0; i < bitmap.size(); ++i) { if (!bitmap.get(i)) { - blocks.append(firstBlockInBitmap + i); + blocks.append(first_block_in_bitmap + i); if (blocks.size() == count) return false; } } return true; }); - dbgprintf("Ext2FS: allocateBlock found these blocks:\n"); + dbgprintf("Ext2FS: allocate_block found these blocks:\n"); for (auto& bi : blocks) { dbgprintf(" > %u\n", bi); } @@ -628,7 +635,7 @@ Vector Ext2FS::allocate_blocks(unsigned group, unsigned coun unsigned Ext2FS::allocate_inode(unsigned preferredGroup, unsigned expectedSize) { - dbgprintf("Ext2FS: allocateInode(preferredGroup: %u, expectedSize: %u)\n", preferredGroup, expectedSize); + dbgprintf("Ext2FS: allocate_inode(preferredGroup: %u, expectedSize: %u)\n", preferredGroup, expectedSize); unsigned neededBlocks = ceilDiv(expectedSize, blockSize()); @@ -651,11 +658,11 @@ unsigned Ext2FS::allocate_inode(unsigned preferredGroup, unsigned expectedSize) } if (!groupIndex) { - kprintf("Ext2FS: allocateInode: no suitable group found for new inode with %u blocks needed :(\n", neededBlocks); + kprintf("Ext2FS: allocate_inode: no suitable group found for new inode with %u blocks needed :(\n", neededBlocks); return 0; } - dbgprintf("Ext2FS: allocateInode: found suitable group [%u] for new inode with %u blocks needed :^)\n", groupIndex, neededBlocks); + dbgprintf("Ext2FS: allocate_inode: found suitable group [%u] for new inode with %u blocks needed :^)\n", groupIndex, neededBlocks); unsigned firstFreeInodeInGroup = 0; traverse_inode_bitmap(groupIndex, [&firstFreeInodeInGroup] (unsigned firstInodeInBitmap, const Bitmap& bitmap) { @@ -669,7 +676,7 @@ unsigned Ext2FS::allocate_inode(unsigned preferredGroup, unsigned expectedSize) }); if (!firstFreeInodeInGroup) { - kprintf("Ext2FS: firstFreeInodeInGroup returned no inode, despite bgd claiming there are inodes :(\n"); + kprintf("Ext2FS: first_free_inode_in_group returned no inode, despite bgd claiming there are inodes :(\n"); return 0; } @@ -841,14 +848,15 @@ RetainPtr Ext2FS::create_inode(InodeIdentifier parent_id, const String& n // NOTE: This doesn't commit the inode allocation just yet! auto inode_id = allocate_inode(0, 0); if (!inode_id) { - kprintf("Ext2FS: createInode: allocate_inode failed\n"); + kprintf("Ext2FS: create_inode: allocate_inode failed\n"); error = -ENOSPC; return { }; } - auto blocks = allocate_blocks(group_index_from_inode(inode_id), ceilDiv(size, blockSize())); - if (blocks.is_empty()) { - kprintf("Ext2FS: createInode: allocate_blocks failed\n"); + auto needed_blocks = ceilDiv(size, blockSize()); + auto blocks = allocate_blocks(group_index_from_inode(inode_id), needed_blocks); + if (blocks.size() != needed_blocks) { + kprintf("Ext2FS: create_inode: allocate_blocks failed\n"); error = -ENOSPC; return { }; } diff --git a/VirtualFileSystem/VirtualFileSystem.cpp b/VirtualFileSystem/VirtualFileSystem.cpp index 957c39421cc..f6e3484d04f 100644 --- a/VirtualFileSystem/VirtualFileSystem.cpp +++ b/VirtualFileSystem/VirtualFileSystem.cpp @@ -136,12 +136,15 @@ RetainPtr VFS::open(RetainPtr&& device, int& er return FileDescriptor::create(move(device)); } -RetainPtr VFS::open(const String& path, int& error, int options, InodeIdentifier base) +RetainPtr VFS::open(const String& path, int& error, int options, mode_t mode, InodeIdentifier base) { auto inode_id = resolve_path(path, base, error, options); auto inode = get_inode(inode_id); - if (!inode) + if (!inode) { + if (options & O_CREAT) + return create(path, error, options, mode, base); return nullptr; + } auto metadata = inode->metadata(); if (metadata.isCharacterDevice()) { auto it = m_character_devices.find(encodedDevice(metadata.majorDevice, metadata.minorDevice)); @@ -154,13 +157,37 @@ RetainPtr VFS::open(const String& path, int& error, int options, return FileDescriptor::create(move(inode)); } -RetainPtr VFS::create(const String& path, InodeIdentifier base, int& error) +RetainPtr VFS::create(const String& path, int& error, int options, mode_t mode, InodeIdentifier base) { - // FIXME: Do the real thing, not just this fake thing! - (void) path; - (void) base; - m_root_inode->fs().create_inode(m_root_inode->fs().root_inode(), "empty", 0100644, 0, error); - return nullptr; + (void) options; + error = -EWHYTHO; + // FIXME: This won't work nicely across mount boundaries. + FileSystemPath p(path); + if (!p.is_valid()) { + error = -EINVAL; + return nullptr; + } + + InodeIdentifier parent_dir; + auto existing_dir = resolve_path(path, base, error, 0, &parent_dir); + if (existing_dir.is_valid()) { + error = -EEXIST; + return nullptr; + } + if (!parent_dir.is_valid()) { + error = -ENOENT; + return nullptr; + } + if (error != -ENOENT) { + return nullptr; + } + dbgprintf("VFS::create_file: '%s' in %u:%u\n", p.basename().characters(), parent_dir.fsid(), parent_dir.index()); + auto new_file = base.fs()->create_inode(base.fs()->root_inode(), p.basename(), mode, 0, error); + if (!new_file) + return nullptr; + + error = 0; + return FileDescriptor::create(move(new_file)); } bool VFS::mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error) diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 4472eb5e34a..543d9bd4d44 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -64,8 +64,8 @@ public: bool mount(RetainPtr&&, const String& path); RetainPtr open(RetainPtr&&, int& error, int options); - RetainPtr open(const String& path, int& error, int options = 0, InodeIdentifier base = InodeIdentifier()); - RetainPtr create(const String& path, InodeIdentifier base, int& error); + RetainPtr open(const String& path, int& error, int options, mode_t mode, InodeIdentifier base = InodeIdentifier()); + RetainPtr create(const String& path, int& error, int options, mode_t mode, InodeIdentifier base); bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error); bool touch(const String&path);