mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
141 lines
7.4 KiB
C++
141 lines
7.4 KiB
C++
#pragma once
|
|
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <span>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "inherit.h"
|
|
|
|
namespace raven {
|
|
|
|
// Base of the objects held by the kernel handle table.
|
|
class KernelObject {
|
|
public:
|
|
virtual ~KernelObject() = default;
|
|
// Waiting: 1 when a wait by the given thread would succeed now, 0 when it would block, -1 for objects that cannot be waited on.
|
|
virtual int signaled(uint32_t thread) const { (void)thread; return -1; }
|
|
// Runs when a wait succeeds: auto-reset events clear, mutexes change owner, semaphores count down.
|
|
virtual void consume(uint32_t thread) { (void)thread; }
|
|
// Takes what a satisfied wait takes, and says whether it still could: an object shared with other processes can lose it to one of them
|
|
// between the check and the take.
|
|
virtual bool try_consume(uint32_t thread) { consume(thread); return true; }
|
|
// Sets or clears an event that lives in this object; the previous state, or -1 when the object is no event.
|
|
virtual int set_state(bool signaled) { (void)signaled; return -1; }
|
|
// Describes the object for a process that inherits it; false for objects that cannot be handed to another process.
|
|
virtual bool describe(InheritedHandle &record) const { (void)record; return false; }
|
|
// The name of the object's type in the object manager (NtQueryObject); empty for objects Windows has no type for.
|
|
virtual std::u16string_view type_name() const { return {}; }
|
|
// The access a new handle to the object holds when nothing narrower is asked for.
|
|
virtual uint32_t default_access() const { return 0x1f0003; }
|
|
// When the object turns signaled by itself, as a timer does at its due time; a waiter wakes then to look again.
|
|
virtual std::optional<std::chrono::steady_clock::time_point> wakes_at() const { return std::nullopt; }
|
|
};
|
|
|
|
class NativeObjects;
|
|
class EventObject;
|
|
// Rebuilds the handles a creator passed on, each under the value it had there.
|
|
void adopt_inherited(NativeObjects &objects, const std::vector<InheritedHandle> &handles);
|
|
// The object a handle description stands for, built around its descriptor; null for a kind that cannot be handed over.
|
|
std::shared_ptr<KernelObject> object_from_record(const InheritedHandle &record);
|
|
|
|
// A handle value held for something another part of the runtime owns, such as an open registry key; closing the handle tells the owner.
|
|
class ReservedObject : public KernelObject {
|
|
public:
|
|
// The runtime reserves handles for registry keys only.
|
|
std::u16string_view type_name() const override { return u"Key"; }
|
|
std::function<void(uint64_t)> on_close;
|
|
};
|
|
|
|
struct NativeObjectResult {
|
|
uint64_t value = 0;
|
|
uint32_t status = 0;
|
|
bool runtime_timeout = false;
|
|
};
|
|
|
|
// Process handle identities refer to shared object state, rather than copying it.
|
|
class NativeObjects {
|
|
public:
|
|
explicit NativeObjects(size_t limit = 10000) : limit(limit) {}
|
|
NativeObjects(const NativeObjects &) = delete;
|
|
NativeObjects &operator=(const NativeObjects &) = delete;
|
|
NativeObjectResult create_event(uint32_t access, uint32_t type, bool signaled);
|
|
// Events that have a name are one object however often they are created or opened: `existed` tells whether the name was taken.
|
|
NativeObjectResult create_named_event(const std::u16string &name, uint32_t access, uint32_t type, bool signaled, bool &existed);
|
|
// A new handle for the named event; the status is 0xc0000034 when no such event is alive.
|
|
NativeObjectResult open_named_event(const std::u16string &name, uint32_t access);
|
|
NativeObjectResult inherit_file(int descriptor);
|
|
// Reads from an inherited standard handle; the value is the byte count, zero at the end of the input.
|
|
NativeObjectResult read_file(uint64_t handle,std::span<uint8_t> into);
|
|
NativeObjectResult file_type(uint64_t handle) const;
|
|
// The host descriptor behind an inherited standard handle, -1 for any other handle.
|
|
int descriptor(uint64_t handle) const;
|
|
NativeObjectResult write_file(uint64_t handle, std::span<const uint8_t> bytes);
|
|
uint32_t close(uint64_t handle);
|
|
// Takes a handle value for an owner outside the table; zero when none is free. Closing it runs `on_close`.
|
|
uint64_t reserve(std::function<void(uint64_t)> on_close);
|
|
// Forgets a reserved handle without telling the owner (the owner closed it first).
|
|
void release_reserved(uint64_t handle);
|
|
// Generic objects share the handle namespace of events and inherited files.
|
|
NativeObjectResult add_object(std::shared_ptr<KernelObject> object);
|
|
std::shared_ptr<KernelObject> object(uint64_t handle) const;
|
|
template<typename T> std::shared_ptr<T> object_as(uint64_t handle) const { return std::dynamic_pointer_cast<T>(object(handle)); }
|
|
// Waiting state of any handle kind (see KernelObject::signaled); -2 when the handle does not exist.
|
|
int signaled(uint64_t handle, uint32_t thread) const;
|
|
void consume(uint64_t handle, uint32_t thread);
|
|
// Takes what a satisfied wait takes; false when another process took it first.
|
|
bool try_consume(uint64_t handle, uint32_t thread);
|
|
// Duplicates a handle onto the same object (DuplicateHandle within the process).
|
|
// A new handle to the same object; `access` narrows the rights the copy carries (kept for objects whose rights matter, such as sections).
|
|
NativeObjectResult duplicate(uint64_t handle, std::optional<uint32_t> access = std::nullopt);
|
|
uint32_t access_of(uint64_t handle) const;
|
|
void set_access(uint64_t handle, uint32_t access);
|
|
// Whether a child process created with handle inheritance receives the handle (HANDLE_FLAG_INHERIT).
|
|
void set_inherit(uint64_t handle, bool inherit);
|
|
bool inherit(uint64_t handle) const;
|
|
// The generic object behind a handle and the handles that are inheritable, for process creation.
|
|
std::vector<uint64_t> inheritable() const;
|
|
// Puts an object under a handle value that another process chose; false when the value is taken.
|
|
bool adopt(uint64_t handle, std::shared_ptr<KernelObject> object, bool inherit);
|
|
// Process requests share the handle channel, but the process owner validates and serves them.
|
|
std::function<InheritedHandle(const InheritedHandle &)> process_request;
|
|
NativeObjectResult set_event(uint64_t handle, bool signaled);
|
|
NativeObjectResult query_event(uint64_t handle);
|
|
// The object manager type of the object behind a handle; nullopt when the handle does not exist.
|
|
std::optional<std::u16string> type_name(uint64_t handle) const;
|
|
NativeObjectResult wait(uint64_t handle, std::optional<int64_t> timeout,
|
|
std::chrono::steady_clock::time_point deadline);
|
|
size_t count() const;
|
|
std::vector<uint64_t> values() const;
|
|
size_t waiting(uint64_t handle) const;
|
|
|
|
private:
|
|
struct File {
|
|
int descriptor;
|
|
uint32_t type;
|
|
File(int descriptor,uint32_t type) : descriptor(descriptor),type(type) {}
|
|
~File();
|
|
};
|
|
struct Handle { std::shared_ptr<EventObject> event; uint32_t access; std::shared_ptr<File> file; std::shared_ptr<KernelObject> generic = {}; bool inherit = false; };
|
|
mutable std::mutex mutex;
|
|
std::map<uint64_t,Handle> handles;
|
|
std::map<std::u16string,std::weak_ptr<EventObject>> named_events;
|
|
uint64_t next = 0x100;
|
|
// The next free handle value; the caller holds the mutex. Values that inherited handles occupy are skipped.
|
|
uint64_t take();
|
|
void arrive(uint64_t handle) const;
|
|
size_t limit;
|
|
std::pair<std::shared_ptr<EventObject>,uint32_t> event(uint64_t handle, uint32_t access) const;
|
|
std::shared_ptr<File> file(uint64_t handle) const;
|
|
};
|
|
|
|
}
|