Compare commits
21 Commits
4ef7b8e71c
...
1dc26d3c06
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dc26d3c06 | |||
| a05fcdde8c | |||
| deb2f52a35 | |||
| 8224659c48 | |||
| 1922d78661 | |||
| 6d1ecc2388 | |||
| 5cf658c175 | |||
| ef2738bfb7 | |||
| d7865b2929 | |||
| 9c79971bdc | |||
| ff75c15ba3 | |||
| 9e6fa0a1ba | |||
| 6e95519acc | |||
| d081655913 | |||
| 9c3eb8d270 | |||
| 68479bf07e | |||
| d528314ae3 | |||
| 40dd29b876 | |||
| dc1d7e3fae | |||
| 8f8ba2751c | |||
| 928d3e3fe7 |
@@ -49,6 +49,8 @@
|
|||||||
|
|
||||||
#define DEBUG_VTTY 1
|
#define DEBUG_VTTY 1
|
||||||
|
|
||||||
|
#define DEBUG_DEVFS 0
|
||||||
|
|
||||||
#define DEBUG_PCI 0
|
#define DEBUG_PCI 0
|
||||||
#define DEBUG_SCHEDULER 0
|
#define DEBUG_SCHEDULER 0
|
||||||
#define DEBUG_PS2 1
|
#define DEBUG_PS2 1
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ namespace Kernel
|
|||||||
protected:
|
protected:
|
||||||
Device(mode_t, uid_t, gid_t);
|
Device(mode_t, uid_t, gid_t);
|
||||||
|
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() final override { return BAN::Error::from_errno(EINVAL); }
|
private:
|
||||||
|
BAN::ErrorOr<void> sync_inode(SyncType) final override { return {}; }
|
||||||
|
BAN::ErrorOr<void> sync_data() final override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlockDevice : public Device, public BAN::Weakable<BlockDevice>
|
class BlockDevice : public Device, public BAN::Weakable<BlockDevice>
|
||||||
|
|||||||
@@ -21,24 +21,8 @@ namespace Kernel
|
|||||||
void notify(BAN::RefPtr<Inode> inode, uint32_t event);
|
void notify(BAN::RefPtr<Inode> inode, uint32_t event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Epoll() {
|
Epoll();
|
||||||
m_ino = 0;
|
|
||||||
m_mode = Mode::IRUSR | Mode::IWUSR;
|
|
||||||
m_nlink = 0;
|
|
||||||
m_uid = 0;
|
|
||||||
m_gid = 0;
|
|
||||||
m_size = 0;
|
|
||||||
m_atime = {};
|
|
||||||
m_mtime = {};
|
|
||||||
m_ctime = {};
|
|
||||||
m_blksize = PAGE_SIZE;
|
|
||||||
m_blocks = 0;
|
|
||||||
m_dev = 0;
|
|
||||||
m_rdev = 0;
|
|
||||||
m_kind = InodeKind::EPOLL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
const FileSystem* filesystem() const override { return nullptr; }
|
const FileSystem* filesystem() const override { return nullptr; }
|
||||||
|
|
||||||
bool can_read_impl() const override { return false; }
|
bool can_read_impl() const override { return false; }
|
||||||
@@ -46,7 +30,8 @@ namespace Kernel
|
|||||||
bool has_error_impl() const override { return false; }
|
bool has_error_impl() const override { return false; }
|
||||||
bool has_hungup_impl() const override { return false; }
|
bool has_hungup_impl() const override { return false; }
|
||||||
|
|
||||||
BAN::ErrorOr<void> fsync_impl() override { return {}; }
|
BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
|
||||||
|
BAN::ErrorOr<void> sync_data() override { return {}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ListenEventList
|
struct ListenEventList
|
||||||
|
|||||||
@@ -11,38 +11,22 @@ namespace Kernel
|
|||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(uint64_t initval, bool semaphore);
|
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(uint64_t initval, bool semaphore);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventFD(uint64_t initval, bool is_semaphore);
|
||||||
|
|
||||||
const FileSystem* filesystem() const override { return nullptr; }
|
const FileSystem* filesystem() const override { return nullptr; }
|
||||||
|
|
||||||
protected:
|
BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
|
||||||
|
BAN::ErrorOr<void> sync_data() override { return {}; }
|
||||||
|
|
||||||
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
BAN::ErrorOr<void> fsync_impl() final override { return {}; }
|
|
||||||
|
|
||||||
bool can_read_impl() const override { return m_value > 0; }
|
bool can_read_impl() const override { return m_value > 0; }
|
||||||
bool can_write_impl() const override { return m_value < UINT64_MAX - 1; }
|
bool can_write_impl() const override { return m_value < UINT64_MAX - 1; }
|
||||||
bool has_error_impl() const override { return false; }
|
bool has_error_impl() const override { return false; }
|
||||||
bool has_hungup_impl() const override { return false; }
|
bool has_hungup_impl() const override { return false; }
|
||||||
|
|
||||||
private:
|
|
||||||
EventFD(uint64_t initval, bool is_semaphore)
|
|
||||||
: m_is_semaphore(is_semaphore)
|
|
||||||
, m_value(initval)
|
|
||||||
{
|
|
||||||
m_ino = 0;
|
|
||||||
m_mode = { Mode::IFCHR | Mode::IRUSR | Mode::IWUSR };
|
|
||||||
m_nlink = 0;
|
|
||||||
m_uid = 0;
|
|
||||||
m_gid = 0;
|
|
||||||
m_size = 0;
|
|
||||||
m_atime = {};
|
|
||||||
m_mtime = {};
|
|
||||||
m_ctime = {};
|
|
||||||
m_blksize = 8;
|
|
||||||
m_blocks = 0;
|
|
||||||
m_dev = 0;
|
|
||||||
m_rdev = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const bool m_is_semaphore;
|
const bool m_is_semaphore;
|
||||||
BAN::Atomic<uint64_t> m_value;
|
BAN::Atomic<uint64_t> m_value;
|
||||||
|
|||||||
@@ -16,25 +16,12 @@ namespace Kernel
|
|||||||
public:
|
public:
|
||||||
~Ext2Inode();
|
~Ext2Inode();
|
||||||
|
|
||||||
#if 0
|
|
||||||
virtual ino_t ino() const override { return m_ino; };
|
|
||||||
virtual Mode mode() const override { return { m_inode.mode }; }
|
|
||||||
virtual nlink_t nlink() const override { return m_inode.links_count; }
|
|
||||||
virtual uid_t uid() const override { return m_inode.uid; }
|
|
||||||
virtual gid_t gid() const override { return m_inode.gid; }
|
|
||||||
virtual off_t size() const override { return m_inode.size; }
|
|
||||||
virtual timespec atime() const override { return timespec { .tv_sec = m_inode.atime, .tv_nsec = 0 }; }
|
|
||||||
virtual timespec mtime() const override { return timespec { .tv_sec = m_inode.mtime, .tv_nsec = 0 }; }
|
|
||||||
virtual timespec ctime() const override { return timespec { .tv_sec = m_inode.ctime, .tv_nsec = 0 }; }
|
|
||||||
virtual blksize_t blksize() const override;
|
|
||||||
virtual blkcnt_t blocks() const override;
|
|
||||||
virtual dev_t dev() const override { return 0; }
|
|
||||||
virtual dev_t rdev() const override { return 0; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
virtual const FileSystem* filesystem() const override;
|
virtual const FileSystem* filesystem() const override;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
virtual BAN::ErrorOr<void> sync_inode(SyncType) override;
|
||||||
|
virtual BAN::ErrorOr<void> sync_data() override;
|
||||||
|
|
||||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
|
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
|
||||||
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
|
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
|
||||||
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||||
@@ -49,10 +36,6 @@ namespace Kernel
|
|||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||||
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
|
||||||
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
|
|
||||||
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
|
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() override;
|
|
||||||
|
|
||||||
virtual bool can_read_impl() const override { return true; }
|
virtual bool can_read_impl() const override { return true; }
|
||||||
virtual bool can_write_impl() const override { return true; }
|
virtual bool can_write_impl() const override { return true; }
|
||||||
@@ -66,7 +49,7 @@ namespace Kernel
|
|||||||
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked
|
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked
|
||||||
uint32_t max_used_data_block_count() const { return size() / blksize(); }
|
uint32_t max_used_data_block_count() const { return size() / blksize(); }
|
||||||
|
|
||||||
BAN::ErrorOr<void> sync_no_lock();
|
BAN::ErrorOr<void> sync_inode_no_lock();
|
||||||
|
|
||||||
BAN::ErrorOr<bool> is_directory_empty_no_lock();
|
BAN::ErrorOr<bool> is_directory_empty_no_lock();
|
||||||
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_no_lock(BAN::StringView);
|
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_no_lock(BAN::StringView);
|
||||||
@@ -101,7 +84,7 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
// TODO: there was some memcmp smarty pants stuff here.
|
// TODO: there was some memcmp smarty pants stuff here.
|
||||||
// How do we wanna approach it?
|
// How do we wanna approach it?
|
||||||
if (auto ret = inode.sync_no_lock(); ret.is_error())
|
if (auto ret = inode.sync_inode_no_lock(); ret.is_error())
|
||||||
dwarnln("failed to sync inode: {}", ret.error());
|
dwarnln("failed to sync inode: {}", ret.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ namespace Kernel
|
|||||||
|
|
||||||
const FAT::DirectoryEntry& entry() const { return m_entry; }
|
const FAT::DirectoryEntry& entry() const { return m_entry; }
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
virtual BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
|
||||||
|
virtual BAN::ErrorOr<void> sync_data() override { return {}; }
|
||||||
|
|
||||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
|
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
|
||||||
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
|
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
|
||||||
//virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
//virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||||
@@ -29,9 +32,6 @@ namespace Kernel
|
|||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
//virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
//virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
//virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
//virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||||
//virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
|
||||||
//virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
|
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
|
|
||||||
|
|
||||||
virtual bool can_read_impl() const override { return true; }
|
virtual bool can_read_impl() const override { return true; }
|
||||||
virtual bool can_write_impl() const override { return true; }
|
virtual bool can_write_impl() const override { return true; }
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ namespace Kernel
|
|||||||
bool ifsock() const { return (mode & Mask::TYPE_MASK) == Mask::IFSOCK; }
|
bool ifsock() const { return (mode & Mask::TYPE_MASK) == Mask::IFSOCK; }
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
};
|
};
|
||||||
enum InodeKind : uint8_t {
|
|
||||||
|
enum InodeKind : uint8_t
|
||||||
|
{
|
||||||
DEVICE = 0x01,
|
DEVICE = 0x01,
|
||||||
EPOLL = 0x02,
|
EPOLL = 0x02,
|
||||||
PIPE = 0x04,
|
PIPE = 0x04,
|
||||||
@@ -69,6 +71,15 @@ namespace Kernel
|
|||||||
PARTITION = 0x10,
|
PARTITION = 0x10,
|
||||||
STORAGE = 0x20,
|
STORAGE = 0x20,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SyncType
|
||||||
|
{
|
||||||
|
General,
|
||||||
|
Mode,
|
||||||
|
UidGid,
|
||||||
|
Times,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Inode() {}
|
virtual ~Inode() {}
|
||||||
|
|
||||||
@@ -134,10 +145,10 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<void> fsync();
|
BAN::ErrorOr<void> fsync();
|
||||||
|
|
||||||
// Select/Non blocking API
|
// Select/Non blocking API
|
||||||
bool can_read() const;
|
bool can_read() const { return can_read_impl(); }
|
||||||
bool can_write() const;
|
bool can_write() const { return can_write_impl(); }
|
||||||
bool has_error() const;
|
bool has_error() const { return has_error_impl(); }
|
||||||
bool has_hungup() const;
|
bool has_hungup() const { return has_hungup_impl(); }
|
||||||
|
|
||||||
BAN::ErrorOr<long> ioctl(int request, void* arg);
|
BAN::ErrorOr<long> ioctl(int request, void* arg);
|
||||||
|
|
||||||
@@ -148,6 +159,9 @@ namespace Kernel
|
|||||||
virtual void on_close(int status_flags) { (void)status_flags; }
|
virtual void on_close(int status_flags) { (void)status_flags; }
|
||||||
virtual void on_clone(int status_flags) { (void)status_flags; }
|
virtual void on_clone(int status_flags) { (void)status_flags; }
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<void> sync_inode(SyncType) = 0;
|
||||||
|
virtual BAN::ErrorOr<void> sync_data() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Directory API
|
// Directory API
|
||||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
|
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
|
||||||
@@ -178,10 +192,6 @@ namespace Kernel
|
|||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
|
||||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); }
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); }
|
||||||
virtual BAN::ErrorOr<void> chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); }
|
|
||||||
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
|
|
||||||
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) { return BAN::Error::from_errno(ENOTSUP); }
|
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() = 0;
|
|
||||||
|
|
||||||
// Select/Non blocking API
|
// Select/Non blocking API
|
||||||
virtual bool can_read_impl() const = 0;
|
virtual bool can_read_impl() const = 0;
|
||||||
@@ -196,6 +206,7 @@ namespace Kernel
|
|||||||
// But the thing is I would have to refactor a big chunk of the codebase
|
// But the thing is I would have to refactor a big chunk of the codebase
|
||||||
// to add it as a parameter to Inode() soooooo yeah no, not doing that rn.
|
// to add it as a parameter to Inode() soooooo yeah no, not doing that rn.
|
||||||
uint8_t m_kind = 0;
|
uint8_t m_kind = 0;
|
||||||
|
|
||||||
BAN::Atomic<ino_t> m_ino;
|
BAN::Atomic<ino_t> m_ino;
|
||||||
BAN::Atomic<mode_t> m_mode;
|
BAN::Atomic<mode_t> m_mode;
|
||||||
BAN::Atomic<nlink_t> m_nlink;
|
BAN::Atomic<nlink_t> m_nlink;
|
||||||
@@ -210,12 +221,14 @@ namespace Kernel
|
|||||||
BAN::Atomic<blkcnt_t> m_blocks;
|
BAN::Atomic<blkcnt_t> m_blocks;
|
||||||
BAN::Atomic<dev_t> m_dev;
|
BAN::Atomic<dev_t> m_dev;
|
||||||
BAN::Atomic<dev_t> m_rdev;
|
BAN::Atomic<dev_t> m_rdev;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SpinLock m_shared_region_lock;
|
SpinLock m_shared_region_lock;
|
||||||
BAN::WeakPtr<SharedFileData> m_shared_region;
|
BAN::WeakPtr<SharedFileData> m_shared_region;
|
||||||
|
|
||||||
SpinLock m_epoll_lock;
|
SpinLock m_epoll_lock;
|
||||||
BAN::LinkedList<class Epoll*> m_epolls;
|
BAN::LinkedList<class Epoll*> m_epolls;
|
||||||
|
|
||||||
friend class Epoll;
|
friend class Epoll;
|
||||||
friend class FileBackedRegion;
|
friend class FileBackedRegion;
|
||||||
friend class OpenFileDescriptorSet;
|
friend class OpenFileDescriptorSet;
|
||||||
|
|||||||
@@ -5,23 +5,30 @@
|
|||||||
#include <kernel/Memory/ByteRingBuffer.h>
|
#include <kernel/Memory/ByteRingBuffer.h>
|
||||||
#include <kernel/ThreadBlocker.h>
|
#include <kernel/ThreadBlocker.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
class Pipe : public Inode
|
class Pipe final : public Inode, public BAN::Weakable<Pipe>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(const Credentials&);
|
static BAN::ErrorOr<BAN::RefPtr<Inode>> open(BAN::RefPtr<Inode>, int status_flags);
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(uid_t, gid_t);
|
||||||
|
~Pipe();
|
||||||
|
|
||||||
void on_close(int status_flags) override;
|
void on_close(int status_flags) override;
|
||||||
void on_clone(int status_flags) override;
|
void on_clone(int status_flags) override;
|
||||||
|
|
||||||
virtual const FileSystem* filesystem() const override { return nullptr; }
|
virtual const FileSystem* filesystem() const override { return nullptr; }
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
virtual BAN::ErrorOr<void> sync_inode(SyncType) override;
|
||||||
|
virtual BAN::ErrorOr<void> sync_data() override;
|
||||||
|
|
||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() final override { return {}; }
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||||
|
|
||||||
virtual bool can_read_impl() const override { return !m_buffer->empty(); }
|
virtual bool can_read_impl() const override { return !m_buffer->empty(); }
|
||||||
virtual bool can_write_impl() const override { return true; }
|
virtual bool can_write_impl() const override { return true; }
|
||||||
@@ -31,7 +38,7 @@ namespace Kernel
|
|||||||
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
|
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pipe(const Credentials&);
|
Pipe(const struct stat&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mutex m_mutex;
|
Mutex m_mutex;
|
||||||
@@ -39,8 +46,10 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::UniqPtr<ByteRingBuffer> m_buffer;
|
BAN::UniqPtr<ByteRingBuffer> m_buffer;
|
||||||
|
|
||||||
BAN::Atomic<uint32_t> m_writing_count { 1 };
|
BAN::Atomic<uint32_t> m_writing_count { 0 };
|
||||||
BAN::Atomic<uint32_t> m_reading_count { 1 };
|
BAN::Atomic<uint32_t> m_reading_count { 0 };
|
||||||
|
|
||||||
|
BAN::RefPtr<Inode> m_named_inode;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ namespace Kernel
|
|||||||
m_gid = info.gid;
|
m_gid = info.gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> fsync_impl() final override { return {}; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BAN::ErrorOr<void> sync_inode(SyncType) final override { return {}; }
|
||||||
|
BAN::ErrorOr<void> sync_data() final override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,14 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<TmpInode>> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
static BAN::ErrorOr<BAN::RefPtr<TmpInode>> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||||
~TmpInode();
|
virtual ~TmpInode();
|
||||||
|
|
||||||
virtual const FileSystem* filesystem() const override;
|
virtual const FileSystem* filesystem() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||||
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
|
||||||
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
|
|
||||||
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
|
|
||||||
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
|
|
||||||
|
|
||||||
void sync();
|
void write_inode_to_fs();
|
||||||
virtual BAN::ErrorOr<void> prepare_unlink_no_lock() { return {}; };
|
virtual BAN::ErrorOr<void> prepare_unlink_no_lock() { return {}; };
|
||||||
|
|
||||||
void free_all_blocks();
|
void free_all_blocks();
|
||||||
@@ -49,6 +45,10 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<size_t> block_index_with_allocation(size_t data_block_index);
|
BAN::ErrorOr<size_t> block_index_with_allocation(size_t data_block_index);
|
||||||
BAN::ErrorOr<size_t> block_index_from_indirect_with_allocation_no_lock(size_t& block, size_t index, uint32_t depth);
|
BAN::ErrorOr<size_t> block_index_from_indirect_with_allocation_no_lock(size_t& block, size_t index, uint32_t depth);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::ErrorOr<void> sync_inode(SyncType) override;
|
||||||
|
BAN::ErrorOr<void> sync_data() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TmpFileSystem& m_fs;
|
TmpFileSystem& m_fs;
|
||||||
TmpBlocks m_tmp_blocks;
|
TmpBlocks m_tmp_blocks;
|
||||||
@@ -66,7 +66,7 @@ namespace Kernel
|
|||||||
static BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
static BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||||
~TmpFileInode();
|
~TmpFileInode();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||||
@@ -82,13 +82,36 @@ namespace Kernel
|
|||||||
friend class TmpInode;
|
friend class TmpInode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE: this is just a dummy, when opening a fifo a pipe is created
|
||||||
|
class TmpFIFOInode : public TmpInode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<TmpFIFOInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||||
|
~TmpFIFOInode();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
|
|
||||||
|
virtual bool can_read_impl() const override { return false; }
|
||||||
|
virtual bool can_write_impl() const override { return false; }
|
||||||
|
virtual bool has_error_impl() const override { return false; }
|
||||||
|
virtual bool has_hungup_impl() const override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TmpFIFOInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
|
||||||
|
|
||||||
|
friend class TmpInode;
|
||||||
|
};
|
||||||
|
|
||||||
class TmpSocketInode : public TmpInode
|
class TmpSocketInode : public TmpInode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
static BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
|
||||||
~TmpSocketInode();
|
~TmpSocketInode();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
|
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
|
||||||
@@ -110,7 +133,7 @@ namespace Kernel
|
|||||||
static BAN::ErrorOr<BAN::RefPtr<TmpSymlinkInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, BAN::StringView target);
|
static BAN::ErrorOr<BAN::RefPtr<TmpSymlinkInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, BAN::StringView target);
|
||||||
~TmpSymlinkInode();
|
~TmpSymlinkInode();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
BAN::ErrorOr<BAN::String> link_target_impl() override;
|
BAN::ErrorOr<BAN::String> link_target_impl() override;
|
||||||
BAN::ErrorOr<void> set_link_target_impl(BAN::StringView) override;
|
BAN::ErrorOr<void> set_link_target_impl(BAN::StringView) override;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Kernel
|
|||||||
virtual uint32_t lock_depth() const = 0;
|
virtual uint32_t lock_depth() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mutex : public BaseMutex
|
class Mutex final : public BaseMutex
|
||||||
{
|
{
|
||||||
BAN_NON_COPYABLE(Mutex);
|
BAN_NON_COPYABLE(Mutex);
|
||||||
BAN_NON_MOVABLE(Mutex);
|
BAN_NON_MOVABLE(Mutex);
|
||||||
@@ -40,6 +40,7 @@ namespace Kernel
|
|||||||
pid_t expected = -1;
|
pid_t expected = -1;
|
||||||
while (!m_locker.compare_exchange(expected, tid))
|
while (!m_locker.compare_exchange(expected, tid))
|
||||||
{
|
{
|
||||||
|
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
|
||||||
Processor::yield();
|
Processor::yield();
|
||||||
expected = -1;
|
expected = -1;
|
||||||
}
|
}
|
||||||
@@ -84,13 +85,14 @@ namespace Kernel
|
|||||||
pid_t locker() const override { return m_locker; }
|
pid_t locker() const override { return m_locker; }
|
||||||
bool is_locked() const override { return m_locker != -1; }
|
bool is_locked() const override { return m_locker != -1; }
|
||||||
uint32_t lock_depth() const override { return m_lock_depth; }
|
uint32_t lock_depth() const override { return m_lock_depth; }
|
||||||
|
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Atomic<pid_t> m_locker { -1 };
|
BAN::Atomic<pid_t> m_locker { -1 };
|
||||||
uint32_t m_lock_depth { 0 };
|
uint32_t m_lock_depth { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class PriorityMutex : public BaseMutex
|
class PriorityMutex final : public BaseMutex
|
||||||
{
|
{
|
||||||
BAN_NON_COPYABLE(PriorityMutex);
|
BAN_NON_COPYABLE(PriorityMutex);
|
||||||
BAN_NON_MOVABLE(PriorityMutex);
|
BAN_NON_MOVABLE(PriorityMutex);
|
||||||
@@ -113,6 +115,7 @@ namespace Kernel
|
|||||||
pid_t expected = -1;
|
pid_t expected = -1;
|
||||||
while (!(has_priority || m_queue_length == 0) || !m_locker.compare_exchange(expected, tid))
|
while (!(has_priority || m_queue_length == 0) || !m_locker.compare_exchange(expected, tid))
|
||||||
{
|
{
|
||||||
|
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
|
||||||
Processor::yield();
|
Processor::yield();
|
||||||
expected = -1;
|
expected = -1;
|
||||||
}
|
}
|
||||||
@@ -164,6 +167,7 @@ namespace Kernel
|
|||||||
pid_t locker() const override { return m_locker; }
|
pid_t locker() const override { return m_locker; }
|
||||||
bool is_locked() const override { return m_locker != -1; }
|
bool is_locked() const override { return m_locker != -1; }
|
||||||
uint32_t lock_depth() const override { return m_lock_depth; }
|
uint32_t lock_depth() const override { return m_lock_depth; }
|
||||||
|
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Atomic<pid_t> m_locker { -1 };
|
BAN::Atomic<pid_t> m_locker { -1 };
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace Kernel
|
|||||||
// FIXME: this should probably be ordered tree like map
|
// FIXME: this should probably be ordered tree like map
|
||||||
// for fast lookup and less memory usage
|
// for fast lookup and less memory usage
|
||||||
BAN::Vector<paddr_t> pages;
|
BAN::Vector<paddr_t> pages;
|
||||||
|
BAN::Vector<uint32_t> writers;
|
||||||
BAN::RefPtr<Inode> inode;
|
BAN::RefPtr<Inode> inode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -100,13 +100,14 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<long> open_inode(VirtualFileSystem::File&&, int flags);
|
BAN::ErrorOr<long> open_inode(VirtualFileSystem::File&&, int flags);
|
||||||
|
|
||||||
BAN::ErrorOr<void> create_file_or_dir(int fd, const char* path, mode_t mode) const;
|
BAN::ErrorOr<void> create_file(int fd, const char* path, mode_t) const;
|
||||||
BAN::ErrorOr<long> sys_openat(int, const char* path, int, mode_t);
|
BAN::ErrorOr<long> sys_openat(int fd, const char* path, int flags, mode_t);
|
||||||
BAN::ErrorOr<long> sys_close(int fd);
|
BAN::ErrorOr<long> sys_close(int fd);
|
||||||
BAN::ErrorOr<long> sys_read(int fd, void* buffer, size_t count);
|
BAN::ErrorOr<long> sys_read(int fd, void* buffer, size_t count);
|
||||||
BAN::ErrorOr<long> sys_write(int fd, const void* buffer, size_t count);
|
BAN::ErrorOr<long> sys_write(int fd, const void* buffer, size_t count);
|
||||||
BAN::ErrorOr<long> sys_access(const char* path, int amode);
|
BAN::ErrorOr<long> sys_access(const char* path, int amode);
|
||||||
BAN::ErrorOr<long> sys_create_dir(const char*, mode_t);
|
BAN::ErrorOr<long> sys_mkdirat(int fd, const char* path, mode_t);
|
||||||
|
BAN::ErrorOr<long> sys_mkfifoat(int fd, const char* path, mode_t);
|
||||||
BAN::ErrorOr<long> sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag);
|
BAN::ErrorOr<long> sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag);
|
||||||
BAN::ErrorOr<long> sys_renameat(int oldfd, const char* old, int newfd, const char* _new);
|
BAN::ErrorOr<long> sys_renameat(int oldfd, const char* old, int newfd, const char* _new);
|
||||||
BAN::ErrorOr<long> sys_unlinkat(int fd, const char* path, int flag);
|
BAN::ErrorOr<long> sys_unlinkat(int fd, const char* path, int flag);
|
||||||
@@ -214,6 +215,7 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
|
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
|
||||||
BAN::ErrorOr<long> sys_pthread_self();
|
BAN::ErrorOr<long> sys_pthread_self();
|
||||||
BAN::ErrorOr<long> sys_pthread_kill(pthread_t thread, int signal);
|
BAN::ErrorOr<long> sys_pthread_kill(pthread_t thread, int signal);
|
||||||
|
BAN::ErrorOr<long> sys_pthread_detach(pthread_t thread);
|
||||||
|
|
||||||
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);
|
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);
|
||||||
|
|
||||||
|
|||||||
@@ -51,15 +51,6 @@ namespace Kernel
|
|||||||
|
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
|
|
||||||
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
|
||||||
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
|
|
||||||
|
|
||||||
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
|
|
||||||
|
|
||||||
virtual bool can_read_impl() const override { return m_output.flush; }
|
|
||||||
virtual bool has_error_impl() const override { return false; }
|
|
||||||
virtual bool has_hungup_impl() const override { return false; }
|
|
||||||
|
|
||||||
virtual bool master_has_closed() const { return false; }
|
virtual bool master_has_closed() const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -68,10 +59,16 @@ namespace Kernel
|
|||||||
virtual bool putchar_impl(uint8_t ch) = 0;
|
virtual bool putchar_impl(uint8_t ch) = 0;
|
||||||
virtual void after_write() {}
|
virtual void after_write() {}
|
||||||
|
|
||||||
|
void update_winsize(unsigned short cols, unsigned short rows);
|
||||||
|
|
||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) final override;
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) final override;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) final override;
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) final override;
|
||||||
|
|
||||||
void update_winsize(unsigned short cols, unsigned short rows);
|
virtual bool can_read_impl() const override { return m_output.flush; }
|
||||||
|
virtual bool has_error_impl() const override { return false; }
|
||||||
|
virtual bool has_hungup_impl() const override { return false; }
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool putchar(uint8_t ch);
|
bool putchar(uint8_t ch);
|
||||||
@@ -105,7 +102,7 @@ namespace Kernel
|
|||||||
protected:
|
protected:
|
||||||
Mutex m_mutex;
|
Mutex m_mutex;
|
||||||
|
|
||||||
RecursiveSpinLock m_write_lock;
|
Mutex m_write_lock;
|
||||||
ThreadBlocker m_write_blocker;
|
ThreadBlocker m_write_blocker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ namespace Kernel
|
|||||||
const Process& process() const;
|
const Process& process() const;
|
||||||
bool has_process() const { return m_process; }
|
bool has_process() const { return m_process; }
|
||||||
|
|
||||||
|
void detach() { m_is_detached = true; }
|
||||||
|
bool is_detached() const { return m_is_detached; }
|
||||||
|
|
||||||
bool is_userspace() const { return m_is_userspace; }
|
bool is_userspace() const { return m_is_userspace; }
|
||||||
|
|
||||||
uint64_t cpu_time_ns() const;
|
uint64_t cpu_time_ns() const;
|
||||||
@@ -176,6 +179,7 @@ namespace Kernel
|
|||||||
State m_state { State::NotStarted };
|
State m_state { State::NotStarted };
|
||||||
Process* m_process { nullptr };
|
Process* m_process { nullptr };
|
||||||
bool m_is_userspace { false };
|
bool m_is_userspace { false };
|
||||||
|
BAN::Atomic<bool> m_is_detached { false };
|
||||||
bool m_delete_process { false };
|
bool m_delete_process { false };
|
||||||
|
|
||||||
vaddr_t m_fsbase { 0 };
|
vaddr_t m_fsbase { 0 };
|
||||||
|
|||||||
@@ -14,6 +14,24 @@ namespace Kernel
|
|||||||
return BAN::RefPtr<Epoll>::adopt(epoll_ptr);
|
return BAN::RefPtr<Epoll>::adopt(epoll_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Epoll::Epoll()
|
||||||
|
{
|
||||||
|
m_ino = 0;
|
||||||
|
m_mode = Mode::IRUSR | Mode::IWUSR;
|
||||||
|
m_nlink = 0;
|
||||||
|
m_uid = 0;
|
||||||
|
m_gid = 0;
|
||||||
|
m_size = 0;
|
||||||
|
m_atime = {};
|
||||||
|
m_mtime = {};
|
||||||
|
m_ctime = {};
|
||||||
|
m_blksize = PAGE_SIZE;
|
||||||
|
m_blocks = 0;
|
||||||
|
m_dev = 0;
|
||||||
|
m_rdev = 0;
|
||||||
|
m_kind = InodeKind::EPOLL;
|
||||||
|
}
|
||||||
|
|
||||||
Epoll::~Epoll()
|
Epoll::~Epoll()
|
||||||
{
|
{
|
||||||
for (auto& [inode, _] : m_listening_events)
|
for (auto& [inode, _] : m_listening_events)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ namespace Kernel
|
|||||||
MUST(static_cast<TmpDirectoryInode*>(root_inode().ptr())->link_inode(*device, device->name()));
|
MUST(static_cast<TmpDirectoryInode*>(root_inode().ptr())->link_inode(*device, device->name()));
|
||||||
MUST(m_devices.push_back(device));
|
MUST(m_devices.push_back(device));
|
||||||
|
|
||||||
dprintln("Added device /dev/{}", device->name());
|
dprintln_if(DEBUG_DEVFS, "Added device /dev/{}", device->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevFileSystem::remove_device(BAN::RefPtr<Device> device)
|
void DevFileSystem::remove_device(BAN::RefPtr<Device> device)
|
||||||
@@ -170,7 +170,7 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintln("Removed device /dev/{}", device->name());
|
dprintln_if(DEBUG_DEVFS, "Removed device /dev/{}", device->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevFileSystem::add_inode(BAN::StringView path, BAN::RefPtr<TmpInode> inode)
|
void DevFileSystem::add_inode(BAN::StringView path, BAN::RefPtr<TmpInode> inode)
|
||||||
|
|||||||
@@ -14,6 +14,25 @@ namespace Kernel
|
|||||||
return BAN::RefPtr<Inode>(BAN::RefPtr<EventFD>::adopt(eventfd_ptr));
|
return BAN::RefPtr<Inode>(BAN::RefPtr<EventFD>::adopt(eventfd_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventFD::EventFD(uint64_t initval, bool is_semaphore)
|
||||||
|
: m_is_semaphore(is_semaphore)
|
||||||
|
, m_value(initval)
|
||||||
|
{
|
||||||
|
m_ino = 0;
|
||||||
|
m_mode = Mode::IFCHR | Mode::IRUSR | Mode::IWUSR;
|
||||||
|
m_nlink = 0;
|
||||||
|
m_uid = 0;
|
||||||
|
m_gid = 0;
|
||||||
|
m_size = 0;
|
||||||
|
m_atime = {};
|
||||||
|
m_mtime = {};
|
||||||
|
m_ctime = {};
|
||||||
|
m_blksize = 8;
|
||||||
|
m_blocks = 0;
|
||||||
|
m_dev = 0;
|
||||||
|
m_rdev = 0;
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<size_t> EventFD::read_impl(off_t, BAN::ByteSpan buffer)
|
BAN::ErrorOr<size_t> EventFD::read_impl(off_t, BAN::ByteSpan buffer)
|
||||||
{
|
{
|
||||||
if (buffer.size() < sizeof(uint64_t))
|
if (buffer.size() < sizeof(uint64_t))
|
||||||
|
|||||||
@@ -73,6 +73,22 @@ namespace Kernel
|
|||||||
return &m_fs;
|
return &m_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Ext2Inode::sync_inode(SyncType)
|
||||||
|
{
|
||||||
|
RWLockRDGuard _(m_lock);
|
||||||
|
TRY(sync_inode_no_lock());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Ext2Inode::sync_data()
|
||||||
|
{
|
||||||
|
RWLockRDGuard _(m_lock);
|
||||||
|
for (size_t i = 0; i < max_used_data_block_count(); i++)
|
||||||
|
if (const auto fs_block = TRY(fs_block_of_data_block_index_no_lock(i, false)); fs_block.has_value())
|
||||||
|
TRY(m_fs.sync_block(fs_block.value()));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block_no_lock(uint32_t& block, uint32_t index, uint32_t depth, bool allocate)
|
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block_no_lock(uint32_t& block, uint32_t index, uint32_t depth, bool allocate)
|
||||||
{
|
{
|
||||||
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
|
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
|
||||||
@@ -195,7 +211,7 @@ namespace Kernel
|
|||||||
memset(m_ext2_blocks.block, 0, sizeof(m_ext2_blocks.block));
|
memset(m_ext2_blocks.block, 0, sizeof(m_ext2_blocks.block));
|
||||||
memcpy(m_ext2_blocks.block, target.data(), target.size());
|
memcpy(m_ext2_blocks.block, target.data(), target.size());
|
||||||
m_size = target.size();
|
m_size = target.size();
|
||||||
TRY(sync_no_lock());
|
TRY(sync_inode_no_lock());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +350,7 @@ namespace Kernel
|
|||||||
const auto old_size = static_cast<size_t>(m_size);
|
const auto old_size = static_cast<size_t>(m_size);
|
||||||
|
|
||||||
m_size = new_size;
|
m_size = new_size;
|
||||||
if (auto ret = sync_no_lock(); ret.is_error())
|
if (auto ret = sync_inode_no_lock(); ret.is_error())
|
||||||
{
|
{
|
||||||
m_size = old_size;
|
m_size = old_size;
|
||||||
return ret.release_error();
|
return ret.release_error();
|
||||||
@@ -343,82 +359,6 @@ namespace Kernel
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::chmod_impl(mode_t mode)
|
|
||||||
{
|
|
||||||
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
|
|
||||||
|
|
||||||
RWLockWRGuard _(m_lock);
|
|
||||||
|
|
||||||
if (m_mode == mode)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const auto old_mode = m_mode.load();
|
|
||||||
|
|
||||||
m_mode = (m_mode & Inode::Mode::TYPE_MASK) | mode;
|
|
||||||
if (auto ret = sync_no_lock(); ret.is_error())
|
|
||||||
{
|
|
||||||
m_mode = old_mode;
|
|
||||||
return ret.release_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::chown_impl(uid_t uid, gid_t gid)
|
|
||||||
{
|
|
||||||
RWLockWRGuard _(m_lock);
|
|
||||||
|
|
||||||
if (m_uid == uid && m_gid == gid)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const auto old_uid = m_uid.load();
|
|
||||||
const auto old_gid = m_gid.load();
|
|
||||||
|
|
||||||
m_uid = uid;
|
|
||||||
m_gid = gid;
|
|
||||||
if (auto ret = sync_no_lock(); ret.is_error())
|
|
||||||
{
|
|
||||||
m_uid = old_uid;
|
|
||||||
m_gid = old_gid;
|
|
||||||
return ret.release_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::utimens_impl(const timespec times[2])
|
|
||||||
{
|
|
||||||
RWLockWRGuard _(m_lock);
|
|
||||||
|
|
||||||
const uint32_t old_times[2] {
|
|
||||||
static_cast<uint32_t>(m_atime.tv_sec),
|
|
||||||
static_cast<uint32_t>(m_mtime.tv_sec),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (times[0].tv_nsec != UTIME_OMIT)
|
|
||||||
m_atime.tv_sec = times[0].tv_sec;
|
|
||||||
if (times[1].tv_nsec != UTIME_OMIT)
|
|
||||||
m_mtime.tv_sec = times[1].tv_sec;
|
|
||||||
|
|
||||||
if (auto ret = sync_no_lock(); ret.is_error())
|
|
||||||
{
|
|
||||||
m_atime.tv_sec = old_times[0];
|
|
||||||
m_mtime.tv_sec = old_times[1];
|
|
||||||
return ret.release_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::fsync_impl()
|
|
||||||
{
|
|
||||||
RWLockRDGuard _(m_lock);
|
|
||||||
for (size_t i = 0; i < max_used_data_block_count(); i++)
|
|
||||||
if (const auto fs_block = TRY(fs_block_of_data_block_index_no_lock(i, false)); fs_block.has_value())
|
|
||||||
TRY(m_fs.sync_block(fs_block.value()));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::cleanup_indirect_block_no_lock(uint32_t block, uint32_t depth)
|
BAN::ErrorOr<void> Ext2Inode::cleanup_indirect_block_no_lock(uint32_t block, uint32_t depth)
|
||||||
{
|
{
|
||||||
ASSERT(block);
|
ASSERT(block);
|
||||||
@@ -467,7 +407,7 @@ done:
|
|||||||
// mark blocks as deleted
|
// mark blocks as deleted
|
||||||
memset(m_ext2_blocks.block, 0x00, sizeof(m_ext2_blocks.block));
|
memset(m_ext2_blocks.block, 0x00, sizeof(m_ext2_blocks.block));
|
||||||
|
|
||||||
TRY(sync_no_lock());
|
TRY(sync_inode_no_lock());
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -747,7 +687,7 @@ done:
|
|||||||
memcpy(new_entry.name, name.data(), name.size());
|
memcpy(new_entry.name, name.data(), name.size());
|
||||||
|
|
||||||
inode.m_nlink++;
|
inode.m_nlink++;
|
||||||
TRY(inode.sync_no_lock());
|
TRY(inode.sync_inode_no_lock());
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
@@ -872,13 +812,13 @@ needs_new_block:
|
|||||||
if (entry_name == "."_sv)
|
if (entry_name == "."_sv)
|
||||||
{
|
{
|
||||||
m_nlink--;
|
m_nlink--;
|
||||||
TRY(sync_no_lock());
|
TRY(sync_inode_no_lock());
|
||||||
}
|
}
|
||||||
else if (entry_name == ".."_sv)
|
else if (entry_name == ".."_sv)
|
||||||
{
|
{
|
||||||
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
|
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
|
||||||
parent->m_nlink--;
|
parent->m_nlink--;
|
||||||
TRY(parent->sync_no_lock());
|
TRY(parent->sync_inode_no_lock());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
@@ -935,7 +875,7 @@ needs_new_block:
|
|||||||
else
|
else
|
||||||
inode->m_nlink--;
|
inode->m_nlink--;
|
||||||
|
|
||||||
TRY(sync_no_lock());
|
TRY(sync_inode_no_lock());
|
||||||
|
|
||||||
// NOTE: If this was the last link to inode we must
|
// NOTE: If this was the last link to inode we must
|
||||||
// remove it from inode cache to trigger cleanup
|
// remove it from inode cache to trigger cleanup
|
||||||
@@ -964,13 +904,9 @@ needs_new_block:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Ext2Inode::sync_no_lock()
|
BAN::ErrorOr<void> Ext2Inode::sync_inode_no_lock()
|
||||||
{
|
{
|
||||||
auto inode_location = TRY(m_fs.locate_inode(ino()));
|
const Ext2::Inode inode {
|
||||||
auto block_buffer = TRY(m_fs.get_block_buffer());
|
|
||||||
|
|
||||||
TRY(m_fs.read_block(inode_location.block, block_buffer));
|
|
||||||
Ext2::Inode inode {
|
|
||||||
.mode = static_cast<uint16_t>(m_mode),
|
.mode = static_cast<uint16_t>(m_mode),
|
||||||
.uid = static_cast<uint16_t>(m_uid),
|
.uid = static_cast<uint16_t>(m_uid),
|
||||||
.size = static_cast<uint32_t>(m_size),
|
.size = static_cast<uint32_t>(m_size),
|
||||||
@@ -988,8 +924,13 @@ needs_new_block:
|
|||||||
.file_acl = m_og_file_acl,
|
.file_acl = m_og_file_acl,
|
||||||
.dir_acl = m_og_dir_acl,
|
.dir_acl = m_og_dir_acl,
|
||||||
.faddr = m_og_faddr,
|
.faddr = m_og_faddr,
|
||||||
.osd2 = m_og_osd2
|
.osd2 = m_og_osd2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto inode_location = TRY(m_fs.locate_inode(ino()));
|
||||||
|
auto block_buffer = TRY(m_fs.get_block_buffer());
|
||||||
|
|
||||||
|
TRY(m_fs.read_block(inode_location.block, block_buffer));
|
||||||
if (memcmp(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode)))
|
if (memcmp(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode)))
|
||||||
{
|
{
|
||||||
memcpy(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode));
|
memcpy(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode));
|
||||||
|
|||||||
@@ -246,50 +246,73 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> Inode::chmod(mode_t mode)
|
BAN::ErrorOr<void> Inode::chmod(mode_t mode)
|
||||||
{
|
{
|
||||||
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
|
|
||||||
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
||||||
return BAN::Error::from_errno(EROFS);
|
return BAN::Error::from_errno(EROFS);
|
||||||
return chmod_impl(mode);
|
|
||||||
|
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
|
||||||
|
mode |= m_mode & Inode::Mode::TYPE_MASK;
|
||||||
|
|
||||||
|
const auto old_mode = m_mode.exchange(mode);
|
||||||
|
|
||||||
|
if (auto ret = sync_inode(SyncType::Mode); ret.is_error())
|
||||||
|
{
|
||||||
|
m_mode.compare_exchange(mode, old_mode);
|
||||||
|
return ret.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Inode::chown(uid_t uid, gid_t gid)
|
BAN::ErrorOr<void> Inode::chown(uid_t uid, gid_t gid)
|
||||||
{
|
{
|
||||||
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
||||||
return BAN::Error::from_errno(EROFS);
|
return BAN::Error::from_errno(EROFS);
|
||||||
return chown_impl(uid, gid);
|
|
||||||
|
// TODO: unify uid and gid to a single atomic operation.
|
||||||
|
// this needs 64 bit atomic support from 32 bit target
|
||||||
|
|
||||||
|
const auto old_uid = m_uid.exchange(uid);
|
||||||
|
const auto old_gid = m_gid.exchange(gid);
|
||||||
|
|
||||||
|
if (auto ret = sync_inode(SyncType::UidGid); ret.is_error())
|
||||||
|
{
|
||||||
|
m_uid.compare_exchange(uid, old_uid);
|
||||||
|
m_gid.compare_exchange(gid, old_gid);
|
||||||
|
return ret.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Inode::utimens(const timespec times[2])
|
BAN::ErrorOr<void> Inode::utimens(const timespec times[2])
|
||||||
{
|
{
|
||||||
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
|
||||||
return BAN::Error::from_errno(EROFS);
|
return BAN::Error::from_errno(EROFS);
|
||||||
return utimens_impl(times);
|
|
||||||
|
// TODO: make these atomic
|
||||||
|
|
||||||
|
const auto old_atime = m_atime;
|
||||||
|
const auto old_mtime = m_mtime;
|
||||||
|
|
||||||
|
m_atime = times[0];
|
||||||
|
m_mtime = times[1];
|
||||||
|
|
||||||
|
if (auto ret = sync_inode(SyncType::Times); ret.is_error())
|
||||||
|
{
|
||||||
|
m_atime = old_atime;
|
||||||
|
m_mtime = old_mtime;
|
||||||
|
return ret.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Inode::fsync()
|
BAN::ErrorOr<void> Inode::fsync()
|
||||||
{
|
{
|
||||||
// TODO: should we sync shared data?
|
// TODO: should we sync MAP_SHARED data?
|
||||||
return fsync_impl();
|
TRY(sync_inode(SyncType::General));
|
||||||
}
|
TRY(sync_data());
|
||||||
|
return {};
|
||||||
bool Inode::can_read() const
|
|
||||||
{
|
|
||||||
return can_read_impl();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Inode::can_write() const
|
|
||||||
{
|
|
||||||
return can_write_impl();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Inode::has_error() const
|
|
||||||
{
|
|
||||||
return has_error_impl();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Inode::has_hungup() const
|
|
||||||
{
|
|
||||||
return has_hungup_impl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Inode::ioctl(int request, void* arg)
|
BAN::ErrorOr<long> Inode::ioctl(int request, void* arg)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <BAN/HashMap.h>
|
||||||
#include <kernel/FS/Pipe.h>
|
#include <kernel/FS/Pipe.h>
|
||||||
#include <kernel/Lock/LockGuard.h>
|
#include <kernel/Lock/LockGuard.h>
|
||||||
#include <kernel/Thread.h>
|
#include <kernel/Thread.h>
|
||||||
@@ -12,34 +13,124 @@ namespace Kernel
|
|||||||
|
|
||||||
static constexpr size_t s_pipe_buffer_size = 0x10000;
|
static constexpr size_t s_pipe_buffer_size = 0x10000;
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::create(const Credentials& credentials)
|
static Mutex s_named_pipe_mutex;
|
||||||
|
static BAN::HashMap<BAN::RefPtr<Inode>, BAN::WeakPtr<Pipe>> s_named_pipes;
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::open(BAN::RefPtr<Inode> inode, int status_flags)
|
||||||
{
|
{
|
||||||
auto* pipe_ptr = new Pipe(credentials);
|
BAN::RefPtr<Pipe> pipe;
|
||||||
|
|
||||||
|
{
|
||||||
|
LockGuard _(s_named_pipe_mutex);
|
||||||
|
|
||||||
|
auto it = s_named_pipes.find(inode);
|
||||||
|
if (it == s_named_pipes.end())
|
||||||
|
it = TRY(s_named_pipes.insert(inode, {}));
|
||||||
|
|
||||||
|
if (!(pipe = it->value.lock()))
|
||||||
|
{
|
||||||
|
// FIXME: these should probably reference the underlying inode(?)
|
||||||
|
const struct stat st {
|
||||||
|
.st_dev = inode->dev(),
|
||||||
|
.st_ino = inode->ino(),
|
||||||
|
.st_mode = inode->mode().mode,
|
||||||
|
.st_nlink = inode->nlink(),
|
||||||
|
.st_uid = inode->uid(),
|
||||||
|
.st_gid = inode->gid(),
|
||||||
|
.st_rdev = inode->rdev(),
|
||||||
|
.st_size = inode->size(),
|
||||||
|
.st_atim = inode->atime(),
|
||||||
|
.st_mtim = inode->mtime(),
|
||||||
|
.st_ctim = inode->ctime(),
|
||||||
|
.st_blksize = inode->blksize(),
|
||||||
|
.st_blocks = inode->blocks(),
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* pipe_ptr = new Pipe(st);
|
||||||
|
if (pipe_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
pipe = BAN::RefPtr<Pipe>::adopt(pipe_ptr);
|
||||||
|
pipe->m_buffer = TRY(ByteRingBuffer::create(s_pipe_buffer_size));
|
||||||
|
pipe->m_named_inode = inode;
|
||||||
|
|
||||||
|
it->value = TRY(pipe->get_weak_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LockGuard _(pipe->m_mutex);
|
||||||
|
|
||||||
|
if (status_flags & O_RDONLY)
|
||||||
|
pipe->m_reading_count++;
|
||||||
|
if (status_flags & O_WRONLY)
|
||||||
|
pipe->m_writing_count++;
|
||||||
|
|
||||||
|
if (status_flags & O_NONBLOCK)
|
||||||
|
{
|
||||||
|
if ((status_flags & O_WRONLY) && pipe->m_writing_count == 0)
|
||||||
|
return BAN::Error::from_errno(ENXIO);
|
||||||
|
return BAN::RefPtr<Inode>(pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& block_value = (status_flags & O_WRONLY) ? pipe->m_reading_count : pipe->m_writing_count;
|
||||||
|
while (block_value == 0)
|
||||||
|
TRY(Thread::current().block_or_eintr_indefinite(pipe->m_thread_blocker, &pipe->m_mutex));
|
||||||
|
return BAN::RefPtr<Inode>(pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::create(uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
const timespec current_time = SystemTimer::get().real_time();
|
||||||
|
const struct stat st {
|
||||||
|
.st_dev = 0, // FIXME
|
||||||
|
.st_ino = 0, // FIXME
|
||||||
|
.st_mode = Mode::IFIFO | Mode::IRUSR | Mode::IWUSR,
|
||||||
|
.st_nlink = 0,
|
||||||
|
.st_uid = uid,
|
||||||
|
.st_gid = gid,
|
||||||
|
.st_rdev = 0, // FIXME
|
||||||
|
.st_size = 0,
|
||||||
|
.st_atim = current_time,
|
||||||
|
.st_mtim = current_time,
|
||||||
|
.st_ctim = current_time,
|
||||||
|
.st_blksize = PAGE_SIZE,
|
||||||
|
.st_blocks = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* pipe_ptr = new Pipe(st);
|
||||||
if (pipe_ptr == nullptr)
|
if (pipe_ptr == nullptr)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
auto pipe = BAN::RefPtr<Pipe>::adopt(pipe_ptr);
|
auto pipe = BAN::RefPtr<Pipe>::adopt(pipe_ptr);
|
||||||
pipe->m_buffer = TRY(ByteRingBuffer::create(s_pipe_buffer_size));
|
pipe->m_buffer = TRY(ByteRingBuffer::create(s_pipe_buffer_size));
|
||||||
|
pipe->m_reading_count++;
|
||||||
|
pipe->m_writing_count++;
|
||||||
return BAN::RefPtr<Inode>(pipe);
|
return BAN::RefPtr<Inode>(pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::Pipe(const Credentials& credentials)
|
Pipe::Pipe(const struct stat& st)
|
||||||
{
|
{
|
||||||
timespec current_time = SystemTimer::get().real_time();
|
m_ino = st.st_ino;
|
||||||
m_atime = current_time;
|
m_mode = st.st_mode;
|
||||||
m_mtime = current_time;
|
m_nlink = st.st_nlink;
|
||||||
m_ctime = current_time;
|
m_uid = st.st_uid;
|
||||||
m_uid = credentials.euid();
|
m_gid = st.st_gid;
|
||||||
m_gid = credentials.egid();
|
m_size = st.st_size;
|
||||||
|
m_atime = st.st_atim;
|
||||||
|
m_mtime = st.st_mtim;
|
||||||
|
m_ctime = st.st_ctim;
|
||||||
|
m_blksize = st.st_blksize;
|
||||||
|
m_blocks = st.st_blocks;
|
||||||
|
m_dev = st.st_dev;
|
||||||
|
m_rdev = st.st_rdev;
|
||||||
|
|
||||||
m_ino = 0; // FIXME
|
m_kind |= InodeKind::PIPE;
|
||||||
m_mode = { Mode::IFIFO | Mode::IRUSR | Mode::IWUSR };
|
}
|
||||||
m_nlink = 1;
|
|
||||||
m_size = 0;
|
Pipe::~Pipe()
|
||||||
m_blksize = 4096;
|
{
|
||||||
m_blocks = 0;
|
if (!m_named_inode)
|
||||||
m_dev = 0; // FIXME
|
return;
|
||||||
m_rdev = 0; // FIXME
|
LockGuard _(s_named_pipe_mutex);
|
||||||
m_kind = InodeKind::PIPE;
|
s_named_pipes.remove(m_named_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pipe::on_clone(int status_flags)
|
void Pipe::on_clone(int status_flags)
|
||||||
@@ -82,6 +173,44 @@ namespace Kernel
|
|||||||
m_thread_blocker.unblock();
|
m_thread_blocker.unblock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Pipe::sync_inode(SyncType type)
|
||||||
|
{
|
||||||
|
if (!m_named_inode)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SyncType::General:
|
||||||
|
break;
|
||||||
|
case SyncType::Mode:
|
||||||
|
TRY(m_named_inode->chmod(m_mode));
|
||||||
|
break;
|
||||||
|
case SyncType::UidGid:
|
||||||
|
TRY(m_named_inode->chown(m_uid, m_gid));
|
||||||
|
break;
|
||||||
|
case SyncType::Times:
|
||||||
|
const timespec times[] { m_atime, m_mtime };
|
||||||
|
TRY(m_named_inode->utimens(times));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mode = m_named_inode->mode().mode;
|
||||||
|
|
||||||
|
m_uid = m_named_inode->uid();
|
||||||
|
m_gid = m_named_inode->gid();
|
||||||
|
|
||||||
|
m_atime = m_named_inode->atime();
|
||||||
|
m_mtime = m_named_inode->mtime();
|
||||||
|
m_ctime = m_named_inode->ctime();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Pipe::sync_data()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<size_t> Pipe::read_impl(off_t, BAN::ByteSpan buffer)
|
BAN::ErrorOr<size_t> Pipe::read_impl(off_t, BAN::ByteSpan buffer)
|
||||||
{
|
{
|
||||||
LockGuard _(m_mutex);
|
LockGuard _(m_mutex);
|
||||||
@@ -134,6 +263,10 @@ namespace Kernel
|
|||||||
return to_copy;
|
return to_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Pipe::truncate_impl(size_t)
|
||||||
|
{
|
||||||
|
return BAN::Error::from_errno(ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Pipe::ioctl_impl(int cmd, void* arg)
|
BAN::ErrorOr<long> Pipe::ioctl_impl(int cmd, void* arg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,41 +92,14 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
if (nlink() > 0)
|
if (nlink() > 0)
|
||||||
{
|
{
|
||||||
sync();
|
write_inode_to_fs();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
free_all_blocks();
|
free_all_blocks();
|
||||||
m_fs.delete_inode(ino());
|
m_fs.delete_inode(ino());
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> TmpInode::chmod_impl(mode_t new_mode)
|
void TmpInode::write_inode_to_fs()
|
||||||
{
|
|
||||||
// FIXME: make this atomic
|
|
||||||
ASSERT(!(new_mode & Inode::Mode::TYPE_MASK));
|
|
||||||
m_mode &= Inode::Mode::TYPE_MASK;
|
|
||||||
m_mode |= new_mode;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> TmpInode::chown_impl(uid_t new_uid, gid_t new_gid)
|
|
||||||
{
|
|
||||||
// FIXME: make this atomic
|
|
||||||
m_uid = new_uid;
|
|
||||||
m_gid = new_gid;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> TmpInode::utimens_impl(const timespec times[2])
|
|
||||||
{
|
|
||||||
// FIXME: make this atomic
|
|
||||||
if (times[0].tv_nsec != UTIME_OMIT)
|
|
||||||
m_atime = times[0];
|
|
||||||
if (times[1].tv_nsec != UTIME_OMIT)
|
|
||||||
m_atime = times[1];
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmpInode::sync()
|
|
||||||
{
|
{
|
||||||
TmpInodeInfo info = {
|
TmpInodeInfo info = {
|
||||||
.mode = m_mode.load(),
|
.mode = m_mode.load(),
|
||||||
@@ -143,6 +116,16 @@ namespace Kernel
|
|||||||
m_fs.write_inode(m_ino, info);
|
m_fs.write_inode(m_ino, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TmpInode::sync_inode(SyncType)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TmpInode::sync_data()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void TmpInode::free_all_blocks()
|
void TmpInode::free_all_blocks()
|
||||||
{
|
{
|
||||||
LockGuard _(m_lock);
|
LockGuard _(m_lock);
|
||||||
@@ -408,7 +391,32 @@ namespace Kernel
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIFO INODE */
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<TmpFIFOInode>> TmpFIFOInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
auto info = create_inode_info(Mode::IFIFO | mode, uid, gid);
|
||||||
|
ino_t ino = TRY(fs.allocate_inode(info));
|
||||||
|
|
||||||
|
auto* inode_ptr = new TmpFIFOInode(fs, ino, info);
|
||||||
|
if (inode_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
return BAN::RefPtr<TmpFIFOInode>::adopt(inode_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TmpFIFOInode::TmpFIFOInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
|
||||||
|
: TmpInode(fs, ino, info)
|
||||||
|
{
|
||||||
|
ASSERT(mode().ififo());
|
||||||
|
}
|
||||||
|
|
||||||
|
TmpFIFOInode::~TmpFIFOInode()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/* SOCKET INODE */
|
/* SOCKET INODE */
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||||
{
|
{
|
||||||
auto info = create_inode_info(Mode::IFSOCK | mode, uid, gid);
|
auto info = create_inode_info(Mode::IFSOCK | mode, uid, gid);
|
||||||
@@ -679,6 +687,9 @@ namespace Kernel
|
|||||||
case Mode::IFLNK:
|
case Mode::IFLNK:
|
||||||
new_inode = TRY(TmpSymlinkInode::create_new(m_fs, mode, uid, gid, ""_sv));
|
new_inode = TRY(TmpSymlinkInode::create_new(m_fs, mode, uid, gid, ""_sv));
|
||||||
break;
|
break;
|
||||||
|
case Mode::IFIFO:
|
||||||
|
new_inode = TRY(TmpFIFOInode::create_new(m_fs, mode, uid, gid));
|
||||||
|
break;
|
||||||
case Mode::IFSOCK:
|
case Mode::IFSOCK:
|
||||||
new_inode = TRY(TmpSocketInode::create_new(m_fs, mode, uid, gid));
|
new_inode = TRY(TmpSocketInode::create_new(m_fs, mode, uid, gid));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ namespace Kernel
|
|||||||
if (!(region->m_shared_data = inode->m_shared_region.lock()))
|
if (!(region->m_shared_data = inode->m_shared_region.lock()))
|
||||||
{
|
{
|
||||||
auto shared_data = TRY(BAN::RefPtr<SharedFileData>::create());
|
auto shared_data = TRY(BAN::RefPtr<SharedFileData>::create());
|
||||||
TRY(shared_data->pages.resize(BAN::Math::div_round_up<size_t>(inode->size(), PAGE_SIZE)));
|
const size_t page_count = BAN::Math::div_round_up<size_t>(inode->size(), PAGE_SIZE);
|
||||||
|
TRY(shared_data->pages.resize(page_count, 0));
|
||||||
|
TRY(shared_data->writers.resize(page_count, 0));
|
||||||
shared_data->inode = inode;
|
shared_data->inode = inode;
|
||||||
inode->m_shared_region = TRY(shared_data->get_weak_ptr());
|
inode->m_shared_region = TRY(shared_data->get_weak_ptr());
|
||||||
region->m_shared_data = BAN::move(shared_data);
|
region->m_shared_data = BAN::move(shared_data);
|
||||||
@@ -59,9 +61,21 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
if (m_vaddr == 0)
|
if (m_vaddr == 0)
|
||||||
return;
|
return;
|
||||||
for (paddr_t dirty_page : m_dirty_pages)
|
|
||||||
if (dirty_page)
|
switch (m_type)
|
||||||
Heap::get().release_page(dirty_page);
|
{
|
||||||
|
case Type::PRIVATE:
|
||||||
|
for (paddr_t dirty_page : m_dirty_pages)
|
||||||
|
if (dirty_page)
|
||||||
|
Heap::get().release_page(dirty_page);
|
||||||
|
break;
|
||||||
|
case Type::SHARED:
|
||||||
|
const size_t page_count = BAN::Math::div_round_up<size_t>(size(), PAGE_SIZE);
|
||||||
|
for (size_t i = 0; i < page_count; i++)
|
||||||
|
if (m_page_table.get_page_flags(m_vaddr + i * PAGE_SIZE) & PageTable::Flags::ReadWrite)
|
||||||
|
BAN::atomic_sub_fetch(m_shared_data->writers[m_offset / PAGE_SIZE + i], 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedFileData::~SharedFileData()
|
SharedFileData::~SharedFileData()
|
||||||
@@ -79,7 +93,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void SharedFileData::sync_no_lock(size_t page_index)
|
void SharedFileData::sync_no_lock(size_t page_index)
|
||||||
{
|
{
|
||||||
if (pages[page_index] == 0)
|
if (pages[page_index] == 0 || BAN::atomic_load(writers[page_index]) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t page_buffer[PAGE_SIZE];
|
uint8_t page_buffer[PAGE_SIZE];
|
||||||
@@ -179,11 +193,12 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const paddr_t paddr = m_shared_data->pages[shared_page_index];
|
|
||||||
auto flags = m_flags;
|
auto flags = m_flags;
|
||||||
if (m_type == Type::PRIVATE)
|
if (m_type == Type::PRIVATE)
|
||||||
flags &= ~PageTable::Flags::ReadWrite;
|
flags &= ~PageTable::Flags::ReadWrite;
|
||||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
if (flags & PageTable::Flags::ReadWrite)
|
||||||
|
BAN::atomic_add_fetch(m_shared_data->writers[shared_page_index], 1);
|
||||||
|
m_page_table.map_page_at(m_shared_data->pages[shared_page_index], vaddr, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ namespace Kernel
|
|||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
while (length < address_len - sizeof(sa_family_t) && sockaddr_un.sun_path[length])
|
while (length < sizeof(sockaddr_un::sun_path) && sockaddr_un.sun_path[length])
|
||||||
length++;
|
length++;
|
||||||
|
if (length >= sizeof(sockaddr_un::sun_path))
|
||||||
|
return BAN::Error::from_errno(ENAMETOOLONG);
|
||||||
|
|
||||||
return BAN::StringView { sockaddr_un.sun_path, length };
|
return BAN::StringView { sockaddr_un.sun_path, length };
|
||||||
}
|
}
|
||||||
@@ -269,7 +271,7 @@ namespace Kernel
|
|||||||
auto parent_file = sun_path.front() == '/'
|
auto parent_file = sun_path.front() == '/'
|
||||||
? TRY(Process::current().root_file().clone())
|
? TRY(Process::current().root_file().clone())
|
||||||
: TRY(Process::current().working_directory().clone());
|
: TRY(Process::current().working_directory().clone());
|
||||||
if (auto ret = Process::current().create_file_or_dir(AT_FDCWD, sun_path.data(), 0755 | S_IFSOCK); ret.is_error())
|
if (auto ret = Process::current().create_file(AT_FDCWD, sun_path.data(), 0755 | Inode::Mode::IFSOCK); ret.is_error())
|
||||||
{
|
{
|
||||||
if (ret.error().get_error_code() == EEXIST)
|
if (ret.error().get_error_code() == EEXIST)
|
||||||
return BAN::Error::from_errno(EADDRINUSE);
|
return BAN::Error::from_errno(EADDRINUSE);
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ namespace Kernel
|
|||||||
|
|
||||||
constexpr int status_mask = O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC | O_ACCMODE;
|
constexpr int status_mask = O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC | O_ACCMODE;
|
||||||
|
|
||||||
|
if (file.inode->mode().ififo())
|
||||||
|
file.inode = TRY(Pipe::open(file.inode, flags & status_mask));
|
||||||
|
|
||||||
LockGuard _(m_mutex);
|
LockGuard _(m_mutex);
|
||||||
|
|
||||||
const int fd = TRY(get_free_fd());
|
const int fd = TRY(get_free_fd());
|
||||||
@@ -214,7 +217,7 @@ namespace Kernel
|
|||||||
|
|
||||||
TRY(get_free_fd_pair(fds));
|
TRY(get_free_fd_pair(fds));
|
||||||
|
|
||||||
auto pipe = TRY(Pipe::create(m_credentials));
|
auto pipe = TRY(Pipe::create(m_credentials.euid(), m_credentials.egid()));
|
||||||
m_open_files[fds[0]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe rd>"_sv), 0, O_RDONLY));
|
m_open_files[fds[0]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe rd>"_sv), 0, O_RDONLY));
|
||||||
m_open_files[fds[1]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe wr>"_sv), 0, O_WRONLY));
|
m_open_files[fds[1]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe wr>"_sv), 0, O_WRONLY));
|
||||||
|
|
||||||
|
|||||||
@@ -439,6 +439,8 @@ namespace Kernel
|
|||||||
.cancel_type = 0,
|
.cancel_type = 0,
|
||||||
.cancel_state = 0,
|
.cancel_state = 0,
|
||||||
.canceled = 0,
|
.canceled = 0,
|
||||||
|
.specific_keys = {},
|
||||||
|
.specific_values = {},
|
||||||
.dtv = { 0, region->vaddr() }
|
.dtv = { 0, region->vaddr() }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1161,26 +1163,27 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> Process::create_file_or_dir(int fd, const char* path, mode_t mode) const
|
BAN::ErrorOr<void> Process::create_file(int fd, const char* path, mode_t mode) const
|
||||||
{
|
{
|
||||||
switch (mode & Inode::Mode::TYPE_MASK)
|
switch (mode & Inode::Mode::TYPE_MASK)
|
||||||
{
|
{
|
||||||
case Inode::Mode::IFREG:
|
case Inode::Mode::IFREG:
|
||||||
case Inode::Mode::IFDIR:
|
|
||||||
case Inode::Mode::IFLNK:
|
case Inode::Mode::IFLNK:
|
||||||
case Inode::Mode::IFIFO:
|
case Inode::Mode::IFIFO:
|
||||||
case Inode::Mode::IFSOCK:
|
case Inode::Mode::IFSOCK:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_EXEC | O_WRONLY));
|
struct uid_gid_t { uid_t uid; gid_t gid; };
|
||||||
|
const auto [uid, gid] = ({
|
||||||
|
LockGuard _(m_process_lock);
|
||||||
|
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
|
||||||
|
});
|
||||||
|
|
||||||
if (Inode::Mode(mode).ifdir())
|
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_EXEC | O_WRONLY));
|
||||||
TRY(parent.inode->create_directory(file_name, mode, m_credentials.euid(), parent.inode->gid()));
|
TRY(parent.inode->create_file(file_name, mode, uid, gid));
|
||||||
else
|
|
||||||
TRY(parent.inode->create_file(file_name, mode, m_credentials.euid(), parent.inode->gid()));
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -1299,26 +1302,40 @@ namespace Kernel
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Process::sys_create_dir(const char* user_path, mode_t mode)
|
BAN::ErrorOr<long> Process::sys_mkdirat(int fd, const char* user_path, mode_t mode)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
TRY(read_string_from_user(user_path, path, PATH_MAX));
|
TRY(read_string_from_user(user_path, path, PATH_MAX));
|
||||||
|
|
||||||
uid_t uid;
|
struct uid_gid_t { uid_t uid; gid_t gid; };
|
||||||
gid_t gid;
|
const auto [uid, gid] = ({
|
||||||
|
|
||||||
{
|
|
||||||
LockGuard _(m_process_lock);
|
LockGuard _(m_process_lock);
|
||||||
uid = m_credentials.euid();
|
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
|
||||||
gid = m_credentials.egid();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
auto [parent, file_name] = TRY(find_parent_file(AT_FDCWD, path, O_WRONLY));
|
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_WRONLY));
|
||||||
TRY(parent.inode->create_directory(file_name, (mode & 0777) | Inode::Mode::IFDIR, uid, gid));
|
TRY(parent.inode->create_directory(file_name, (mode & 0777) | Inode::Mode::IFDIR, uid, gid));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<long> Process::sys_mkfifoat(int fd, const char* user_path, mode_t mode)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
TRY(read_string_from_user(user_path, path, PATH_MAX));
|
||||||
|
|
||||||
|
struct uid_gid_t { uid_t uid; gid_t gid; };
|
||||||
|
const auto [uid, gid] = ({
|
||||||
|
LockGuard _(m_process_lock);
|
||||||
|
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
|
||||||
|
});
|
||||||
|
|
||||||
|
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_WRONLY));
|
||||||
|
TRY(parent.inode->create_file(file_name, (mode & 0777) | Inode::Mode::IFIFO, uid, gid));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Process::sys_hardlinkat(int fd1, const char* user_path1, int fd2, const char* user_path2, int flag)
|
BAN::ErrorOr<long> Process::sys_hardlinkat(int fd1, const char* user_path1, int fd2, const char* user_path2, int flag)
|
||||||
{
|
{
|
||||||
char path1[PATH_MAX];
|
char path1[PATH_MAX];
|
||||||
@@ -1374,7 +1391,6 @@ namespace Kernel
|
|||||||
if (user_path != nullptr)
|
if (user_path != nullptr)
|
||||||
TRY(read_string_from_user(user_path, path, PATH_MAX));
|
TRY(read_string_from_user(user_path, path, PATH_MAX));
|
||||||
|
|
||||||
|
|
||||||
auto [parent, file_name] = TRY(find_parent_file(fd, user_path ? path : nullptr, O_WRONLY));
|
auto [parent, file_name] = TRY(find_parent_file(fd, user_path ? path : nullptr, O_WRONLY));
|
||||||
|
|
||||||
const auto inode = TRY(parent.inode->find_inode(file_name));
|
const auto inode = TRY(parent.inode->find_inode(file_name));
|
||||||
@@ -1416,15 +1432,11 @@ namespace Kernel
|
|||||||
TRY(read_string_from_user(user_path1, path1, PATH_MAX));
|
TRY(read_string_from_user(user_path1, path1, PATH_MAX));
|
||||||
|
|
||||||
char path2[PATH_MAX];
|
char path2[PATH_MAX];
|
||||||
if (user_path2 != nullptr)
|
TRY(read_string_from_user(user_path2, path2, PATH_MAX));
|
||||||
TRY(read_string_from_user(user_path2, path2, PATH_MAX));
|
|
||||||
|
|
||||||
if (!find_file(fd, user_path2 ? path2 : nullptr, O_NOFOLLOW).is_error())
|
TRY(create_file(fd, path2, 0777 | Inode::Mode::IFLNK));
|
||||||
return BAN::Error::from_errno(EEXIST);
|
|
||||||
|
|
||||||
TRY(create_file_or_dir(fd, user_path2 ? path2 : nullptr, 0777 | Inode::Mode::IFLNK));
|
auto symlink = TRY(find_file(fd, path2, O_NOFOLLOW));
|
||||||
|
|
||||||
auto symlink = TRY(find_file(fd, user_path2 ? path2 : nullptr, O_NOFOLLOW));
|
|
||||||
TRY(symlink.inode->set_link_target(path1));
|
TRY(symlink.inode->set_link_target(path1));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1523,11 +1535,7 @@ namespace Kernel
|
|||||||
if (flag == AT_SYMLINK_NOFOLLOW)
|
if (flag == AT_SYMLINK_NOFOLLOW)
|
||||||
flag = O_NOFOLLOW;
|
flag = O_NOFOLLOW;
|
||||||
|
|
||||||
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
|
const timespec current_ts = SystemTimer::get().real_time();
|
||||||
const timespec current_ts = {
|
|
||||||
.tv_sec = static_cast<time_t>(current_ns / 1'000'000),
|
|
||||||
.tv_nsec = static_cast<long>(current_ns % 1'000'000),
|
|
||||||
};
|
|
||||||
|
|
||||||
timespec times[2];
|
timespec times[2];
|
||||||
if (user_times == nullptr)
|
if (user_times == nullptr)
|
||||||
@@ -1548,7 +1556,6 @@ namespace Kernel
|
|||||||
else if (times[i].tv_nsec < 0 || times[i].tv_nsec >= 1'000'000'000)
|
else if (times[i].tv_nsec < 0 || times[i].tv_nsec >= 1'000'000'000)
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
|
if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
|
||||||
@@ -1569,6 +1576,11 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (times[0].tv_nsec == UTIME_OMIT)
|
||||||
|
times[0] = inode->atime();
|
||||||
|
if (times[1].tv_nsec == UTIME_OMIT)
|
||||||
|
times[1] = inode->mtime();
|
||||||
|
|
||||||
TRY(inode->utimens(times));
|
TRY(inode->utimens(times));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1763,12 +1775,13 @@ namespace Kernel
|
|||||||
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
|
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
|
||||||
|
|
||||||
BAN::Vector<MemoryRegion*> regions;
|
BAN::Vector<MemoryRegion*> regions;
|
||||||
|
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
|
||||||
|
|
||||||
BAN::ScopeGuard _([®ions] {
|
BAN::ScopeGuard _([®ions] {
|
||||||
for (auto* region : regions)
|
for (auto* region : regions)
|
||||||
region->unpin();
|
region->unpin();
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: this can leak memory if push to regions fails but pinning succeeded
|
|
||||||
if (message.msg_name)
|
if (message.msg_name)
|
||||||
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true))));
|
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true))));
|
||||||
if (message.msg_control)
|
if (message.msg_control)
|
||||||
@@ -1776,6 +1789,7 @@ namespace Kernel
|
|||||||
if (message.msg_iov)
|
if (message.msg_iov)
|
||||||
{
|
{
|
||||||
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), true))));
|
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), true))));
|
||||||
|
TRY(regions.reserve(regions.size() + message.msg_iovlen));
|
||||||
for (int i = 0; i < message.msg_iovlen; i++)
|
for (int i = 0; i < message.msg_iovlen; i++)
|
||||||
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, true))));
|
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, true))));
|
||||||
}
|
}
|
||||||
@@ -1793,6 +1807,8 @@ namespace Kernel
|
|||||||
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
|
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
|
||||||
|
|
||||||
BAN::Vector<MemoryRegion*> regions;
|
BAN::Vector<MemoryRegion*> regions;
|
||||||
|
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
|
||||||
|
|
||||||
BAN::ScopeGuard _([®ions] {
|
BAN::ScopeGuard _([®ions] {
|
||||||
for (auto* region : regions)
|
for (auto* region : regions)
|
||||||
region->unpin();
|
region->unpin();
|
||||||
@@ -1805,6 +1821,7 @@ namespace Kernel
|
|||||||
if (message.msg_iov)
|
if (message.msg_iov)
|
||||||
{
|
{
|
||||||
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), false))));
|
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), false))));
|
||||||
|
TRY(regions.reserve(regions.size() + message.msg_iovlen));
|
||||||
for (int i = 0; i < message.msg_iovlen; i++)
|
for (int i = 0; i < message.msg_iovlen; i++)
|
||||||
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, false))));
|
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, false))));
|
||||||
}
|
}
|
||||||
@@ -3332,20 +3349,24 @@ namespace Kernel
|
|||||||
if (m_threads.size() == 1)
|
if (m_threads.size() == 1)
|
||||||
return sys_exit(0);
|
return sys_exit(0);
|
||||||
|
|
||||||
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
|
auto& thread = Thread::current();
|
||||||
|
if (!thread.is_detached())
|
||||||
|
{
|
||||||
|
TRY(m_exited_pthreads.emplace_back(thread.tid(), value));
|
||||||
|
m_pthread_exit_blocker.unblock();
|
||||||
|
}
|
||||||
|
|
||||||
m_pthread_exit_blocker.unblock();
|
|
||||||
m_process_lock.unlock();
|
m_process_lock.unlock();
|
||||||
Thread::current().on_exit();
|
thread.on_exit();
|
||||||
|
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t thread, void** user_value)
|
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t tid, void** user_value)
|
||||||
{
|
{
|
||||||
LockGuard _(m_process_lock);
|
LockGuard _(m_process_lock);
|
||||||
|
|
||||||
if (thread == Thread::current().tid())
|
if (tid == Thread::current().tid())
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
const auto check_thread =
|
const auto check_thread =
|
||||||
@@ -3353,13 +3374,10 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_exited_pthreads.size(); i++)
|
for (size_t i = 0; i < m_exited_pthreads.size(); i++)
|
||||||
{
|
{
|
||||||
if (m_exited_pthreads[i].thread != thread)
|
if (m_exited_pthreads[i].thread != tid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
void* ret = m_exited_pthreads[i].value;
|
void* ret = m_exited_pthreads[i].value;
|
||||||
|
|
||||||
m_exited_pthreads.remove(i);
|
m_exited_pthreads.remove(i);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3377,10 +3395,18 @@ namespace Kernel
|
|||||||
|
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto* _thread : m_threads)
|
bool joinable = false;
|
||||||
if (_thread->tid() == thread)
|
for (auto* thread : m_threads)
|
||||||
found = true;
|
{
|
||||||
|
if (thread->tid() != tid)
|
||||||
|
continue;
|
||||||
|
found = true;
|
||||||
|
joinable = !thread->is_detached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
|
return BAN::Error::from_errno(ESRCH);
|
||||||
|
if (!joinable)
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3423,6 +3449,24 @@ namespace Kernel
|
|||||||
return BAN::Error::from_errno(ESRCH);
|
return BAN::Error::from_errno(ESRCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<long> Process::sys_pthread_detach(pthread_t tid)
|
||||||
|
{
|
||||||
|
LockGuard _(m_process_lock);
|
||||||
|
|
||||||
|
for (auto* thread : m_threads)
|
||||||
|
{
|
||||||
|
if (thread->tid() != tid)
|
||||||
|
continue;
|
||||||
|
if (thread->is_detached())
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
thread->detach();
|
||||||
|
m_pthread_exit_blocker.unblock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BAN::Error::from_errno(ESRCH);
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Process::sys_setuid(uid_t uid)
|
BAN::ErrorOr<long> Process::sys_setuid(uid_t uid)
|
||||||
{
|
{
|
||||||
if (uid < 0 || uid >= 1'000'000'000)
|
if (uid < 0 || uid >= 1'000'000'000)
|
||||||
@@ -3884,6 +3928,8 @@ namespace Kernel
|
|||||||
region->pin();
|
region->pin();
|
||||||
return region.ptr();
|
return region.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_and_pin_pointer_access_with_allocation:
|
validate_and_pin_pointer_access_with_allocation:
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ namespace Kernel
|
|||||||
case SYS_RECVMSG:
|
case SYS_RECVMSG:
|
||||||
case SYS_SENDMSG:
|
case SYS_SENDMSG:
|
||||||
case SYS_FLOCK:
|
case SYS_FLOCK:
|
||||||
|
case SYS_FUTEX:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -155,22 +155,6 @@ namespace Kernel
|
|||||||
DevFileSystem::get().add_inode("tty", MUST(DevTTY::create(0666, 0, 0)));
|
DevFileSystem::get().add_inode("tty", MUST(DevTTY::create(0666, 0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> TTY::chmod_impl(mode_t mode)
|
|
||||||
{
|
|
||||||
// FIXME: make this atomic
|
|
||||||
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
|
|
||||||
m_mode &= Inode::Mode::TYPE_MASK;
|
|
||||||
m_mode |= mode;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> TTY::chown_impl(uid_t uid, gid_t gid)
|
|
||||||
{
|
|
||||||
m_uid = uid;
|
|
||||||
m_gid = gid;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TTY::update_winsize(unsigned short cols, unsigned short rows)
|
void TTY::update_winsize(unsigned short cols, unsigned short rows)
|
||||||
{
|
{
|
||||||
// FIXME: make this atomic
|
// FIXME: make this atomic
|
||||||
@@ -424,7 +408,7 @@ namespace Kernel
|
|||||||
|
|
||||||
const auto termios = get_termios();
|
const auto termios = get_termios();
|
||||||
|
|
||||||
SpinLockGuard _1(m_write_lock);
|
LockGuard _(m_write_lock);
|
||||||
if (termios.c_oflag & OPOST)
|
if (termios.c_oflag & OPOST)
|
||||||
{
|
{
|
||||||
if ((termios.c_oflag & ONLCR) && ch == NL)
|
if ((termios.c_oflag & ONLCR) && ch == NL)
|
||||||
@@ -471,15 +455,13 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<size_t> TTY::write_impl(off_t, BAN::ConstByteSpan buffer)
|
BAN::ErrorOr<size_t> TTY::write_impl(off_t, BAN::ConstByteSpan buffer)
|
||||||
{
|
{
|
||||||
SpinLockGuard write_guard(m_write_lock);
|
LockGuard write_guard(m_write_lock);
|
||||||
|
|
||||||
while (!can_write())
|
while (!can_write())
|
||||||
{
|
{
|
||||||
if (master_has_closed())
|
if (master_has_closed())
|
||||||
return BAN::Error::from_errno(EIO);
|
return BAN::Error::from_errno(EIO);
|
||||||
|
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &m_write_lock));
|
||||||
SpinLockGuardAsMutex smutex(write_guard);
|
|
||||||
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &smutex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
@@ -497,7 +479,7 @@ namespace Kernel
|
|||||||
void TTY::putchar_current(uint8_t ch)
|
void TTY::putchar_current(uint8_t ch)
|
||||||
{
|
{
|
||||||
ASSERT(s_tty);
|
ASSERT(s_tty);
|
||||||
SpinLockGuard _(s_tty->m_write_lock);
|
LockGuard _(s_tty->m_write_lock);
|
||||||
s_tty->putchar(ch);
|
s_tty->putchar(ch);
|
||||||
s_tty->after_write();
|
s_tty->after_write();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::clear()
|
void VirtualTTY::clear()
|
||||||
{
|
{
|
||||||
SpinLockGuard _(m_write_lock);
|
LockGuard _(m_write_lock);
|
||||||
for (uint32_t i = 0; i < m_width * m_height; i++)
|
for (uint32_t i = 0; i < m_width * m_height; i++)
|
||||||
m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
|
m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
|
||||||
m_terminal_driver->clear(m_background);
|
m_terminal_driver->clear(m_background);
|
||||||
@@ -73,7 +73,7 @@ namespace Kernel
|
|||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
{
|
{
|
||||||
SpinLockGuard _(m_write_lock);
|
LockGuard _(m_write_lock);
|
||||||
|
|
||||||
TRY(m_terminal_driver->set_font(BAN::move(font)));
|
TRY(m_terminal_driver->set_font(BAN::move(font)));
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::reset_ansi()
|
void VirtualTTY::reset_ansi()
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
m_ansi_state = {
|
m_ansi_state = {
|
||||||
.nums = { -1, -1, -1, -1, -1 },
|
.nums = { -1, -1, -1, -1, -1 },
|
||||||
.index = 0,
|
.index = 0,
|
||||||
@@ -121,7 +121,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::handle_ansi_csi_color(uint8_t value)
|
void VirtualTTY::handle_ansi_csi_color(uint8_t value)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@@ -201,7 +201,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::handle_ansi_csi(uint8_t ch)
|
void VirtualTTY::handle_ansi_csi(uint8_t ch)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
@@ -446,7 +446,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y)
|
void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
ASSERT(x < m_width && y < m_height);
|
ASSERT(x < m_width && y < m_height);
|
||||||
const auto& cell = m_buffer[y * m_width + x];
|
const auto& cell = m_buffer[y * m_width + x];
|
||||||
m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background);
|
m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background);
|
||||||
@@ -454,7 +454,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y)
|
void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
ASSERT(x < m_width && y < m_height);
|
ASSERT(x < m_width && y < m_height);
|
||||||
auto& cell = m_buffer[y * m_width + x];
|
auto& cell = m_buffer[y * m_width + x];
|
||||||
cell.codepoint = codepoint;
|
cell.codepoint = codepoint;
|
||||||
@@ -488,7 +488,7 @@ namespace Kernel
|
|||||||
|
|
||||||
void VirtualTTY::putcodepoint(uint32_t codepoint)
|
void VirtualTTY::putcodepoint(uint32_t codepoint)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
|
|
||||||
switch (codepoint)
|
switch (codepoint)
|
||||||
{
|
{
|
||||||
@@ -534,7 +534,7 @@ namespace Kernel
|
|||||||
|
|
||||||
bool VirtualTTY::putchar_impl(uint8_t ch)
|
bool VirtualTTY::putchar_impl(uint8_t ch)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.is_locked_by_current_thread());
|
||||||
|
|
||||||
uint32_t codepoint = ch;
|
uint32_t codepoint = ch;
|
||||||
|
|
||||||
|
|||||||
14
userspace/libraries/LibC/include/bits/types/pthread_key_t.h
Normal file
14
userspace/libraries/LibC/include/bits/types/pthread_key_t.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _BITS_TYPES_PTHREAD_KEY_H
|
||||||
|
#define _BITS_TYPES_PTHREAD_KEY_H 1
|
||||||
|
|
||||||
|
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pthread.h.html
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef unsigned pthread_key_t;
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,14 +8,13 @@
|
|||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
#include <bits/types/pthread_attr_t.h>
|
#include <bits/types/pthread_attr_t.h>
|
||||||
|
#include <bits/types/pthread_key_t.h>
|
||||||
#include <bits/types/pthread_t.h>
|
#include <bits/types/pthread_t.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef int pthread_once_t;
|
typedef int pthread_once_t;
|
||||||
|
|
||||||
typedef unsigned pthread_key_t;
|
|
||||||
|
|
||||||
typedef pthread_t pthread_spinlock_t;
|
typedef pthread_t pthread_spinlock_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ __BEGIN_DECLS
|
|||||||
#define __need_size_t
|
#define __need_size_t
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <bits/types/pthread_key_t.h>
|
||||||
#include <bits/types/pthread_t.h>
|
#include <bits/types/pthread_t.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct _pthread_cleanup_t
|
typedef struct _pthread_cleanup_t
|
||||||
@@ -44,6 +46,8 @@ struct uthread
|
|||||||
int cancel_type;
|
int cancel_type;
|
||||||
int cancel_state;
|
int cancel_state;
|
||||||
volatile int canceled;
|
volatile int canceled;
|
||||||
|
pthread_key_t specific_keys[PTHREAD_KEYS_MAX];
|
||||||
|
void* specific_values[PTHREAD_KEYS_MAX];
|
||||||
// FIXME: make this dynamic
|
// FIXME: make this dynamic
|
||||||
uintptr_t dtv[1 + 256];
|
uintptr_t dtv[1 + 256];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ __BEGIN_DECLS
|
|||||||
O(SYS_TTY_CTRL, tty_ctrl) \
|
O(SYS_TTY_CTRL, tty_ctrl) \
|
||||||
O(SYS_POWEROFF, poweroff) \
|
O(SYS_POWEROFF, poweroff) \
|
||||||
O(SYS_FCHMODAT, fchmodat) \
|
O(SYS_FCHMODAT, fchmodat) \
|
||||||
O(SYS_CREATE_DIR, create_dir) \
|
O(SYS_MKDIRAT, mkdirat) \
|
||||||
|
O(SYS_MKFIFOAT, mkfifoat) \
|
||||||
O(SYS_UNLINKAT, unlinkat) \
|
O(SYS_UNLINKAT, unlinkat) \
|
||||||
O(SYS_READLINKAT, readlinkat) \
|
O(SYS_READLINKAT, readlinkat) \
|
||||||
O(SYS_MSYNC, msync) \
|
O(SYS_MSYNC, msync) \
|
||||||
@@ -105,6 +106,7 @@ __BEGIN_DECLS
|
|||||||
O(SYS_PTHREAD_JOIN, pthread_join) \
|
O(SYS_PTHREAD_JOIN, pthread_join) \
|
||||||
O(SYS_PTHREAD_SELF, pthread_self) \
|
O(SYS_PTHREAD_SELF, pthread_self) \
|
||||||
O(SYS_PTHREAD_KILL, pthread_kill) \
|
O(SYS_PTHREAD_KILL, pthread_kill) \
|
||||||
|
O(SYS_PTHREAD_DETACH, pthread_detach) \
|
||||||
O(SYS_EPOLL_CREATE1, epoll_create1) \
|
O(SYS_EPOLL_CREATE1, epoll_create1) \
|
||||||
O(SYS_EPOLL_CTL, epoll_ctl) \
|
O(SYS_EPOLL_CTL, epoll_ctl) \
|
||||||
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \
|
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \
|
||||||
|
|||||||
@@ -612,16 +612,17 @@ long syscall(long syscall, ...);
|
|||||||
#include <kernel/API/Syscall.h>
|
#include <kernel/API/Syscall.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#define _syscall(...) ({ \
|
#define _syscall(...) ({ \
|
||||||
long _ret = -ERESTART; \
|
long _ret; \
|
||||||
while (_ret == -ERESTART) \
|
do { \
|
||||||
_ret = _kas_syscall(__VA_ARGS__); \
|
_ret = _kas_syscall(__VA_ARGS__); \
|
||||||
if (_ret < 0) { \
|
} while (__builtin_expect(_ret == -ERESTART, 0)); \
|
||||||
|
if (__builtin_expect(_ret < 0, 0)) { \
|
||||||
errno = -_ret; \
|
errno = -_ret; \
|
||||||
_ret = -1; \
|
_ret = -1; \
|
||||||
} \
|
} \
|
||||||
_ret; \
|
_ret; \
|
||||||
})
|
})
|
||||||
#define syscall _syscall
|
#define syscall(...) _syscall(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern char** environ;
|
extern char** environ;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
struct pthread_trampoline_info_t
|
struct pthread_trampoline_info_t
|
||||||
{
|
{
|
||||||
struct uthread* uthread;
|
struct uthread* uthread;
|
||||||
|
bool detached;
|
||||||
void* (*start_routine)(void*);
|
void* (*start_routine)(void*);
|
||||||
void* arg;
|
void* arg;
|
||||||
};
|
};
|
||||||
@@ -71,6 +72,8 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
|
|||||||
#endif
|
#endif
|
||||||
free(arg);
|
free(arg);
|
||||||
signal(SIGCANCEL, &_pthread_cancel_handler);
|
signal(SIGCANCEL, &_pthread_cancel_handler);
|
||||||
|
if (info.detached)
|
||||||
|
pthread_detach(info.uthread->id);
|
||||||
pthread_exit(info.start_routine(info.arg));
|
pthread_exit(info.start_routine(info.arg));
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -137,12 +140,6 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
|
|||||||
uthread->cleanup_stack = cleanup;
|
uthread->cleanup_stack = cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
static thread_local struct
|
|
||||||
{
|
|
||||||
void* value;
|
|
||||||
pthread_key_t key;
|
|
||||||
} s_pthread_key_values[PTHREAD_KEYS_MAX] {};
|
|
||||||
|
|
||||||
static pthread_key_t s_pthread_key_current = 1;
|
static pthread_key_t s_pthread_key_current = 1;
|
||||||
static pthread_key_t s_pthread_key_map[PTHREAD_KEYS_MAX] {};
|
static pthread_key_t s_pthread_key_map[PTHREAD_KEYS_MAX] {};
|
||||||
static void (*s_pthread_key_destructors[PTHREAD_KEYS_MAX])(void*) {};
|
static void (*s_pthread_key_destructors[PTHREAD_KEYS_MAX])(void*) {};
|
||||||
@@ -190,17 +187,19 @@ void* pthread_getspecific(pthread_key_t key)
|
|||||||
{
|
{
|
||||||
void* ret = nullptr;
|
void* ret = nullptr;
|
||||||
|
|
||||||
|
auto* uthread = _get_uthread();
|
||||||
|
|
||||||
pthread_spin_lock(&s_pthread_key_lock);
|
pthread_spin_lock(&s_pthread_key_lock);
|
||||||
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
|
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
|
||||||
{
|
{
|
||||||
if (s_pthread_key_map[i] != key)
|
if (s_pthread_key_map[i] != key)
|
||||||
continue;
|
continue;
|
||||||
if (s_pthread_key_values[i].key != key)
|
if (uthread->specific_keys[i] != key)
|
||||||
{
|
{
|
||||||
s_pthread_key_values[i].key = key;
|
uthread->specific_keys[i] = key;
|
||||||
s_pthread_key_values[i].value = nullptr;
|
uthread->specific_values[i] = nullptr;
|
||||||
}
|
}
|
||||||
ret = s_pthread_key_values[i].value;
|
ret = uthread->specific_values[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&s_pthread_key_lock);
|
pthread_spin_unlock(&s_pthread_key_lock);
|
||||||
@@ -212,14 +211,16 @@ int pthread_setspecific(pthread_key_t key, const void* value)
|
|||||||
{
|
{
|
||||||
int ret = EINVAL;
|
int ret = EINVAL;
|
||||||
|
|
||||||
|
auto* uthread = _get_uthread();
|
||||||
|
|
||||||
pthread_spin_lock(&s_pthread_key_lock);
|
pthread_spin_lock(&s_pthread_key_lock);
|
||||||
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
|
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
|
||||||
{
|
{
|
||||||
if (s_pthread_key_map[i] != key)
|
if (s_pthread_key_map[i] != key)
|
||||||
continue;
|
continue;
|
||||||
if (s_pthread_key_values[i].key != key)
|
if (uthread->specific_keys[i] != key)
|
||||||
s_pthread_key_values[i].key = key;
|
uthread->specific_keys[i] = key;
|
||||||
s_pthread_key_values[i].value = const_cast<void*>(value);
|
uthread->specific_values[i] = const_cast<void*>(value);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -259,8 +260,6 @@ int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate)
|
|||||||
switch (detachstate)
|
switch (detachstate)
|
||||||
{
|
{
|
||||||
case PTHREAD_CREATE_DETACHED:
|
case PTHREAD_CREATE_DETACHED:
|
||||||
dwarnln("TODO: pthread_attr_setdetachstate");
|
|
||||||
return ENOTSUP;
|
|
||||||
case PTHREAD_CREATE_JOINABLE:
|
case PTHREAD_CREATE_JOINABLE:
|
||||||
attr->detachstate = detachstate;
|
attr->detachstate = detachstate;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -388,6 +387,7 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
|
|||||||
|
|
||||||
*info = {
|
*info = {
|
||||||
.uthread = nullptr,
|
.uthread = nullptr,
|
||||||
|
.detached = attr ? (attr->detachstate == PTHREAD_CREATE_DETACHED) : false,
|
||||||
.start_routine = start_routine,
|
.start_routine = start_routine,
|
||||||
.arg = arg,
|
.arg = arg,
|
||||||
};
|
};
|
||||||
@@ -419,6 +419,8 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
|
|||||||
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
||||||
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
||||||
.canceled = 0,
|
.canceled = 0,
|
||||||
|
.specific_keys = {},
|
||||||
|
.specific_values = {},
|
||||||
.dtv = { self->dtv[0] }
|
.dtv = { self->dtv[0] }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -448,9 +450,7 @@ pthread_create_error:
|
|||||||
|
|
||||||
int pthread_detach(pthread_t thread)
|
int pthread_detach(pthread_t thread)
|
||||||
{
|
{
|
||||||
(void)thread;
|
return syscall(SYS_PTHREAD_DETACH, thread);
|
||||||
dwarnln("TODO: pthread_detach");
|
|
||||||
return ENOTSUP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pthread_exit(void* value_ptr)
|
void pthread_exit(void* value_ptr)
|
||||||
@@ -468,10 +468,11 @@ void pthread_exit(void* value_ptr)
|
|||||||
void* value = nullptr;
|
void* value = nullptr;
|
||||||
|
|
||||||
pthread_spin_lock(&s_pthread_key_lock);
|
pthread_spin_lock(&s_pthread_key_lock);
|
||||||
if (s_pthread_key_map[i] && s_pthread_key_values[i].key == s_pthread_key_map[i])
|
if (s_pthread_key_map[i] && uthread->specific_keys[i] == s_pthread_key_map[i])
|
||||||
{
|
{
|
||||||
destructor = s_pthread_key_destructors[i];
|
destructor = s_pthread_key_destructors[i];
|
||||||
value = s_pthread_key_values[i].value;
|
value = uthread->specific_values[i];
|
||||||
|
uthread->specific_values[i] = nullptr;
|
||||||
}
|
}
|
||||||
pthread_spin_unlock(&s_pthread_key_lock);
|
pthread_spin_unlock(&s_pthread_key_lock);
|
||||||
|
|
||||||
|
|||||||
@@ -52,28 +52,27 @@ mode_t umask(mode_t cmask)
|
|||||||
|
|
||||||
int mkdir(const char* path, mode_t mode)
|
int mkdir(const char* path, mode_t mode)
|
||||||
{
|
{
|
||||||
return syscall(SYS_CREATE_DIR, path, __UMASKED_MODE(mode));
|
return mkdirat(AT_FDCWD, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mkdirat(int fd, const char* path, mode_t mode)
|
||||||
|
{
|
||||||
|
return syscall(SYS_MKDIRAT, fd, path, __UMASKED_MODE(mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
int mkfifo(const char* path, mode_t mode)
|
int mkfifo(const char* path, mode_t mode)
|
||||||
{
|
{
|
||||||
(void)path; (void)mode;
|
return mkfifoat(AT_FDCWD, path, mode);
|
||||||
dwarnln("TODO: mkfifo");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mkfifoat(int fd, const char* path, mode_t mode)
|
int mkfifoat(int fd, const char* path, mode_t mode)
|
||||||
{
|
{
|
||||||
(void)fd; (void)path; (void)mode;
|
return syscall(SYS_MKFIFOAT, fd, path, __UMASKED_MODE(mode));
|
||||||
dwarnln("TODO: mkfifoat");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mknod(const char* path, mode_t mode, dev_t dev)
|
int mknod(const char* path, mode_t mode, dev_t dev)
|
||||||
{
|
{
|
||||||
(void)path; (void)mode; (void)dev;
|
return mknodat(AT_FDCWD, path, mode, dev);
|
||||||
dwarnln("TODO: mknod");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mknodat(int fd, const char* path, mode_t mode, dev_t dev)
|
int mknodat(int fd, const char* path, mode_t mode, dev_t dev)
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
|
|||||||
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
||||||
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
||||||
.canceled = false,
|
.canceled = false,
|
||||||
|
.specific_keys = {},
|
||||||
|
.specific_values = {},
|
||||||
.dtv = { 0 },
|
.dtv = { 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -270,7 +272,7 @@ long syscall(long syscall, ...)
|
|||||||
|
|
||||||
return _syscall(syscall, arg1, arg2, arg3, arg4, arg5);
|
return _syscall(syscall, arg1, arg2, arg3, arg4, arg5);
|
||||||
}
|
}
|
||||||
#define syscall _syscall
|
#define syscall(...) _syscall(__VA_ARGS__)
|
||||||
|
|
||||||
int close(int fd)
|
int close(int fd)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ set(USERSPACE_PROGRAMS
|
|||||||
sync
|
sync
|
||||||
tee
|
tee
|
||||||
test
|
test
|
||||||
|
tr
|
||||||
true
|
true
|
||||||
TaskBar
|
TaskBar
|
||||||
Terminal
|
Terminal
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@ static void initialize_tls(MasterTLS master_tls)
|
|||||||
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
.cancel_type = PTHREAD_CANCEL_DEFERRED,
|
||||||
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
.cancel_state = PTHREAD_CANCEL_ENABLE,
|
||||||
.canceled = false,
|
.canceled = false,
|
||||||
|
.specific_keys = {},
|
||||||
|
.specific_values = {},
|
||||||
.dtv = {},
|
.dtv = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ int main(int argc, char** argv)
|
|||||||
{ "pin", required_argument, nullptr, 'p' },
|
{ "pin", required_argument, nullptr, 'p' },
|
||||||
{ "volume", required_argument, nullptr, 'v' },
|
{ "volume", required_argument, nullptr, 'v' },
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "ld:p:v:h", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "ld:p:v:h", long_options, nullptr);
|
||||||
@@ -125,16 +126,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
case 'h':
|
|
||||||
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
|
|
||||||
fprintf(stderr, " control the audio server\n");
|
|
||||||
fprintf(stderr, "OPTIONS:\n");
|
|
||||||
fprintf(stderr, " -l, --list list devices and their pins\n");
|
|
||||||
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
|
||||||
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
|
||||||
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
|
|
||||||
fprintf(stderr, " -h, --help show this message and exit\n");
|
|
||||||
return 0;
|
|
||||||
case 'l':
|
case 'l':
|
||||||
list = true;
|
list = true;
|
||||||
break;
|
break;
|
||||||
@@ -149,8 +140,17 @@ int main(int argc, char** argv)
|
|||||||
volume_rel = (optarg[0] == '-');
|
volume_rel = (optarg[0] == '-');
|
||||||
volume = parse_u32_or_exit(optarg + volume_rel.has_value());
|
volume = parse_u32_or_exit(optarg + volume_rel.has_value());
|
||||||
break;
|
break;
|
||||||
case '?':
|
case 'h':
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
|
||||||
|
fprintf(stderr, " control the audio server\n");
|
||||||
|
fprintf(stderr, "OPTIONS:\n");
|
||||||
|
fprintf(stderr, " -l, --list list devices and their pins\n");
|
||||||
|
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
||||||
|
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
||||||
|
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
|
||||||
|
fprintf(stderr, " -h, --help show this message and exit\n");
|
||||||
|
return 0;
|
||||||
|
case ':': case '?':
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ int main(int argc, char** argv)
|
|||||||
static option long_options[] {
|
static option long_options[] {
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
{ "recursive", no_argument, nullptr, 'r' },
|
{ "recursive", no_argument, nullptr, 'r' },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "hr", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "hr", long_options, nullptr);
|
||||||
@@ -173,17 +174,16 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
|
case 'r':
|
||||||
|
recursive = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
printf("usage: %s [OPTIONS]... SOURCE... DEST\n", argv[0]);
|
printf("usage: %s [OPTIONS]... SOURCE... DEST\n", argv[0]);
|
||||||
printf("Copies files SOURCE... to DEST\n");
|
printf("Copies files SOURCE... to DEST\n");
|
||||||
printf("OPTIONS:\n");
|
printf("OPTIONS:\n");
|
||||||
printf(" -h, --help Show this message and exit\n");
|
printf(" -h, --help Show this message and exit\n");
|
||||||
return 0;
|
return 0;
|
||||||
case 'r':
|
case ':': case '?':
|
||||||
recursive = true;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ int main(int argc, char* argv[])
|
|||||||
static option long_options[] {
|
static option long_options[] {
|
||||||
{ "zero", no_argument, nullptr, 'z' },
|
{ "zero", no_argument, nullptr, 'z' },
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "zh", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "zh", long_options, nullptr);
|
||||||
@@ -23,17 +24,13 @@ int main(int argc, char* argv[])
|
|||||||
zero = true;
|
zero = true;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
|
fprintf(stderr, "usage: %s [OPTION] NAME...\n", argv[0]);
|
||||||
fprintf(stderr, " control the audio server\n");
|
fprintf(stderr, " output the directory containing each NAME\n");
|
||||||
fprintf(stderr, "OPTIONS:\n");
|
fprintf(stderr, "OPTIONS:\n");
|
||||||
fprintf(stderr, " -l, --list list devices and their pins\n");
|
fprintf(stderr, " -z, --zero end each output with NUL instead of a newline\n");
|
||||||
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
fprintf(stderr, " -h, --help show this message and exit\n");
|
||||||
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
|
||||||
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
|
|
||||||
fprintf(stderr, " -h, --help show this message and exit\n");
|
|
||||||
return 0;
|
return 0;
|
||||||
case '?':
|
case ':': case '?':
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -41,7 +38,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
if (optind >= argc)
|
if (optind >= argc)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "missing operand\n");
|
fprintf(stderr, "%s: missing operand\n", argv[0]);
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,24 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
struct config_t
|
struct Config
|
||||||
{
|
{
|
||||||
bool list = false;
|
enum class Visibility {
|
||||||
bool all = false;
|
Normal,
|
||||||
bool directory = false;
|
AlmostAll,
|
||||||
|
All
|
||||||
|
};
|
||||||
|
Visibility visibility { Visibility::Normal };
|
||||||
|
bool show_as_list { false };
|
||||||
|
bool human_readable { false };
|
||||||
|
bool directory { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct simple_entry_t
|
struct simple_entry_t
|
||||||
@@ -58,7 +65,7 @@ const char* entry_color(mode_t mode)
|
|||||||
return "\e[0m";
|
return "\e[0m";
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_access_string(mode_t mode)
|
BAN::String build_access_string(mode_t mode, const Config&)
|
||||||
{
|
{
|
||||||
BAN::String access;
|
BAN::String access;
|
||||||
MUST(access.resize(10));
|
MUST(access.resize(10));
|
||||||
@@ -75,12 +82,12 @@ BAN::String build_access_string(mode_t mode)
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_hard_links_string(nlink_t links)
|
BAN::String build_hard_links_string(nlink_t links, const Config&)
|
||||||
{
|
{
|
||||||
return MUST(BAN::String::formatted("{}", links));
|
return MUST(BAN::String::formatted("{}", links));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_owner_name_string(uid_t uid)
|
BAN::String build_owner_name_string(uid_t uid, const Config&)
|
||||||
{
|
{
|
||||||
struct passwd* passwd = getpwuid(uid);
|
struct passwd* passwd = getpwuid(uid);
|
||||||
if (passwd == nullptr)
|
if (passwd == nullptr)
|
||||||
@@ -88,7 +95,7 @@ BAN::String build_owner_name_string(uid_t uid)
|
|||||||
return BAN::String(BAN::StringView(passwd->pw_name));
|
return BAN::String(BAN::StringView(passwd->pw_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_owner_group_string(gid_t gid)
|
BAN::String build_owner_group_string(gid_t gid, const Config&)
|
||||||
{
|
{
|
||||||
struct group* grp = getgrgid(gid);
|
struct group* grp = getgrgid(gid);
|
||||||
if (grp == nullptr)
|
if (grp == nullptr)
|
||||||
@@ -96,23 +103,35 @@ BAN::String build_owner_group_string(gid_t gid)
|
|||||||
return BAN::String(BAN::StringView(grp->gr_name));
|
return BAN::String(BAN::StringView(grp->gr_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_size_string(off_t size)
|
BAN::String build_size_string(off_t size, const Config& config)
|
||||||
{
|
{
|
||||||
return MUST(BAN::String::formatted("{}", size));
|
if (!config.human_readable || size < 1024)
|
||||||
|
return MUST(BAN::String::formatted("{}", size));
|
||||||
|
|
||||||
|
size = size / 1024 * 10;
|
||||||
|
|
||||||
|
size_t suffix_idx = 0;
|
||||||
|
for (; size >= 10240; size /= 1024)
|
||||||
|
suffix_idx++;
|
||||||
|
|
||||||
|
constexpr char suffix[] { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' };
|
||||||
|
if (size >= 100)
|
||||||
|
return MUST(BAN::String::formatted("{}{}", size / 10, suffix[suffix_idx]));
|
||||||
|
return MUST(BAN::String::formatted("{}.{}{}", size / 10, size % 10, suffix[suffix_idx]));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_month_string(BAN::Time time)
|
BAN::String build_month_string(BAN::Time time, const Config&)
|
||||||
{
|
{
|
||||||
static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||||
return BAN::String(BAN::StringView(months[(time.month - 1) % 12]));
|
return BAN::String(BAN::StringView(months[(time.month - 1) % 12]));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_day_string(BAN::Time time)
|
BAN::String build_day_string(BAN::Time time, const Config&)
|
||||||
{
|
{
|
||||||
return MUST(BAN::String::formatted("{}", time.day));
|
return MUST(BAN::String::formatted("{}", time.day));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::String build_time_string(BAN::Time time)
|
BAN::String build_time_string(BAN::Time time, const Config&)
|
||||||
{
|
{
|
||||||
static uint32_t current_year = ({ timespec real_time; clock_gettime(CLOCK_REALTIME, &real_time); BAN::from_unix_time(real_time.tv_sec).year; });
|
static uint32_t current_year = ({ timespec real_time; clock_gettime(CLOCK_REALTIME, &real_time); BAN::from_unix_time(real_time.tv_sec).year; });
|
||||||
if (time.year != current_year)
|
if (time.year != current_year)
|
||||||
@@ -151,7 +170,7 @@ BAN::Vector<size_t> resolve_layout(const BAN::Vector<simple_entry_t>& entries)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int list_directory(const BAN::String& path, config_t config)
|
int list_directory(const BAN::String& path, Config config)
|
||||||
{
|
{
|
||||||
static char link_buffer[PATH_MAX];
|
static char link_buffer[PATH_MAX];
|
||||||
|
|
||||||
@@ -166,9 +185,14 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool is_directory = S_ISDIR(st.st_mode);
|
||||||
|
|
||||||
|
const size_t block_size = st.st_blksize;
|
||||||
|
size_t blocks_used = 0;
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!S_ISDIR(st.st_mode))
|
if (!is_directory)
|
||||||
{
|
{
|
||||||
MUST(entries.emplace_back(path, st, BAN::String()));
|
MUST(entries.emplace_back(path, st, BAN::String()));
|
||||||
if (S_ISLNK(st.st_mode))
|
if (S_ISLNK(st.st_mode))
|
||||||
@@ -191,8 +215,19 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
struct dirent* dirent;
|
struct dirent* dirent;
|
||||||
while ((dirent = readdir(dirp)))
|
while ((dirent = readdir(dirp)))
|
||||||
{
|
{
|
||||||
if (!config.all && dirent->d_name[0] == '.')
|
switch (config.visibility)
|
||||||
continue;
|
{
|
||||||
|
case Config::Visibility::Normal:
|
||||||
|
if (dirent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case Config::Visibility::AlmostAll:
|
||||||
|
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case Config::Visibility::All:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (fstatat(dirfd(dirp), dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
|
if (fstatat(dirfd(dirp), dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
|
||||||
{
|
{
|
||||||
@@ -201,6 +236,8 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blocks_used += st.st_blocks;
|
||||||
|
|
||||||
MUST(entries.emplace_back(BAN::StringView(dirent->d_name), st, BAN::String()));
|
MUST(entries.emplace_back(BAN::StringView(dirent->d_name), st, BAN::String()));
|
||||||
if (S_ISLNK(st.st_mode))
|
if (S_ISLNK(st.st_mode))
|
||||||
{
|
{
|
||||||
@@ -218,8 +255,8 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
[](const simple_entry_t& lhs, const simple_entry_t& rhs)
|
[](const simple_entry_t& lhs, const simple_entry_t& rhs)
|
||||||
{
|
{
|
||||||
// sort directories first
|
// sort directories first
|
||||||
bool lhs_isdir = S_ISDIR(lhs.st.st_mode);
|
const bool lhs_isdir = S_ISDIR(lhs.st.st_mode);
|
||||||
bool rhs_isdir = S_ISDIR(rhs.st.st_mode);
|
const bool rhs_isdir = S_ISDIR(rhs.st.st_mode);
|
||||||
if (lhs_isdir != rhs_isdir)
|
if (lhs_isdir != rhs_isdir)
|
||||||
return lhs_isdir;
|
return lhs_isdir;
|
||||||
|
|
||||||
@@ -231,7 +268,7 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!config.list)
|
if (!config.show_as_list)
|
||||||
{
|
{
|
||||||
if (!g_stdout_terminal)
|
if (!g_stdout_terminal)
|
||||||
{
|
{
|
||||||
@@ -297,9 +334,9 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
{
|
{
|
||||||
full_entry_t full_entry;
|
full_entry_t full_entry;
|
||||||
|
|
||||||
#define GET_ENTRY_STRING(property, input) \
|
#define GET_ENTRY_STRING(property, input) \
|
||||||
full_entry.property = build_ ## property ## _string(input); \
|
full_entry.property = build_ ## property ## _string(input, config); \
|
||||||
if (full_entry.property.size() > max_entry.property.size()) \
|
if (full_entry.property.size() > max_entry.property.size()) \
|
||||||
max_entry.property = full_entry.property;
|
max_entry.property = full_entry.property;
|
||||||
|
|
||||||
GET_ENTRY_STRING(access, entry.st.st_mode);
|
GET_ENTRY_STRING(access, entry.st.st_mode);
|
||||||
@@ -323,6 +360,14 @@ int list_directory(const BAN::String& path, config_t config)
|
|||||||
MUST(full_entries.push_back(BAN::move(full_entry)));
|
MUST(full_entries.push_back(BAN::move(full_entry)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_directory)
|
||||||
|
{
|
||||||
|
if (config.human_readable)
|
||||||
|
printf("total: %s\n", build_size_string(blocks_used * block_size, config).data());
|
||||||
|
else
|
||||||
|
printf("total: %zu\n", blocks_used);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& full_entry : full_entries)
|
for (const auto& full_entry : full_entries)
|
||||||
printf("%*s %*s %*s %*s %*s %*s %*s %*s %s\n",
|
printf("%*s %*s %*s %*s %*s %*s %*s %*s %s\n",
|
||||||
(int)max_entry.access.size(), full_entry.access.data(),
|
(int)max_entry.access.size(), full_entry.access.data(),
|
||||||
@@ -350,60 +395,65 @@ int usage(const char* argv0, int ret)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
config_t config;
|
Config config;
|
||||||
|
|
||||||
int i = 1;
|
for (;;)
|
||||||
for (; i < argc; i++)
|
|
||||||
{
|
{
|
||||||
if (argv[i][0] != '-')
|
static option long_options[] {
|
||||||
break;
|
{ "all", no_argument, nullptr, 'a' },
|
||||||
if (argv[i][1] == '\0')
|
{ "almost-all", no_argument, nullptr, 'A' },
|
||||||
|
{ "directory" , no_argument, nullptr, 'd' },
|
||||||
|
{ "human-readable", no_argument, nullptr, 'h' },
|
||||||
|
{ "list", no_argument, nullptr, 'l' },
|
||||||
|
{ "help", no_argument, nullptr, 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
int ch = getopt_long(argc, argv, "aAlh", long_options, nullptr);
|
||||||
|
if (ch == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (argv[i][1] == '-')
|
switch (ch)
|
||||||
{
|
{
|
||||||
if (strcmp(argv[i], "--help") == 0)
|
case 'a':
|
||||||
return usage(argv[0], 0);
|
config.visibility = Config::Visibility::All;
|
||||||
else if (strcmp(argv[i], "--all") == 0)
|
break;
|
||||||
config.all = true;
|
case 'A':
|
||||||
else if (strcmp(argv[i], "--list") == 0)
|
config.visibility = Config::Visibility::AlmostAll;
|
||||||
config.list = true;
|
break;
|
||||||
else if (strcmp(argv[i], "--directory") == 0)
|
case 'd':
|
||||||
config.directory = true;
|
config.directory = true;
|
||||||
else
|
break;
|
||||||
{
|
case 'h':
|
||||||
fprintf(stderr, "unrecognized option '%s'\n", argv[i]);
|
config.human_readable = true;
|
||||||
return usage(argv[0], 2);
|
break;
|
||||||
}
|
case 'l':
|
||||||
}
|
config.show_as_list = true;
|
||||||
else
|
break;
|
||||||
{
|
case 0:
|
||||||
for (size_t j = 1; argv[i][j]; j++)
|
fprintf(stderr, "usage: %s [OPTION]... [FILE]...\n", argv[0]);
|
||||||
{
|
fprintf(stderr, " list information about FILEs\n");
|
||||||
if (argv[i][j] == 'h')
|
fprintf(stderr, "OPTIONS:\n");
|
||||||
return usage(argv[0], 0);
|
fprintf(stderr, " -a, --all do not ignore entries starting with .\n");
|
||||||
else if (argv[i][j] == 'a')
|
fprintf(stderr, " -A, --almost-all do not list . and ..\n");
|
||||||
config.all = true;
|
fprintf(stderr, " -d, --directory list directories and not their contents\n");
|
||||||
else if (argv[i][j] == 'l')
|
fprintf(stderr, " -h, --human-readable print sizes in human readable form\n");
|
||||||
config.list = true;
|
fprintf(stderr, " -l, --list use long listing format\n");
|
||||||
else if (argv[i][j] == 'd')
|
fprintf(stderr, " --help show this message and exit\n");
|
||||||
config.directory = true;
|
return 0;
|
||||||
else
|
case ':': case '?':
|
||||||
{
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
fprintf(stderr, "unrecognized option '%c'\n", argv[i][j]);
|
return 1;
|
||||||
return usage(argv[0], 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::Vector<BAN::String> files;
|
BAN::Vector<BAN::String> files;
|
||||||
|
|
||||||
if (i == argc)
|
if (optind == argc)
|
||||||
MUST(files.emplace_back("."_sv));
|
MUST(files.emplace_back("."_sv));
|
||||||
else for (; i < argc; i++)
|
else for (int i = optind; i < argc; i++)
|
||||||
MUST(files.emplace_back(BAN::StringView(argv[i])));
|
MUST(files.emplace_back(BAN::StringView(argv[i])));
|
||||||
|
|
||||||
g_stdout_terminal = isatty(STDOUT_FILENO) && tcgetwinsize(STDOUT_FILENO, &g_terminal_size) == 0;
|
g_stdout_terminal = isatty(STDOUT_FILENO) && tcgetwinsize(STDOUT_FILENO, &g_terminal_size) == 0;
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
static option long_options[] {
|
static option long_options[] {
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "h", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "h", long_options, nullptr);
|
||||||
@@ -211,8 +212,7 @@ int main(int argc, char** argv)
|
|||||||
printf("OPTIONS:\n");
|
printf("OPTIONS:\n");
|
||||||
printf(" -h, --help Show this message and exit\n");
|
printf(" -h, --help Show this message and exit\n");
|
||||||
return 0;
|
return 0;
|
||||||
case '?':
|
case ':': case '?':
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ int main(int argc, char** argv)
|
|||||||
{ "interactive", no_argument, nullptr, 'i' },
|
{ "interactive", no_argument, nullptr, 'i' },
|
||||||
{ "force", no_argument, nullptr, 'f' },
|
{ "force", no_argument, nullptr, 'f' },
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "rRifh", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "rRifh", long_options, nullptr);
|
||||||
@@ -110,15 +111,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
case 'h':
|
|
||||||
fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]);
|
|
||||||
fprintf(stderr, " remove each FILE\n");
|
|
||||||
fprintf(stderr, "OPTIONS:\n");
|
|
||||||
fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n");
|
|
||||||
fprintf(stderr, " -i, --interactive prompt removal for all files\n");
|
|
||||||
fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n");
|
|
||||||
fprintf(stderr, " -h, --help show this message and exit\n");
|
|
||||||
return 0;
|
|
||||||
case 'r': case 'R':
|
case 'r': case 'R':
|
||||||
recursive = true;
|
recursive = true;
|
||||||
break;
|
break;
|
||||||
@@ -130,8 +122,16 @@ int main(int argc, char** argv)
|
|||||||
force = false;
|
force = false;
|
||||||
interactive = true;
|
interactive = true;
|
||||||
break;
|
break;
|
||||||
case '?':
|
case 'h':
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]);
|
||||||
|
fprintf(stderr, " remove each FILE\n");
|
||||||
|
fprintf(stderr, "OPTIONS:\n");
|
||||||
|
fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n");
|
||||||
|
fprintf(stderr, " -i, --interactive prompt removal for all files\n");
|
||||||
|
fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n");
|
||||||
|
fprintf(stderr, " -h, --help show this message and exit\n");
|
||||||
|
return 0;
|
||||||
|
case ':': case '?':
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,8 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
if (optind >= argc && !force)
|
if (optind >= argc && !force)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "missing operand. use --help for more information\n");
|
fprintf(stderr, "%s: missing operand\n", argv[0]);
|
||||||
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
287
userspace/programs/tr/main.cpp
Normal file
287
userspace/programs/tr/main.cpp
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#include <BAN/String.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static const char* s_argv0 { nullptr };
|
||||||
|
|
||||||
|
static char parse_char(const char*& array)
|
||||||
|
{
|
||||||
|
assert(array[0]);
|
||||||
|
|
||||||
|
if (array[0] != '\\')
|
||||||
|
return *array++;
|
||||||
|
|
||||||
|
switch (array[1])
|
||||||
|
{
|
||||||
|
case '\\': array += 2; return '\\';
|
||||||
|
case 'a': array += 2; return '\a';
|
||||||
|
case 'b': array += 2; return '\b';
|
||||||
|
case 'f': array += 2; return '\f';
|
||||||
|
case 'n': array += 2; return '\n';
|
||||||
|
case 'r': array += 2; return '\r';
|
||||||
|
case 't': array += 2; return '\t';
|
||||||
|
case 'v': array += 2; return '\v';
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t octal_count = 0;
|
||||||
|
while (octal_count < 3 && '0' <= array[1 + octal_count] && array[1 + octal_count] <= '7')
|
||||||
|
octal_count++;
|
||||||
|
|
||||||
|
if (octal_count == 0)
|
||||||
|
return *array++;
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
for (size_t i = 1; i <= octal_count; i++)
|
||||||
|
value = (value * 8) + (array[i] - '0');
|
||||||
|
|
||||||
|
array += 1 + octal_count;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::String expand_array(const char* array, size_t expand_len)
|
||||||
|
{
|
||||||
|
BAN::String result;
|
||||||
|
while (*array)
|
||||||
|
{
|
||||||
|
if (array[0] == '[')
|
||||||
|
{
|
||||||
|
const char* end = strchr(array + 1, ']');
|
||||||
|
if (end == nullptr)
|
||||||
|
goto normal_character;
|
||||||
|
|
||||||
|
const size_t len = end - array - 1;
|
||||||
|
if (len < 2)
|
||||||
|
goto normal_character;
|
||||||
|
|
||||||
|
if (array[1] == ':' && end[-1] == ':')
|
||||||
|
{
|
||||||
|
int (*class_test)(int) = nullptr;
|
||||||
|
|
||||||
|
#define CHECK_CHAR_CLASS(name) \
|
||||||
|
else if (len == sizeof(#name) + 1 && memcmp(array + 2, #name, len - 2) == 0) \
|
||||||
|
class_test = is##name
|
||||||
|
if (false);
|
||||||
|
CHECK_CHAR_CLASS(alnum);
|
||||||
|
CHECK_CHAR_CLASS(alpha);
|
||||||
|
CHECK_CHAR_CLASS(blank);
|
||||||
|
CHECK_CHAR_CLASS(cntrl);
|
||||||
|
CHECK_CHAR_CLASS(digit);
|
||||||
|
CHECK_CHAR_CLASS(graph);
|
||||||
|
CHECK_CHAR_CLASS(lower);
|
||||||
|
CHECK_CHAR_CLASS(print);
|
||||||
|
CHECK_CHAR_CLASS(punct);
|
||||||
|
CHECK_CHAR_CLASS(space);
|
||||||
|
CHECK_CHAR_CLASS(upper);
|
||||||
|
CHECK_CHAR_CLASS(xdigit);
|
||||||
|
#undef CHECK_CHAR_CLASS
|
||||||
|
|
||||||
|
if (class_test == nullptr)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: invalid character class '%.*s'\n", s_argv0, (int)len - 2, array + 2);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int ch = 0; ch < 0x100; ch++)
|
||||||
|
if (class_test(ch))
|
||||||
|
MUST(result.push_back(ch));
|
||||||
|
}
|
||||||
|
else if (array[1] == '=' && end[-1] == '=')
|
||||||
|
{
|
||||||
|
if (len - 2 != 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: %.*s: equivalence class must be a single character\n", s_argv0, (int)len - 2, array + 2);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: actually support collating elements
|
||||||
|
MUST(result.push_back(array[2]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* temp = array + 1;
|
||||||
|
const char ch = parse_char(temp);
|
||||||
|
|
||||||
|
if (temp[0] != '*')
|
||||||
|
goto normal_character;
|
||||||
|
temp++;
|
||||||
|
|
||||||
|
const int base = (temp[0] == '0') ? 8 : 10;
|
||||||
|
|
||||||
|
bool valid_count = true;
|
||||||
|
for (size_t i = 0; valid_count && temp[i] != ']'; i++)
|
||||||
|
valid_count = ('0' <= temp[i] && temp[i] <= '0' + base - 1);
|
||||||
|
if (!valid_count)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: invalid repeat count '%.*s'\n", s_argv0, (int)len - 2, array + 2);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; temp[i] != ']'; i++)
|
||||||
|
count = (count * base) + (temp[i] - '0');
|
||||||
|
if (count == 0 && result.size() < expand_len)
|
||||||
|
count = expand_len - result.size();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
MUST(result.push_back(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
array = end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
normal_character:
|
||||||
|
const char ch1 = parse_char(array);
|
||||||
|
|
||||||
|
if (array[0] == '-' && array[1])
|
||||||
|
{
|
||||||
|
array++;
|
||||||
|
const char ch2 = parse_char(array);
|
||||||
|
for (int ch = ch1; ch <= ch2; ch++)
|
||||||
|
MUST(result.push_back(ch));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MUST(result.push_back(ch1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
s_argv0 = argv[0];
|
||||||
|
|
||||||
|
bool complement { false };
|
||||||
|
bool delete_ { false };
|
||||||
|
bool squeeze { false };
|
||||||
|
bool truncate { false };
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
static option long_options[] {
|
||||||
|
{ "complement", no_argument, nullptr, 'c' },
|
||||||
|
{ "delete", no_argument, nullptr, 'd' },
|
||||||
|
{ "squeeze-repeats", no_argument, nullptr, 's' },
|
||||||
|
{ "truncate-set1", no_argument, nullptr, 't' },
|
||||||
|
{ "help", no_argument, nullptr, 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
int ch = getopt_long(argc, argv, "cCdst", long_options, nullptr);
|
||||||
|
if (ch == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case 'c': case 'C':
|
||||||
|
complement = true;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete_ = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
squeeze = true;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
truncate = true;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
fprintf(stderr, "usage: %s [OPTION]... STRING1 [STRING2]\n", argv[0]);
|
||||||
|
fprintf(stderr, " translate and/or delete characters from standard input\n");
|
||||||
|
fprintf(stderr, "OPTIONS:\n");
|
||||||
|
fprintf(stderr, " -c, -C, --complement do not ignore entries starting with .\n");
|
||||||
|
fprintf(stderr, " -d, --delete do not list . and ..\n");
|
||||||
|
fprintf(stderr, " -s, --squeeze-repeats list directories and not their contents\n");
|
||||||
|
fprintf(stderr, " -t, --truncate-set1 print sizes in human readable form\n");
|
||||||
|
fprintf(stderr, " --help show this message and exit\n");
|
||||||
|
return 0;
|
||||||
|
case ':' : case '?':
|
||||||
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int needed_args = (delete_ == squeeze) ? 2 : 1;
|
||||||
|
if (optind + needed_args > argc)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: missing operand\n", argv[0]);
|
||||||
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind + 2 < argc)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: extra operand '%s'\n", argv[0], argv[optind + 2]);
|
||||||
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::String array1 = expand_array(argv[optind], 0);
|
||||||
|
if (complement)
|
||||||
|
{
|
||||||
|
bool contains[0x100] {};
|
||||||
|
for (int ch : array1)
|
||||||
|
contains[ch] = true;
|
||||||
|
array1.clear();
|
||||||
|
for (int ch = 0; ch < 0x100; ch++)
|
||||||
|
if (!contains[ch])
|
||||||
|
MUST(array1.push_back(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Optional<BAN::String> array2;
|
||||||
|
if (optind + 1 < argc)
|
||||||
|
{
|
||||||
|
array2 = expand_array(argv[optind + 1], array1.size());;
|
||||||
|
if (truncate && array1.size() > array2->size())
|
||||||
|
MUST(array1.resize(array2->size()));
|
||||||
|
if (!array1.empty() && array2->empty())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: STRING2 must not be empty\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (array2->size() < array1.size())
|
||||||
|
MUST(array2->push_back(array2->back()));
|
||||||
|
}
|
||||||
|
|
||||||
|
char translate_map[0x100] {};
|
||||||
|
for (int ch = 0; ch < 0x100; ch++)
|
||||||
|
translate_map[ch] = ch;
|
||||||
|
if (!delete_ && !squeeze)
|
||||||
|
for (size_t i = 0; i < array1.size(); i++)
|
||||||
|
translate_map[static_cast<int>(array1[i])] = array2.value()[i];
|
||||||
|
|
||||||
|
bool delete_set[0x100] {};
|
||||||
|
if (delete_)
|
||||||
|
{
|
||||||
|
for (int ch : array1)
|
||||||
|
delete_set[ch] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool squeeze_set[0x100] {};
|
||||||
|
if (squeeze)
|
||||||
|
{
|
||||||
|
const auto& array = array2.has_value() ? array2.value() : array1;
|
||||||
|
for (int ch : array)
|
||||||
|
squeeze_set[ch] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prev_char = -1;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int ch = getchar();
|
||||||
|
if (ch == EOF)
|
||||||
|
break;
|
||||||
|
ch = translate_map[ch];
|
||||||
|
if (delete_set[ch])
|
||||||
|
continue;
|
||||||
|
if (squeeze_set[ch] && prev_char == ch)
|
||||||
|
continue;
|
||||||
|
prev_char = ch;
|
||||||
|
putchar(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ int main(int argc, char** argv)
|
|||||||
{ "kernel-name", no_argument, nullptr, 's' },
|
{ "kernel-name", no_argument, nullptr, 's' },
|
||||||
{ "kernel-version", no_argument, nullptr, 'v' },
|
{ "kernel-version", no_argument, nullptr, 'v' },
|
||||||
{ "help", no_argument, nullptr, 0 },
|
{ "help", no_argument, nullptr, 0 },
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "amnrsv", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "amnrsv", long_options, nullptr);
|
||||||
@@ -28,15 +29,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
case 0:
|
|
||||||
printf("usage: %s [OPTION]...\n", argv[0]);
|
|
||||||
printf(" -a, --all print all information\n");
|
|
||||||
printf(" -m, --machine print machine name\n");
|
|
||||||
printf(" -r, --release print release\n");
|
|
||||||
printf(" -n, --nodename print node name\n");
|
|
||||||
printf(" -s, --system print system name\n");
|
|
||||||
printf(" -v, --version print version\n");
|
|
||||||
return 0;
|
|
||||||
case 'a':
|
case 'a':
|
||||||
machine = true;
|
machine = true;
|
||||||
nodename = true;
|
nodename = true;
|
||||||
@@ -59,8 +51,17 @@ int main(int argc, char** argv)
|
|||||||
case 'v':
|
case 'v':
|
||||||
version = true;
|
version = true;
|
||||||
break;
|
break;
|
||||||
case '?':
|
case 0:
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
printf("usage: %s [OPTION]...\n", argv[0]);
|
||||||
|
printf(" -a, --all print all information\n");
|
||||||
|
printf(" -m, --machine print machine name\n");
|
||||||
|
printf(" -r, --release print release\n");
|
||||||
|
printf(" -n, --nodename print node name\n");
|
||||||
|
printf(" -s, --system print system name\n");
|
||||||
|
printf(" -v, --version print version\n");
|
||||||
|
printf(" --help show this message and exit\n");
|
||||||
|
return 0;
|
||||||
|
case ':': case '?':
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user