Compare commits
22 Commits
e5259d5849
...
73fbc37841
Author | SHA1 | Date | |
---|---|---|---|
73fbc37841 | |||
479016ab20 | |||
3d157b760c | |||
54a1998d42 | |||
e60b2a3d2f | |||
f052d8630d | |||
8542cf7cbf | |||
d50ea76bdc | |||
2edb0a3f3a | |||
b4a6e4d56d | |||
f22689fcf5 | |||
6bfc7483bc | |||
acfad51ac0 | |||
7efc3a6ea1 | |||
0b553cadc0 | |||
ae01a31104 | |||
795b0ca8d4 | |||
21cc7e3729 | |||
55d147841f | |||
a2c081f219 | |||
8c2348c425 | |||
25e9187826 |
@ -5,7 +5,7 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING true)
|
||||
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.2.0)
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.3.0)
|
||||
|
||||
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
||||
|
@ -279,7 +279,7 @@ static void mount_devfs()
|
||||
{
|
||||
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255);
|
||||
|
||||
if (mount("/dev", "devfs") < 0) exit(255);
|
||||
if (mount("/dev", "devfs", "devfs") < 0) exit(255);
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -20,8 +20,7 @@ begin:
|
||||
if (rc.error() == EEXIST) return {};
|
||||
if (rc.error() == ENOENT)
|
||||
{
|
||||
PathParser parser = TRY(PathParser::create(path.chars()));
|
||||
auto parent = TRY(parser.dirname());
|
||||
auto parent = TRY(PathParser::dirname(path));
|
||||
|
||||
TRY(mkdir_recursively(parent.view(), (0777 & ~s_umask) | S_IWUSR | S_IXUSR));
|
||||
|
||||
|
@ -6,15 +6,17 @@ Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView target;
|
||||
StringView fstype { "auto" };
|
||||
StringView source;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Mount a file system.");
|
||||
parser.add_system_program_info("mount"_sv);
|
||||
parser.add_positional_argument(target, "mountpoint"_sv, true);
|
||||
parser.add_positional_argument(source, "source"_sv, true);
|
||||
parser.add_value_argument(fstype, 't', "type"_sv, "the file system type to use");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (mount(target.chars(), fstype.chars()) < 0)
|
||||
if (mount(target.chars(), fstype.chars(), source.chars()) < 0)
|
||||
{
|
||||
perror("mount");
|
||||
return 1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p /tmp
|
||||
mount -t tmpfs /tmp
|
||||
mount -t tmpfs /tmp tmpfs
|
||||
chmod 1777 /tmp
|
||||
|
@ -47,6 +47,7 @@ set(SOURCES
|
||||
src/fs/GPT.cpp
|
||||
src/fs/tmpfs/FileSystem.cpp
|
||||
src/fs/tmpfs/Inode.cpp
|
||||
src/fs/ext2/FileSystem.cpp
|
||||
src/fs/devices/DeviceRegistry.cpp
|
||||
src/fs/devices/NullDevice.cpp
|
||||
src/fs/devices/ZeroDevice.cpp
|
||||
|
@ -80,33 +80,15 @@ void decode_page_fault_error_code(u64 code)
|
||||
|
||||
decode_page_fault_error_code(regs->error);
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
||||
kerrorln("Current task %zu was terminated because of a page fault", Scheduler::current()->id);
|
||||
if (Scheduler::current()->is_kernel) Scheduler::current()->state = ThreadState::Dying;
|
||||
else
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
auto* parent = current->parent;
|
||||
if (parent && parent->state == ThreadState::Waiting)
|
||||
{
|
||||
auto child = *parent->child_being_waited_for;
|
||||
if (child == -1 || child == (pid_t)current->id)
|
||||
{
|
||||
parent->child_being_waited_for = (pid_t)current->id;
|
||||
parent->wake_up();
|
||||
}
|
||||
}
|
||||
current->state = ThreadState::Exited;
|
||||
}
|
||||
Scheduler::current()->status = 127;
|
||||
kernel_yield();
|
||||
unreachable();
|
||||
Scheduler::current()->exit_and_signal_parent(127);
|
||||
}
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
CPU::efficient_halt();
|
||||
}
|
||||
|
||||
@ -118,6 +100,13 @@ void decode_page_fault_error_code(u64 code)
|
||||
|
||||
CPU::print_stack_trace_at(regs);
|
||||
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
||||
kerrorln("Current task %zu was terminated because of a general protection fault", Scheduler::current()->id);
|
||||
Scheduler::current()->exit_and_signal_parent(127);
|
||||
}
|
||||
|
||||
CPU::efficient_halt();
|
||||
}
|
||||
|
||||
@ -302,14 +291,14 @@ namespace CPU
|
||||
asm volatile("hlt");
|
||||
}
|
||||
|
||||
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the
|
||||
// "hlt" instruction, which puts the CPU into a low-power idle state until the
|
||||
// next interrupt arrives... and we disable interrupts beforehand.
|
||||
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using
|
||||
// the "hlt" instruction, which puts the CPU into a low-power idle state
|
||||
// until the next interrupt arrives... and we disable interrupts beforehand.
|
||||
{
|
||||
asm volatile("cli"); // Disable interrupts
|
||||
loop:
|
||||
asm volatile("hlt"); // Let the cpu rest and pause until the next interrupt arrives... which in this case should
|
||||
// be never (unless an NMI arrives) :)
|
||||
asm volatile("hlt"); // Let the cpu rest and pause until the next interrupt arrives... which in this case
|
||||
// should be never (unless an NMI arrives) :)
|
||||
goto loop; // Safeguard: if we ever wake up, start our low-power rest again
|
||||
}
|
||||
|
||||
|
@ -71,14 +71,13 @@ namespace VFS
|
||||
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parser = TRY(PathParser::create(path));
|
||||
auto parent_path = TRY(parser.dirname());
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||
|
||||
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(parser.basename());
|
||||
auto child_name = TRY(PathParser::basename(path));
|
||||
|
||||
TRY(validate_filename(child_name.view()));
|
||||
|
||||
@ -87,14 +86,13 @@ namespace VFS
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parser = TRY(PathParser::create(path));
|
||||
auto parent_path = TRY(parser.dirname());
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||
|
||||
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(parser.basename());
|
||||
auto child_name = TRY(PathParser::basename(path));
|
||||
|
||||
TRY(validate_filename(child_name.view()));
|
||||
|
||||
@ -189,9 +187,8 @@ namespace VFS
|
||||
|
||||
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory)
|
||||
{
|
||||
auto root_parser = TRY(PathParser::create(new_root));
|
||||
auto new_root_parent = TRY(root_parser.dirname());
|
||||
auto new_root_path = TRY(root_parser.basename());
|
||||
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
||||
auto new_root_path = TRY(PathParser::basename(new_root));
|
||||
|
||||
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory));
|
||||
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
||||
@ -200,9 +197,8 @@ namespace VFS
|
||||
if (!new_root_inode->is_mountpoint()) return err(EINVAL);
|
||||
if (new_root_inode->fs() == g_root_inode->fs()) return err(EBUSY);
|
||||
|
||||
auto parser = TRY(PathParser::create(put_old));
|
||||
auto parent_path = TRY(parser.dirname());
|
||||
auto child = TRY(parser.basename());
|
||||
auto parent_path = TRY(PathParser::dirname(put_old));
|
||||
auto child = TRY(PathParser::basename(put_old));
|
||||
|
||||
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
||||
|
||||
@ -228,9 +224,8 @@ namespace VFS
|
||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||
SharedPtr<VFS::Inode> working_directory)
|
||||
{
|
||||
auto parser = TRY(PathParser::create(path));
|
||||
auto parent_path = TRY(parser.dirname());
|
||||
auto child = TRY(parser.basename());
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
auto child = TRY(PathParser::basename(path));
|
||||
|
||||
kinfoln("vfs: Mounting filesystem on target %s", path);
|
||||
|
||||
@ -249,9 +244,8 @@ namespace VFS
|
||||
|
||||
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
|
||||
{
|
||||
auto parser = TRY(PathParser::create(path));
|
||||
auto parent_path = TRY(parser.dirname());
|
||||
auto child = TRY(parser.basename());
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
auto child = TRY(PathParser::basename(path));
|
||||
|
||||
if (child.view() == "/") return err(EBUSY);
|
||||
|
||||
|
28
kernel/src/fs/ext2/FileSystem.cpp
Normal file
28
kernel/src/fs/ext2/FileSystem.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "fs/ext2/FileSystem.h"
|
||||
|
||||
namespace Ext2
|
||||
{
|
||||
FileSystem::FileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> FileSystem::find_inode_by_number(ino_t inode)
|
||||
{
|
||||
auto maybe_inode = m_inode_cache.try_get(inode);
|
||||
if (maybe_inode.has_value()) return maybe_inode.value();
|
||||
|
||||
// TODO: Locate the inode's block group descriptor and find it in the block group's inode table.
|
||||
return err(ENOENT);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::FileSystem>> FileSystem::create(SharedPtr<Device> host_device)
|
||||
{
|
||||
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
|
||||
const usize nread = TRY(host_device->read((u8*)&fs->m_superblock, 1024, 1024));
|
||||
if (nread != 1024) return err(EINVAL); // Source had an invalid superblock.
|
||||
if (fs->m_superblock.signature != EXT2_MAGIC) return err(EINVAL); // Source had an invalid superblock.
|
||||
|
||||
// TODO: Implement basic Ext2 reading, enough to be able to mount a volume.
|
||||
return err(ENOTSUP);
|
||||
}
|
||||
}
|
101
kernel/src/fs/ext2/FileSystem.h
Normal file
101
kernel/src/fs/ext2/FileSystem.h
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "fs/VFS.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include <luna/HashMap.h>
|
||||
|
||||
#define EXT2_MAGIC 0xef53
|
||||
|
||||
namespace Ext2
|
||||
{
|
||||
struct [[gnu::packed]] Superblock
|
||||
{
|
||||
u32 nr_inodes;
|
||||
u32 nr_blocks;
|
||||
u32 nr_reserved;
|
||||
u32 nr_free_inodes;
|
||||
u32 nr_free_blocks;
|
||||
u32 superblock_block;
|
||||
u32 log_block_size;
|
||||
u32 log_fragment_size;
|
||||
u32 blocks_per_block_group;
|
||||
u32 fragments_per_block_group;
|
||||
u32 inodes_per_block_group;
|
||||
u32 last_mount_time;
|
||||
u32 last_write_time;
|
||||
u16 mounts_since_last_fsck;
|
||||
u16 mounts_allowed_before_fsck;
|
||||
u16 signature;
|
||||
u16 fs_state;
|
||||
u16 error_action;
|
||||
u16 minor_version;
|
||||
u32 last_fsck_time;
|
||||
u32 fsck_time_interval;
|
||||
u32 os_id;
|
||||
u32 major_version;
|
||||
u16 reserved_block_uid;
|
||||
u16 reserved_block_gid;
|
||||
// TODO: Add extended superblock fields.
|
||||
u8 padding[1024 - 84];
|
||||
};
|
||||
|
||||
static_assert(sizeof(Superblock) == 1024);
|
||||
|
||||
class FileSystem : public VFS::FileSystem
|
||||
{
|
||||
public:
|
||||
SharedPtr<VFS::Inode> root_inode() const override
|
||||
{
|
||||
return m_root_inode;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_file_inode() override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView) override
|
||||
{
|
||||
return err(EROFS);
|
||||
}
|
||||
|
||||
Result<void> set_mount_dir(SharedPtr<VFS::Inode>) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<SharedPtr<VFS::FileSystem>> create(SharedPtr<Device> host_device);
|
||||
|
||||
dev_t host_device_id() const override
|
||||
{
|
||||
return m_host_device_id;
|
||||
}
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> find_inode_by_number(ino_t inode);
|
||||
|
||||
virtual ~FileSystem() = default;
|
||||
|
||||
private:
|
||||
FileSystem();
|
||||
|
||||
SharedPtr<VFS::Inode> m_root_inode;
|
||||
|
||||
SharedPtr<Device> m_host_device;
|
||||
dev_t m_host_device_id;
|
||||
|
||||
Superblock m_superblock;
|
||||
|
||||
// FIXME: This inode cache will keep all inodes in it alive despite having no other references to it, but we're
|
||||
// not worrying about that as for now the filesystem implementation is read-only.
|
||||
HashMap<ino_t, SharedPtr<VFS::Inode>> m_inode_cache;
|
||||
};
|
||||
}
|
@ -91,7 +91,7 @@ Result<u64> UserVM::alloc_region(usize count, bool persistent)
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||
Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persistent)
|
||||
{
|
||||
if (address >= VM_END) return err(EINVAL);
|
||||
|
||||
@ -112,6 +112,7 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||
if (region->start >= address && region->end <= end)
|
||||
{
|
||||
region->used = used;
|
||||
region->persistent = persistent;
|
||||
if (region->start == address && region->end == end)
|
||||
{
|
||||
try_merge_region_with_neighbors(region);
|
||||
@ -125,6 +126,7 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||
auto* middle_region = TRY(split_region(region, address));
|
||||
TRY(split_region(middle_region, end));
|
||||
middle_region->used = used;
|
||||
middle_region->persistent = persistent;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -133,6 +135,7 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||
bool finished = region->end == end;
|
||||
auto* split = TRY(split_region(region, address));
|
||||
split->used = used;
|
||||
split->persistent = persistent;
|
||||
try_merge_region_with_neighbors(split);
|
||||
if (!finished) continue;
|
||||
return true;
|
||||
@ -142,6 +145,7 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||
{
|
||||
TRY(split_region(region, end));
|
||||
region->used = used;
|
||||
region->persistent = persistent;
|
||||
try_merge_region_with_neighbors(region);
|
||||
return true;
|
||||
}
|
||||
|
@ -21,14 +21,14 @@ class UserVM
|
||||
|
||||
Result<u64> alloc_region(usize count, bool persistent = false);
|
||||
|
||||
Result<bool> test_and_alloc_region(u64 address, usize count)
|
||||
Result<bool> test_and_alloc_region(u64 address, usize count, bool persistent = false)
|
||||
{
|
||||
return set_region(address, count, true);
|
||||
return set_region(address, count, true, persistent);
|
||||
}
|
||||
|
||||
Result<bool> free_region(u64 address, usize count)
|
||||
{
|
||||
return set_region(address, count, false);
|
||||
return set_region(address, count, false, false);
|
||||
}
|
||||
|
||||
static Result<OwnedPtr<UserVM>> try_create();
|
||||
@ -36,7 +36,7 @@ class UserVM
|
||||
Result<OwnedPtr<UserVM>> clone();
|
||||
|
||||
private:
|
||||
Result<bool> set_region(u64 address, usize count, bool used);
|
||||
Result<bool> set_region(u64 address, usize count, bool used, bool persistent);
|
||||
Result<void> create_default_region();
|
||||
Result<void> create_null_region();
|
||||
void try_merge_region_with_neighbors(VMRegion* region);
|
||||
|
@ -7,25 +7,5 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
|
||||
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
Scheduler::for_each_child(current, [](Thread* child) {
|
||||
child->parent = Scheduler::init_thread();
|
||||
return true;
|
||||
});
|
||||
|
||||
auto* parent = current->parent;
|
||||
if (parent && parent->state == ThreadState::Waiting)
|
||||
{
|
||||
auto child = *parent->child_being_waited_for;
|
||||
if (child == -1 || child == (pid_t)current->id)
|
||||
{
|
||||
parent->child_being_waited_for = (pid_t)current->id;
|
||||
parent->wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
current->status = status;
|
||||
current->state = ThreadState::Exited;
|
||||
|
||||
kernel_yield();
|
||||
unreachable();
|
||||
current->exit_and_signal_parent(status);
|
||||
}
|
||||
|
@ -13,10 +13,8 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
||||
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
PathParser parser = TRY(PathParser::create(path.chars()));
|
||||
|
||||
auto dirname = TRY(parser.dirname());
|
||||
auto basename = TRY(parser.basename());
|
||||
auto dirname = TRY(PathParser::dirname(path.view()));
|
||||
auto basename = TRY(PathParser::basename(path.view()));
|
||||
|
||||
if (basename.view() == ".") return err(EINVAL);
|
||||
|
||||
@ -47,14 +45,13 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto parser = TRY(PathParser::create(linkpath.chars()));
|
||||
auto parent = TRY(parser.dirname());
|
||||
auto parent = TRY(PathParser::dirname(linkpath.view()));
|
||||
|
||||
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
||||
|
||||
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(parser.basename());
|
||||
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
||||
|
||||
TRY(VFS::validate_filename(child_name.view()));
|
||||
|
||||
@ -101,8 +98,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto parser = TRY(PathParser::create(newpath.chars()));
|
||||
auto parent = TRY(parser.dirname());
|
||||
auto parent = TRY(PathParser::dirname(newpath.view()));
|
||||
|
||||
// FIXME: Use AT_SYMLINK_FOLLOW.
|
||||
auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false));
|
||||
@ -115,7 +111,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
||||
|
||||
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(parser.basename());
|
||||
auto child_name = TRY(PathParser::basename(newpath.view()));
|
||||
|
||||
TRY(VFS::validate_filename(child_name.view()));
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "fs/VFS.h"
|
||||
#include "fs/ext2/FileSystem.h"
|
||||
#include "fs/tmpfs/FileSystem.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
@ -8,15 +9,28 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
|
||||
{
|
||||
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||
auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||
auto source = TRY(MemoryManager::strdup_from_user(args[2]));
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
if (current->auth.euid != 0) return err(EPERM);
|
||||
|
||||
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
|
||||
auto inode = TRY(VFS::resolve_path(source.chars(), current->auth, current->current_directory));
|
||||
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
|
||||
dev_t device_id = inode->device_id();
|
||||
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
|
||||
};
|
||||
|
||||
SharedPtr<VFS::FileSystem> fs;
|
||||
|
||||
if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create());
|
||||
else if (fstype.view() == "devfs")
|
||||
fs = TRY(DeviceRegistry::create_devfs_instance());
|
||||
else if (fstype.view() == "ext2")
|
||||
{
|
||||
auto source_device = TRY(get_source());
|
||||
fs = TRY(Ext2::FileSystem::create(source_device));
|
||||
}
|
||||
else
|
||||
return err(ENODEV);
|
||||
|
||||
|
@ -102,9 +102,8 @@ namespace ELFLoader
|
||||
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
|
||||
if (can_execute_segment(program_header.p_flags)) flags &= ~MMU::NoExecute;
|
||||
|
||||
// FIXME: Set this memory range to persistent so that munmap() cannot remove it.
|
||||
if (!TRY(vm->test_and_alloc_region(
|
||||
base_vaddr, get_blocks_from_size(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE))))
|
||||
base_vaddr, get_blocks_from_size(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE), true)))
|
||||
return err(ENOMEM);
|
||||
|
||||
// Allocate physical memory for the segment
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "thread/Thread.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <bits/atfile.h>
|
||||
#include <bits/open-flags.h>
|
||||
#include <luna/Alloc.h>
|
||||
@ -68,6 +69,33 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
|
||||
}
|
||||
|
||||
[[noreturn]] void Thread::exit_and_signal_parent(u8 _status)
|
||||
{
|
||||
if (is_kernel) state = ThreadState::Dying;
|
||||
else
|
||||
{
|
||||
Scheduler::for_each_child(this, [](Thread* child) {
|
||||
child->parent = Scheduler::init_thread();
|
||||
return true;
|
||||
});
|
||||
|
||||
if (parent && parent->state == ThreadState::Waiting)
|
||||
{
|
||||
auto child = *parent->child_being_waited_for;
|
||||
if (child == -1 || child == (pid_t)id)
|
||||
{
|
||||
parent->child_being_waited_for = (pid_t)id;
|
||||
parent->wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
state = ThreadState::Exited;
|
||||
}
|
||||
status = _status;
|
||||
kernel_yield();
|
||||
unreachable();
|
||||
}
|
||||
|
||||
bool FileDescriptor::should_append()
|
||||
{
|
||||
return flags & O_APPEND;
|
||||
|
@ -99,6 +99,8 @@ struct Thread : public LinkedListNode<Thread>
|
||||
|
||||
PageDirectory* directory;
|
||||
|
||||
[[noreturn]] void exit_and_signal_parent(u8 status);
|
||||
|
||||
bool is_idle()
|
||||
{
|
||||
return state == ThreadState::Idle;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "thread/ThreadImage.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/CString.h>
|
||||
|
||||
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
|
||||
@ -10,8 +11,7 @@ static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack, UserVM
|
||||
{
|
||||
const u64 THREAD_STACK_BASE = 0x10000;
|
||||
|
||||
// FIXME: Set this memory range to persistent so that munmap() cannot remove it.
|
||||
if (!TRY(vm->test_and_alloc_region(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES))) return err(ENOMEM);
|
||||
if (!TRY(vm->test_and_alloc_region(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES, true))) return err(ENOMEM);
|
||||
|
||||
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
|
||||
MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
||||
@ -122,7 +122,7 @@ void ThreadImage::apply(Thread* thread)
|
||||
|
||||
thread->kernel_stack = m_kernel_stack;
|
||||
thread->stack = m_user_stack;
|
||||
thread->set_sp(m_sp);
|
||||
thread->set_sp(align_down<16>(m_sp));
|
||||
|
||||
thread->directory = m_directory;
|
||||
|
||||
|
@ -20,6 +20,7 @@ set(SOURCES
|
||||
src/pwd.cpp
|
||||
src/grp.cpp
|
||||
src/locale.cpp
|
||||
src/scanf.cpp
|
||||
src/sys/stat.cpp
|
||||
src/sys/mman.cpp
|
||||
src/sys/wait.cpp
|
||||
|
@ -27,6 +27,9 @@ extern FILE* stderr;
|
||||
#define stderr stderr
|
||||
|
||||
#define BUFSIZ 1024
|
||||
#define FILENAME_MAX \
|
||||
1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file
|
||||
// name.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -41,6 +44,9 @@ extern "C"
|
||||
/* Bind a stream to a file descriptor. */
|
||||
FILE* fdopen(int fd, const char* mode);
|
||||
|
||||
/* Change the underlying file and mode of a stream. */
|
||||
FILE* freopen(const char* path, const char* mode, FILE* stream);
|
||||
|
||||
/* Close a file and frees up its stream. */
|
||||
int fclose(FILE* stream);
|
||||
|
||||
@ -95,6 +101,9 @@ extern "C"
|
||||
/* Read a character from standard input. */
|
||||
int getchar(void);
|
||||
|
||||
/* Push a character back to stream so that it can be read again. */
|
||||
int ungetc(int c, FILE* stream);
|
||||
|
||||
/* Read a line from stream. */
|
||||
char* fgets(char* buf, size_t size, FILE* stream);
|
||||
|
||||
@ -122,16 +131,34 @@ extern "C"
|
||||
int snprintf(char* buf, size_t max, const char* format, ...);
|
||||
|
||||
/* Write formatted output into a buffer. */
|
||||
int vsprintf(char*, const char*, va_list);
|
||||
int vsprintf(char* buf, const char* format, va_list ap);
|
||||
|
||||
/* Write up to max bytes of formatted output into a buffer. */
|
||||
int vsnprintf(char*, size_t, const char*, va_list);
|
||||
int vsnprintf(char* buf, size_t max, const char* format, va_list ap);
|
||||
|
||||
/* Write formatted output to standard output. */
|
||||
int vprintf(const char*, va_list ap);
|
||||
int vprintf(const char* format, va_list ap);
|
||||
|
||||
/* Write formatted output to standard output. */
|
||||
int printf(const char*, ...);
|
||||
int printf(const char* format, ...);
|
||||
|
||||
/* Scan formatted input from a string. */
|
||||
int vsscanf(const char* str, const char* format, va_list ap);
|
||||
|
||||
/* Scan formatted input from a string. */
|
||||
int sscanf(const char* str, const char* format, ...);
|
||||
|
||||
/* Scan formatted input from a file. */
|
||||
int vfscanf(FILE* stream, const char* format, va_list ap);
|
||||
|
||||
/* Scan formatted input from a file. */
|
||||
int fscanf(FILE* stream, const char* format, ...);
|
||||
|
||||
/* Scan formatted input from standard input. */
|
||||
int vscanf(const char* format, va_list ap);
|
||||
|
||||
/* Scan formatted input from standard input. */
|
||||
int scanf(const char* format, ...);
|
||||
|
||||
/* Write a string followed by a newline to standard output. */
|
||||
int puts(const char* s);
|
||||
|
@ -9,7 +9,7 @@ extern "C"
|
||||
#endif
|
||||
|
||||
/* Mount a file system on target. */
|
||||
int mount(const char* target, const char* fstype);
|
||||
int mount(const char* target, const char* fstype, const char* source);
|
||||
|
||||
/* Unmount the file system mounted on target. */
|
||||
int umount(const char* target);
|
||||
|
263
libc/src/scanf.cpp
Normal file
263
libc/src/scanf.cpp
Normal file
@ -0,0 +1,263 @@
|
||||
#include <errno.h>
|
||||
#include <luna/CType.h>
|
||||
#include <luna/NumberParsing.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FLAG_DISCARD (1 << 0)
|
||||
#define FLAG_ALLOC (1 << 1)
|
||||
#define FLAG_WIDTH (1 << 2)
|
||||
#define FLAG_LONG (1 << 3)
|
||||
#define FLAG_LONG_LONG (1 << 4)
|
||||
#define FLAG_SHORT (1 << 5)
|
||||
#define FLAG_CHAR (1 << 6)
|
||||
|
||||
static int parse_flags(const char** format)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (**format)
|
||||
{
|
||||
case '*':
|
||||
result |= FLAG_DISCARD;
|
||||
(*format)++;
|
||||
break;
|
||||
case 'm':
|
||||
result |= FLAG_ALLOC;
|
||||
(*format)++;
|
||||
break;
|
||||
default: return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t parse_width(const char** format, int& flags)
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
if (_isdigit(**format))
|
||||
{
|
||||
result = scan_unsigned_integer(format);
|
||||
flags |= FLAG_WIDTH;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void parse_type(const char** format, int& flags)
|
||||
{
|
||||
// FIXME: Support %j (intmax_t/uintmax_t)
|
||||
switch (**format)
|
||||
{
|
||||
case 'h':
|
||||
flags |= FLAG_SHORT;
|
||||
(*format)++;
|
||||
if (**format == 'h')
|
||||
{
|
||||
flags |= FLAG_CHAR;
|
||||
(*format)++;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
flags |= FLAG_LONG;
|
||||
(*format)++;
|
||||
if (**format == 'l')
|
||||
{
|
||||
flags |= FLAG_LONG_LONG;
|
||||
(*format)++;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
flags |= (sizeof(ptrdiff_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||
(*format)++;
|
||||
break;
|
||||
case 'z':
|
||||
flags |= (sizeof(size_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||
(*format)++;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_parsed_signed_integer(ssize_t value, int flags, va_list ap)
|
||||
{
|
||||
if (flags & FLAG_LONG_LONG) *va_arg(ap, signed long long*) = (signed long long)value;
|
||||
else if (flags & FLAG_LONG)
|
||||
*va_arg(ap, signed long*) = (signed long)value;
|
||||
else if (flags & FLAG_SHORT)
|
||||
*va_arg(ap, signed int*) = (signed short)value;
|
||||
else if (flags & FLAG_CHAR)
|
||||
*va_arg(ap, signed int*) = (signed char)value;
|
||||
else
|
||||
*va_arg(ap, signed int*) = (signed int)value;
|
||||
}
|
||||
|
||||
static void write_parsed_unsigned_integer(size_t value, int flags, va_list ap)
|
||||
{
|
||||
if (flags & FLAG_LONG_LONG) *va_arg(ap, unsigned long long*) = (unsigned long long)value;
|
||||
else if (flags & FLAG_LONG)
|
||||
*va_arg(ap, unsigned long*) = (unsigned long)value;
|
||||
else if (flags & FLAG_SHORT)
|
||||
*va_arg(ap, unsigned int*) = (unsigned short)value;
|
||||
else if (flags & FLAG_CHAR)
|
||||
*va_arg(ap, unsigned int*) = (unsigned char)value;
|
||||
else
|
||||
*va_arg(ap, unsigned int*) = (unsigned int)value;
|
||||
}
|
||||
|
||||
#define WHITESPACE_CHARACTERS " \t\f\r\n\v"
|
||||
|
||||
static void skip_whitespace(const char** str)
|
||||
{
|
||||
*str += strspn(*str, WHITESPACE_CHARACTERS);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int vsscanf(const char* str, const char* format, va_list ap)
|
||||
{
|
||||
int parsed = 0;
|
||||
const char* s = str; // Keep a pointer to the beginning of the string for %n
|
||||
|
||||
if (*str == 0) return EOF;
|
||||
|
||||
while (*format)
|
||||
{
|
||||
if (*format != '%')
|
||||
{
|
||||
normal:
|
||||
if (!_isspace(*format))
|
||||
{
|
||||
if (*str != *format) return parsed;
|
||||
str++;
|
||||
format++;
|
||||
if (*str == 0) return parsed;
|
||||
continue;
|
||||
}
|
||||
|
||||
skip_whitespace(&format);
|
||||
skip_whitespace(&str);
|
||||
if (*str == 0) return parsed;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
format++;
|
||||
if (*format == '%')
|
||||
{
|
||||
skip_whitespace(&str);
|
||||
goto normal;
|
||||
}
|
||||
|
||||
int flags = parse_flags(&format);
|
||||
size_t width = parse_width(&format, flags);
|
||||
parse_type(&format, flags);
|
||||
char specifier = *format++;
|
||||
if (!specifier) return parsed;
|
||||
|
||||
switch (specifier)
|
||||
{
|
||||
case 's': {
|
||||
skip_whitespace(&str);
|
||||
size_t chars = strcspn(str, WHITESPACE_CHARACTERS);
|
||||
if (!chars) return parsed;
|
||||
if ((flags & FLAG_WIDTH) && chars > width) chars = width;
|
||||
if (!(flags & FLAG_DISCARD))
|
||||
{
|
||||
char* ptr;
|
||||
if (flags & FLAG_ALLOC)
|
||||
{
|
||||
ptr = (char*)malloc(chars + 1);
|
||||
if (!ptr) return parsed;
|
||||
*va_arg(ap, char**) = ptr;
|
||||
}
|
||||
else
|
||||
ptr = va_arg(ap, char*);
|
||||
memcpy(ptr, str, chars);
|
||||
ptr[chars] = 0;
|
||||
}
|
||||
str += chars;
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
if (strlen(str) < width) return parsed;
|
||||
if (!(flags & FLAG_WIDTH)) width = 1;
|
||||
if (!(flags & FLAG_DISCARD))
|
||||
{
|
||||
char* ptr;
|
||||
if (flags & FLAG_ALLOC)
|
||||
{
|
||||
ptr = (char*)malloc(width);
|
||||
if (!ptr) return parsed;
|
||||
*va_arg(ap, char**) = ptr;
|
||||
}
|
||||
else
|
||||
ptr = va_arg(ap, char*);
|
||||
memcpy(ptr, str, width);
|
||||
}
|
||||
str += width;
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
skip_whitespace(&str);
|
||||
ssize_t value = scan_signed_integer(&str, 10);
|
||||
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
skip_whitespace(&str);
|
||||
ssize_t value = scan_signed_integer(&str, 0);
|
||||
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'o': {
|
||||
skip_whitespace(&str);
|
||||
size_t value = scan_unsigned_integer(&str, 8);
|
||||
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
skip_whitespace(&str);
|
||||
size_t value = scan_unsigned_integer(&str, 10);
|
||||
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'X':
|
||||
case 'x': {
|
||||
skip_whitespace(&str);
|
||||
size_t value = scan_unsigned_integer(&str, 16);
|
||||
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
skip_whitespace(&str);
|
||||
size_t value = scan_unsigned_integer(&str, 16);
|
||||
if (!(flags & FLAG_DISCARD)) *va_arg(ap, void**) = (void*)value;
|
||||
parsed++;
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
if (!(flags & FLAG_DISCARD)) *va_arg(ap, int*) = (int)(str - s);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "vsscanf: unknown conversion specifier: %%%c\n", specifier);
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
@ -11,6 +11,13 @@ FILE* stdin = nullptr;
|
||||
FILE* stderr = nullptr;
|
||||
FILE* stdout = nullptr;
|
||||
|
||||
static const char* read_tmpdir()
|
||||
{
|
||||
const char* tmpdir = getenv("TMPDIR");
|
||||
if (!tmpdir) return "/tmp";
|
||||
return tmpdir;
|
||||
}
|
||||
|
||||
static int fopen_parse_mode(const char* mode)
|
||||
{
|
||||
int result = 0;
|
||||
@ -46,6 +53,12 @@ static int fdopen_check_compatible_mode(int fd, int new_flags)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int fflush(FILE*)
|
||||
{
|
||||
// FIXME: Files are not buffered right now.
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* fopen(const char* path, const char* mode)
|
||||
{
|
||||
int flags;
|
||||
@ -85,6 +98,25 @@ extern "C"
|
||||
return f;
|
||||
}
|
||||
|
||||
FILE* freopen(const char* path, const char* mode, FILE* stream)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
||||
|
||||
close(stream->_fd);
|
||||
|
||||
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
||||
|
||||
int fd = open(path, flags, 0666);
|
||||
if (fd < 0) { return nullptr; }
|
||||
|
||||
stream->_fd = fd;
|
||||
clearerr(stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
int fclose(FILE* stream)
|
||||
{
|
||||
if (close(stream->_fd) < 0) return EOF;
|
||||
@ -381,6 +413,54 @@ extern "C"
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sscanf(const char* str, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
int rc = vsscanf(str, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int vfscanf(FILE* stream, const char* format, va_list ap)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
if (!fgets(buf, sizeof(buf), stream)) return EOF;
|
||||
return vsscanf(buf, format, ap);
|
||||
}
|
||||
|
||||
int fscanf(FILE* stream, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
int rc = vfscanf(stream, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int vscanf(const char* format, va_list ap)
|
||||
{
|
||||
return vfscanf(stdin, format, ap);
|
||||
}
|
||||
|
||||
int scanf(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
int rc = vfscanf(stdin, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int puts(const char* s)
|
||||
{
|
||||
if (fputs(s, stdout) < 0) return -1;
|
||||
@ -404,12 +484,16 @@ extern "C"
|
||||
|
||||
FILE* tmpfile()
|
||||
{
|
||||
// FIXME: use /tmp as the directory when the tmpfs is mounted only there.
|
||||
int fd = open("/", O_RDWR | O_TMPFILE, 0600);
|
||||
int fd = open(read_tmpdir(), O_RDWR | O_TMPFILE, 0600);
|
||||
if (fd < 0) return nullptr;
|
||||
|
||||
FILE* f = fdopen(fd, "w+b");
|
||||
if (!f) close(fd);
|
||||
return f;
|
||||
}
|
||||
|
||||
int ungetc(int, FILE*)
|
||||
{
|
||||
fail("FIXME: ungetc: not implemented");
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int mount(const char* target, const char* fstype)
|
||||
int mount(const char* target, const char* fstype, const char* source)
|
||||
{
|
||||
long rc = syscall(SYS_mount, target, fstype);
|
||||
long rc = syscall(SYS_mount, target, fstype, source);
|
||||
__errno_return(rc, int);
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
#include <luna/Types.h>
|
||||
|
||||
// Parse an unsigned integer and advance *str to point to the first non-digit character after the number.
|
||||
usize scan_unsigned_integer(const char** str);
|
||||
usize scan_unsigned_integer(const char** str, int base = 10);
|
||||
|
||||
// Parse a signed integer and advance *str to point to the first non-digit character after the number.
|
||||
isize scan_signed_integer(const char** str);
|
||||
isize scan_signed_integer(const char** str, int base = 10);
|
||||
|
||||
// Parse an unsigned integer, similar to strtoull().
|
||||
usize parse_unsigned_integer(const char* str, const char** endptr, int base);
|
||||
|
@ -37,8 +37,8 @@ class PathParser
|
||||
return m_already_called_next ? (bool)m_strtok_saved_state : is_not_delim(*m_copy);
|
||||
}
|
||||
|
||||
Result<String> basename();
|
||||
Result<String> dirname();
|
||||
static Result<String> basename(StringView path);
|
||||
static Result<String> dirname(StringView path);
|
||||
|
||||
Option<const char*> next();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <luna/Alloc.h>
|
||||
#include <luna/Atomic.h>
|
||||
#include <luna/Hash.h>
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <luna/Result.h>
|
||||
#include <luna/ScopeGuard.h>
|
||||
@ -84,6 +85,11 @@ template <typename T> class SharedPtr
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const SharedPtr<T>& other)
|
||||
{
|
||||
return m_ptr == other.m_ptr && m_ref_count == other.m_ref_count;
|
||||
}
|
||||
|
||||
T* ptr() const
|
||||
{
|
||||
return m_ptr;
|
||||
|
@ -212,7 +212,7 @@ template <typename T> class Vector
|
||||
{
|
||||
Vector<T> other;
|
||||
TRY(other.try_reserve(m_capacity));
|
||||
memcpy(other.m_data, m_data, m_size);
|
||||
memcpy(other.m_data, m_data, m_size * sizeof(T));
|
||||
other.m_size = m_size;
|
||||
return other;
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ static usize parse_precision(const char** format, flags_t& flags, va_list ap)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void parse_length(const char** format, flags_t& flags)
|
||||
static void parse_type(const char** format, flags_t& flags)
|
||||
{
|
||||
// FIXME: Support %j (intmax_t/uintmax_t)
|
||||
switch (**format)
|
||||
@ -415,7 +415,7 @@ Result<usize> cstyle_format(const char* format, callback_t callback, void* arg,
|
||||
flags_t flags = parse_flags(&format);
|
||||
const usize width = parse_width(&format, flags, ap);
|
||||
usize precision = parse_precision(&format, flags, ap);
|
||||
parse_length(&format, flags);
|
||||
parse_type(&format, flags);
|
||||
|
||||
conv_state vstate = { flags, width, precision };
|
||||
|
||||
@ -503,15 +503,18 @@ usize vstring_format(char* buf, usize max, const char* format, va_list ap)
|
||||
[](char c, void* arg) -> Result<void> {
|
||||
StringFormatInfo* info_arg = (StringFormatInfo*)arg;
|
||||
if (!info_arg->remaining) return {};
|
||||
*(info_arg->buffer) = c;
|
||||
info_arg->buffer++;
|
||||
if (info_arg->buffer)
|
||||
{
|
||||
*(info_arg->buffer) = c;
|
||||
info_arg->buffer++;
|
||||
}
|
||||
info_arg->remaining--;
|
||||
return {};
|
||||
},
|
||||
&info, ap)
|
||||
.value();
|
||||
|
||||
*(info.buffer) = 0;
|
||||
if (info.buffer) *(info.buffer) = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -89,12 +89,12 @@ isize parse_signed_integer(const char* str, const char** endptr, int base)
|
||||
return negative ? -(isize)rc : (isize)rc;
|
||||
}
|
||||
|
||||
usize scan_unsigned_integer(const char** str)
|
||||
usize scan_unsigned_integer(const char** str, int base)
|
||||
{
|
||||
return parse_unsigned_integer(*str, str, 10);
|
||||
return parse_unsigned_integer(*str, str, base);
|
||||
}
|
||||
|
||||
isize scan_signed_integer(const char** str)
|
||||
isize scan_signed_integer(const char** str, int base)
|
||||
{
|
||||
return parse_signed_integer(*str, str, 10);
|
||||
return parse_signed_integer(*str, str, base);
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ Option<const char*> PathParser::next()
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<String> PathParser::basename()
|
||||
Result<String> PathParser::basename(StringView path)
|
||||
{
|
||||
char* copy = strdup(m_original);
|
||||
char* copy = strdup(path.chars());
|
||||
if (!copy) return err(ENOMEM);
|
||||
|
||||
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
||||
@ -48,9 +48,9 @@ Result<String> PathParser::basename()
|
||||
return String::from_cstring(result);
|
||||
}
|
||||
|
||||
Result<String> PathParser::dirname()
|
||||
Result<String> PathParser::dirname(StringView path)
|
||||
{
|
||||
char* copy = strdup(m_original);
|
||||
char* copy = strdup(path.chars());
|
||||
if (!copy) return err(ENOMEM);
|
||||
|
||||
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
||||
|
@ -6,5 +6,5 @@ Stack::Stack(u64 base, usize bytes) : m_base(base), m_bytes(bytes)
|
||||
|
||||
u64 Stack::top() const
|
||||
{
|
||||
return (m_base + m_bytes) - sizeof(void*);
|
||||
return (m_base + m_bytes) - 16;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ namespace os
|
||||
|
||||
bool is_still_parsing_flags = true;
|
||||
|
||||
Vector<PositionalArgument> positional_args = TRY(m_positional_args.deep_copy());
|
||||
Vector<PositionalArgument> positional_args = TRY(m_positional_args.shallow_copy());
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ luna_test(libluna/TestUtf8.cpp TestUtf8)
|
||||
luna_test(libluna/TestFormat.cpp TestFormat)
|
||||
luna_test(libluna/TestHashTable.cpp TestHashTable)
|
||||
luna_test(libluna/TestCPath.cpp TestCPath)
|
||||
luna_test(libc/TestScanf.cpp TestScanf)
|
||||
|
||||
luna_app(run-tests.cpp run-tests)
|
||||
endif()
|
||||
|
91
tests/libc/TestScanf.cpp
Normal file
91
tests/libc/TestScanf.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <test.h>
|
||||
|
||||
// FIXME: Add more tests.
|
||||
|
||||
TestResult test_basic_scanf()
|
||||
{
|
||||
char hello[21];
|
||||
char world[21];
|
||||
|
||||
int parsed = sscanf("hello world", "%20s %20s", hello, world);
|
||||
validate(parsed == 2);
|
||||
|
||||
validate(!strcmp(hello, "hello"));
|
||||
validate(!strcmp(world, "world"));
|
||||
|
||||
test_success;
|
||||
}
|
||||
|
||||
TestResult test_incomplete_scanf()
|
||||
{
|
||||
char hello[21];
|
||||
char world[21];
|
||||
|
||||
int parsed = sscanf("hello ", "%20s %20s", hello, world);
|
||||
validate(parsed == 1);
|
||||
|
||||
validate(!strcmp(hello, "hello"));
|
||||
|
||||
test_success;
|
||||
}
|
||||
|
||||
TestResult test_integer_scanf()
|
||||
{
|
||||
int hour;
|
||||
int min;
|
||||
|
||||
int parsed = sscanf("23:59", "%d:%d", &hour, &min);
|
||||
validate(parsed == 2);
|
||||
|
||||
validate(hour == 23);
|
||||
validate(min == 59);
|
||||
|
||||
test_success;
|
||||
}
|
||||
|
||||
TestResult test_integer_auto_base_scanf()
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
|
||||
int parsed = sscanf("65, \t0x23, 0755", "%i, %i, %i", &a, &b, &c);
|
||||
validate(parsed == 3);
|
||||
|
||||
validate(a == 65);
|
||||
validate(b == 0x23);
|
||||
validate(c == 0755);
|
||||
|
||||
test_success;
|
||||
}
|
||||
|
||||
TestResult test_scanf_characters_consumed()
|
||||
{
|
||||
int hour;
|
||||
int min;
|
||||
int nr_chars;
|
||||
|
||||
int parsed = sscanf("23:59", "%d:%d%n", &hour, &min, &nr_chars);
|
||||
validate(parsed == 2);
|
||||
|
||||
validate(hour == 23);
|
||||
validate(min == 59);
|
||||
validate(nr_chars == 5);
|
||||
|
||||
test_success;
|
||||
}
|
||||
|
||||
Result<void> test_main()
|
||||
{
|
||||
test_prelude;
|
||||
|
||||
run_test(test_basic_scanf);
|
||||
run_test(test_incomplete_scanf);
|
||||
run_test(test_integer_scanf);
|
||||
run_test(test_integer_auto_base_scanf);
|
||||
run_test(test_scanf_characters_consumed);
|
||||
|
||||
return {};
|
||||
}
|
Loading…
Reference in New Issue
Block a user