Kernel: Support open() with O_CREAT.

It's now possible to create zero-length files! :^)
Also hook up the new functionality in /bin/touch.
This commit is contained in:
Andreas Kling 2019-01-22 00:58:13 +01:00
parent a47f33bed3
commit f70136a324
Notes: sideshowbarker 2024-07-19 15:59:09 +09:00
9 changed files with 108 additions and 39 deletions

View File

@ -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 {

View File

@ -289,7 +289,7 @@ int Process::do_exec(const String& path, Vector<String>&& arguments, Vector<Stri
return -ENOENT;
int error;
auto descriptor = VFS::the().open(path, error, 0, m_cwd ? m_cwd->identifier() : 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())

View File

@ -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);

View File

@ -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:

View File

@ -7,6 +7,7 @@
#include <pwd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <Kernel/Syscall.h>
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);
}

View File

@ -1,6 +1,26 @@
#include <stdio.h>
#include <errno.h>
#include <utime.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
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 <path>\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;
}

View File

@ -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<DirectoryEntry>&& entries)
@ -598,27 +603,29 @@ bool Ext2FS::write_ext2_inode(unsigned inode, const ext2_inode& e2inode)
Vector<Ext2FS::BlockIndex> 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<BlockIndex> 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::BlockIndex> 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<Inode> 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 { };
}

View File

@ -136,12 +136,15 @@ RetainPtr<FileDescriptor> VFS::open(RetainPtr<CharacterDevice>&& device, int& er
return FileDescriptor::create(move(device));
}
RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options, InodeIdentifier base)
RetainPtr<FileDescriptor> 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<FileDescriptor> VFS::open(const String& path, int& error, int options,
return FileDescriptor::create(move(inode));
}
RetainPtr<FileDescriptor> VFS::create(const String& path, InodeIdentifier base, int& error)
RetainPtr<FileDescriptor> 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)

View File

@ -64,8 +64,8 @@ public:
bool mount(RetainPtr<FS>&&, const String& path);
RetainPtr<FileDescriptor> open(RetainPtr<CharacterDevice>&&, int& error, int options);
RetainPtr<FileDescriptor> open(const String& path, int& error, int options = 0, InodeIdentifier base = InodeIdentifier());
RetainPtr<FileDescriptor> create(const String& path, InodeIdentifier base, int& error);
RetainPtr<FileDescriptor> open(const String& path, int& error, int options, mode_t mode, InodeIdentifier base = InodeIdentifier());
RetainPtr<FileDescriptor> 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);