diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 29c474adee..e1486da01f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -59,7 +59,7 @@ jobs: run: | rustup component add rustfmt cargo install --force --version 0.15.0 cbindgen - cargo install --force --version 0.55.1 bindgen + cargo install --force --version 0.56.0 bindgen - name: Check bindings run: | mkdir build diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c56f9f876..05c0e530f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,7 @@ option(SHADOW_TEST "build tests (default: OFF)" OFF) option(SHADOW_EXPORT "export service libraries and headers (default: OFF)" OFF) option(SHADOW_WERROR "turn compiler warnings into errors. (default: OFF)" OFF) option(SHADOW_COVERAGE "enable code-coverage instrumentation. (default: OFF)" OFF) +option(SHADOW_USE_C_SYSCALLS "use only the C syscall handlers. (default: OFF)" OFF) ## display selected user options MESSAGE(STATUS) @@ -165,6 +166,10 @@ else() set(CARGO_ENV_VARS "") endif(SHADOW_COVERAGE STREQUAL ON) +if(SHADOW_USE_C_SYSCALLS STREQUAL ON) + add_definitions(-DUSE_C_SYSCALLS) +endif() + if($ENV{VERBOSE}) add_definitions(-DVERBOSE) endif() diff --git a/docs/5-Developer-Guide.md b/docs/5-Developer-Guide.md index d9a5c2e6f0..3aa20c9d7a 100644 --- a/docs/5-Developer-Guide.md +++ b/docs/5-Developer-Guide.md @@ -6,6 +6,10 @@ When required, you can rebuild all of the C-Rust bindings by running: cd build && cmake --target bindings .. && make bindings ``` +To see the specific options/flags provided to bindgen and cbindgen, you can use `make VERBOSE=1 bindings`. + +Since the C bindings and Rust bindings rely on each other, you may sometimes need to build the bindings in a specific order. Instead of `make bindings`, you can be more specific using for example `make bindings_main_rust` to make the Rust bindings for `src/main`. + You may need to install bindgen, cbindgen, and clang: ```bash diff --git a/setup b/setup index 97a9edc32f..cfdd28cc3f 100755 --- a/setup +++ b/setup @@ -67,6 +67,11 @@ def main(): action="store_true", dest="do_coverage", default=False) + parser_build.add_argument('--use-c-syscalls', + help="use only the C syscall handlers (disables Rust syscall handlers)", + action="store_true", dest="do_use_c_syscalls", + default=False) + parser_build.add_argument('-v', '--verbose', help="print verbose output from the compiler", action="store_true", dest="do_verbose", @@ -194,6 +199,7 @@ def build(args, remaining): if args.disable_tgen: cmake_cmd += " -DBUILD_TGEN=OFF" if args.do_valgrind: cmake_cmd += " -DLOADER_VALGRIND=ON" if args.do_werror: cmake_cmd += " -DSHADOW_WERROR=ON" + if args.do_use_c_syscalls: cmake_cmd += " -DSHADOW_USE_C_SYSCALLS=ON" if args.do_coverage: if not args.do_debug: diff --git a/src/main/Cargo.toml b/src/main/Cargo.toml index 7d1fce4355..607b1ac0d8 100644 --- a/src/main/Cargo.toml +++ b/src/main/Cargo.toml @@ -9,6 +9,8 @@ path = "lib.rs" crate-type = ["staticlib"] [dependencies] +atomic_refcell = "0.1" +bitflags = "1.2" lazy_static = "1.4.0" libc = "0.2" log = "0.4" diff --git a/src/main/bindings/CMakeLists.txt b/src/main/bindings/CMakeLists.txt index 9bbba97d44..989f218a00 100644 --- a/src/main/bindings/CMakeLists.txt +++ b/src/main/bindings/CMakeLists.txt @@ -3,8 +3,15 @@ add_subdirectory(rust) # A fake target that depends on the wrapper. add_custom_target(bindings_main) -add_dependencies(bindings_main bindings_main_rust) add_dependencies(bindings_main bindings_main_c) +add_dependencies(bindings_main bindings_main_rust) + +# The C bindings should be generated first since cbindgen doesn't require the Rust code +# to be valid, whereas bindgen does require the C code to be valid. If the C bindings +# are no longer correct, but the Rust bindings are generated first, then there will be +# no way to correct the C bindings since the Rust binding generation will always fail +# before the C bindings can be corrected. +add_dependencies(bindings_main_rust bindings_main_c) # Only re-generate bindings when explicititly requested, so that # our CI doesn't need to install the heavy bindgen dependency. diff --git a/src/main/bindings/c/CMakeLists.txt b/src/main/bindings/c/CMakeLists.txt index e270c6dc33..1ff8b334e1 100644 --- a/src/main/bindings/c/CMakeLists.txt +++ b/src/main/bindings/c/CMakeLists.txt @@ -17,14 +17,20 @@ foreach(COMPILE_DEFINITION IN LISTS COMPILE_DEFINITIONS) list(APPEND LLVM_FLAGS "-D${COMPILE_DEFINITION}") endforeach(COMPILE_DEFINITION) -# Generate wrapper.rs in the source tree. +# Generate bindings.h in the source tree. add_custom_command(OUTPUT bindings.h COMMAND cbindgen ${CMAKE_SOURCE_DIR}/src/main --config ${CMAKE_CURRENT_SOURCE_DIR}/cbindgen.toml --output ${CMAKE_CURRENT_SOURCE_DIR}/bindings.h) +# Generate bindings-opaque.h in the source tree. +add_custom_command(OUTPUT bindings-opaque.h + COMMAND cbindgen ${CMAKE_SOURCE_DIR}/src/main + --config ${CMAKE_CURRENT_SOURCE_DIR}/cbindgen-opaque.toml + --output ${CMAKE_CURRENT_SOURCE_DIR}/bindings-opaque.h) + # A fake target that depends on the wrapper. -add_custom_target(bindings_main_c DEPENDS bindings.h) +add_custom_target(bindings_main_c DEPENDS bindings.h bindings-opaque.h) # Only re-generate bindings when explicititly requested, so that # our CI doesn't need to install the heavy bindgen dependency. diff --git a/src/main/bindings/c/bindings-opaque.h b/src/main/bindings/c/bindings-opaque.h new file mode 100644 index 0000000000..e3a226a2ec --- /dev/null +++ b/src/main/bindings/c/bindings-opaque.h @@ -0,0 +1,38 @@ +/* + * The Shadow Simulator + * See LICENSE for licensing information + */ +// clang-format off + + +#ifndef main_opaque_bindings_h +#define main_opaque_bindings_h + +/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ + +// A queue of byte chunks. +typedef struct ByteQueue ByteQueue; + +typedef struct CompatDescriptor CompatDescriptor; + +// Manages the address-space for a plugin process. +// +// The MemoryManager's primary purpose is to make plugin process's memory directly accessible to +// Shadow. It does this by tracking what regions of program memory in the plugin are mapped to +// what (analagous to /proc//maps), and *remapping* parts of the plugin's address space into +// a shared memory-file, which is also mapped into Shadow. +// +// Shadow provides several methods for allowing Shadow to access the plugin's memory, such as +// `get_readable_ptr`. If the corresponding region of plugin memory is mapped into the shared +// memory file, the corresponding Shadow pointer is returned. If not, then, it'll fall back to +// (generally slower) Thread APIs. +// +// For the MemoryManager to maintain consistent state, and to remap regions of memory it knows how +// to remap, Shadow must delegate handling of mman-related syscalls (such as `mmap`) to the +// MemoryManager via its `handle_*` methods. +typedef struct MemoryManager MemoryManager; + +// An opaque type used when passing `*const AtomicRefCell` to C. +typedef struct PosixFileArc PosixFileArc; + +#endif /* main_opaque_bindings_h */ diff --git a/src/main/bindings/c/bindings.h b/src/main/bindings/c/bindings.h index d49dc39f68..0a13ffa4e5 100644 --- a/src/main/bindings/c/bindings.h +++ b/src/main/bindings/c/bindings.h @@ -14,31 +14,58 @@ #include #include #include +#include "main/bindings/c/bindings-opaque.h" +#include "main/host/descriptor/descriptor_types.h" +#include "main/host/status_listener.h" +#include "main/host/syscall_handler.h" #include "main/host/syscall_types.h" #include "main/host/thread.h" -// A queue of byte chunks. -typedef struct ByteQueue ByteQueue; - -// Manages the address-space for a plugin process. -// -// The MemoryManager's primary purpose is to make plugin process's memory directly accessible to -// Shadow. It does this by tracking what regions of program memory in the plugin are mapped to -// what (analagous to /proc//maps), and *remapping* parts of the plugin's address space into -// a shared memory-file, which is also mapped into Shadow. -// -// Shadow provides several methods for allowing Shadow to access the plugin's memory, such as -// `get_readable_ptr`. If the corresponding region of plugin memory is mapped into the shared -// memory file, the corresponding Shadow pointer is returned. If not, then, it'll fall back to -// (generally slower) Thread APIs. -// -// For the MemoryManager to maintain consistent state, and to remap regions of memory it knows how -// to remap, Shadow must delegate handling of mman-related syscalls (such as `mmap`) to the -// MemoryManager via its `handle_*` methods. -typedef struct MemoryManager MemoryManager; - void rust_logging_init(void); +// The new compat descriptor takes ownership of the reference to the legacy descriptor and +// does not increment its ref count, but will decrement the ref count when this compat +// descriptor is freed/dropped. +CompatDescriptor *compatdescriptor_fromLegacy(LegacyDescriptor *legacy_descriptor); + +// If the compat descriptor is a legacy descriptor, returns a pointer to the legacy +// descriptor object. Otherwise returns NULL. The legacy descriptor's ref count is not +// modified, so the pointer must not outlive the lifetime of the compat descriptor. +LegacyDescriptor *compatdescriptor_asLegacy(const CompatDescriptor *descriptor); + +// When the compat descriptor is freed/dropped, it will decrement the legacy descriptor's +// ref count. +void compatdescriptor_free(CompatDescriptor *descriptor); + +// This is a no-op for non-legacy descriptors. +void compatdescriptor_setHandle(CompatDescriptor *descriptor, int handle); + +// If the compat descriptor is a new descriptor, returns a pointer to the reference-counted +// posix file object. Otherwise returns NULL. The posix file object's ref count is not +// modified, so the pointer must not outlive the lifetime of the compat descriptor. +const PosixFileArc *compatdescriptor_borrowPosixFile(CompatDescriptor *descriptor); + +// If the compat descriptor is a new descriptor, returns a pointer to the reference-counted +// posix file object. Otherwise returns NULL. The posix file object's ref count is +// incremented, so the pointer must always later be passed to `posixfile_drop()`, otherwise +// the memory will leak. +const PosixFileArc *compatdescriptor_newRefPosixFile(CompatDescriptor *descriptor); + +// Decrement the ref count of the posix file object. The pointer must not be used after +// calling this function. +void posixfile_drop(const PosixFileArc *file); + +// Get the status of the posix file object. +Status posixfile_getStatus(const PosixFileArc *file); + +// Add a status listener to the posix file object. This will increment the status +// listener's ref count, and will decrement the ref count when this status listener is +// removed or when the posix file is freed/dropped. +void posixfile_addListener(const PosixFileArc *file, StatusListener *listener); + +// Remove a listener from the posix file object. +void posixfile_removeListener(const PosixFileArc *file, StatusListener *listener); + // # Safety // * `thread` must point to a valid object. MemoryManager *memorymanager_new(Thread *thread); @@ -106,10 +133,22 @@ SysCallReg memorymanager_handleMprotect(MemoryManager *memory_manager, uintptr_t size, int32_t prot); +SysCallReturn rustsyscallhandler_close(SysCallHandler *sys, const SysCallArgs *args); + +SysCallReturn rustsyscallhandler_read(SysCallHandler *sys, const SysCallArgs *args); + +SysCallReturn rustsyscallhandler_write(SysCallHandler *sys, const SysCallArgs *args); + +SysCallReturn rustsyscallhandler_pipe(SysCallHandler *sys, const SysCallArgs *args); + +SysCallReturn rustsyscallhandler_pipe2(SysCallHandler *sys, const SysCallArgs *args); + ByteQueue *bytequeue_new(size_t chunk_size); void bytequeue_free(ByteQueue *bq_ptr); +size_t bytequeue_len(ByteQueue *bq); + void bytequeue_push(ByteQueue *bq, const unsigned char *src, size_t len); size_t bytequeue_pop(ByteQueue *bq, unsigned char *dst, size_t len); diff --git a/src/main/bindings/c/cbindgen-opaque.toml b/src/main/bindings/c/cbindgen-opaque.toml new file mode 100644 index 0000000000..0188e46d06 --- /dev/null +++ b/src/main/bindings/c/cbindgen-opaque.toml @@ -0,0 +1,18 @@ +language = 'C' +include_guard = "main_opaque_bindings_h" +line_length = 100 +documentation_style = "c99" +header = '''/* + * The Shadow Simulator + * See LICENSE for licensing information + */ +// clang-format off +''' +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +no_includes = true + +[export] +# Avoid exporting C types back through again. +exclude = ["PluginPtr", "SysCallReg"] +# Generate only opaque types +item_types = ["opaque"] diff --git a/src/main/bindings/c/cbindgen.toml b/src/main/bindings/c/cbindgen.toml index cd818970ad..d98741685d 100644 --- a/src/main/bindings/c/cbindgen.toml +++ b/src/main/bindings/c/cbindgen.toml @@ -9,8 +9,17 @@ header = '''/* // clang-format off ''' autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" -includes = ["main/host/syscall_types.h", "main/host/thread.h"] +includes = [ + "main/bindings/c/bindings-opaque.h", + "main/host/descriptor/descriptor_types.h", + "main/host/status_listener.h", + "main/host/syscall_handler.h", + "main/host/syscall_types.h", + "main/host/thread.h", +] [export] # Avoid exporting C types back through again. exclude = ["PluginPtr", "SysCallReg"] +# Generate all item types, excluding opaque types +item_types = ["constants", "globals", "enums", "structs", "unions", "typedefs", "functions"] diff --git a/src/main/bindings/rust/CMakeLists.txt b/src/main/bindings/rust/CMakeLists.txt index 50e2d5e7b8..9a7a333bc6 100644 --- a/src/main/bindings/rust/CMakeLists.txt +++ b/src/main/bindings/rust/CMakeLists.txt @@ -21,10 +21,33 @@ endforeach(COMPILE_DEFINITION) add_custom_command(OUTPUT wrapper.rs COMMAND bindgen --whitelist-function "thread_.*" + --whitelist-function "descriptor_unref" + --whitelist-function "descriptor_setHandle" + --whitelist-function "process_.*CompatDescriptor" + --whitelist-function "process_get.*Ptr" + --whitelist-function "statuslistener_ref" + --whitelist-function "statuslistener_unref" + --whitelist-function "statuslistener_onStatusChanged" + --whitelist-function "syscallcondition_new" + --whitelist-function "syscallhandler_.*" + --blacklist-function "syscallhandler_new" + --blacklist-function "syscallhandler_ref" + --blacklist-function "syscallhandler_unref" + --blacklist-function "syscallhandler_make_syscall" --whitelist-type "PluginPtr" + --whitelist-type "Status" + --whitelist-type "StatusListener" --whitelist-type "SysCall.*" + --whitelist-type "LegacyDescriptor" + --whitelist-type "Trigger" + --whitelist-type "TriggerType" + --whitelist-var "CONFIG_PIPE_BUFFER_SIZE" + --whitelist-var "SYSCALL_IO_BUFSIZE" + --opaque-type "LegacyDescriptor" + --opaque-type "CompatDescriptor" --disable-header-comment --raw-line "/* automatically generated by rust-bindgen */" + --raw-line "use crate::host::descriptor::CompatDescriptor\;" ${CMAKE_CURRENT_SOURCE_DIR}/wrapper.h -o ${CMAKE_CURRENT_SOURCE_DIR}/wrapper.rs -- ${LLVM_FLAGS} MAIN_DEPENDENCY wrapper.h IMPLICIT_DEPENDS C wrapper.h) diff --git a/src/main/bindings/rust/wrapper.h b/src/main/bindings/rust/wrapper.h index e324626d3d..6aa84fff6c 100644 --- a/src/main/bindings/rust/wrapper.h +++ b/src/main/bindings/rust/wrapper.h @@ -5,5 +5,10 @@ // Don't forget to whitelist functions/types/vars in CMakeLists.txt +#include "main/host/descriptor/descriptor.h" +#include "main/host/status.h" +#include "main/host/status_listener.h" +#include "main/host/syscall_condition.h" #include "main/host/syscall_types.h" +#include "main/host/syscall/unistd.h" #include "main/host/thread.h" diff --git a/src/main/bindings/rust/wrapper.rs b/src/main/bindings/rust/wrapper.rs index 294a7e9e8d..b0c692bf35 100644 --- a/src/main/bindings/rust/wrapper.rs +++ b/src/main/bindings/rust/wrapper.rs @@ -1,9 +1,69 @@ /* automatically generated by rust-bindgen */ +use crate::host::descriptor::CompatDescriptor; +pub const CONFIG_PIPE_BUFFER_SIZE: u32 = 65536; +pub const SYSCALL_IO_BUFSIZE: u32 = 16384; +pub type size_t = ::std::os::raw::c_ulong; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_long; pub type __uint64_t = ::std::os::raw::c_ulong; pub type __pid_t = ::std::os::raw::c_int; +pub type pid_t = __pid_t; +pub type gint = ::std::os::raw::c_int; +pub type gdouble = f64; +pub type gpointer = *mut ::std::os::raw::c_void; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _GTimer { + _unused: [u8; 0], +} +pub type GTimer = _GTimer; +pub use self::_Status as Status; +pub const _Status_STATUS_NONE: _Status = 0; +pub const _Status_STATUS_DESCRIPTOR_ACTIVE: _Status = 1; +pub const _Status_STATUS_DESCRIPTOR_READABLE: _Status = 2; +pub const _Status_STATUS_DESCRIPTOR_WRITABLE: _Status = 4; +pub const _Status_STATUS_DESCRIPTOR_CLOSED: _Status = 8; +pub const _Status_STATUS_FUTEX_WAKEUP: _Status = 16; +pub type _Status = i32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Process { + _unused: [u8; 0], +} +pub type Process = _Process; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Host { + _unused: [u8; 0], +} +pub type Host = _Host; +pub type LegacyDescriptor = [u64; 6usize]; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PosixFileArc { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _StatusListener { + _unused: [u8; 0], +} +pub type StatusListener = _StatusListener; +extern "C" { + pub fn statuslistener_ref(listener: *mut StatusListener); +} +extern "C" { + pub fn statuslistener_unref(listener: *mut StatusListener); +} +extern "C" { + pub fn statuslistener_onStatusChanged( + listener: *mut StatusListener, + currentStatus: Status, + transitions: Status, + ); +} +pub type SysCallHandler = _SysCallHandler; pub type PluginVirtualPtr = _PluginVirtualPtr; pub type PluginPtr = _PluginVirtualPtr; #[repr(C)] @@ -187,8 +247,12 @@ fn bindgen_test_layout__SysCallReturn() { ); } pub type SysCallReturn = _SysCallReturn; -pub type size_t = ::std::os::raw::c_ulong; -pub type pid_t = __pid_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Futex { + _unused: [u8; 0], +} +pub type Futex = _Futex; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _Thread { @@ -301,3 +365,366 @@ extern "C" { extern "C" { pub fn thread_isLeader(thread: *mut Thread) -> bool; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Timer { + _unused: [u8; 0], +} +pub type Timer = _Timer; +extern "C" { + pub fn process_registerCompatDescriptor( + proc_: *mut Process, + compatDesc: *mut CompatDescriptor, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn process_deregisterCompatDescriptor(proc_: *mut Process, handle: ::std::os::raw::c_int); +} +extern "C" { + pub fn process_getRegisteredCompatDescriptor( + proc_: *mut Process, + handle: ::std::os::raw::c_int, + ) -> *mut CompatDescriptor; +} +extern "C" { + pub fn process_getReadablePtr( + proc_: *mut Process, + thread: *mut Thread, + plugin_src: PluginPtr, + n: size_t, + ) -> *const ::std::os::raw::c_void; +} +extern "C" { + pub fn process_getWriteablePtr( + proc_: *mut Process, + thread: *mut Thread, + plugin_src: PluginPtr, + n: size_t, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn process_getMutablePtr( + proc_: *mut Process, + thread: *mut Thread, + plugin_src: PluginPtr, + n: size_t, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn descriptor_unref(data: gpointer); +} +extern "C" { + pub fn descriptor_setHandle(descriptor: *mut LegacyDescriptor, handle: gint); +} +pub use self::_TriggerType as TriggerType; +pub const _TriggerType_TRIGGER_NONE: _TriggerType = 0; +pub const _TriggerType_TRIGGER_DESCRIPTOR: _TriggerType = 1; +pub const _TriggerType_TRIGGER_POSIX_FILE: _TriggerType = 2; +pub const _TriggerType_TRIGGER_FUTEX: _TriggerType = 3; +pub type _TriggerType = i32; +pub type TriggerObject = _TriggerObject; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _TriggerObject { + pub as_pointer: *mut ::std::os::raw::c_void, + pub as_descriptor: *mut LegacyDescriptor, + pub as_file: *const PosixFileArc, + pub as_futex: *mut Futex, + _bindgen_union_align: u64, +} +#[test] +fn bindgen_test_layout__TriggerObject() { + assert_eq!( + ::std::mem::size_of::<_TriggerObject>(), + 8usize, + concat!("Size of: ", stringify!(_TriggerObject)) + ); + assert_eq!( + ::std::mem::align_of::<_TriggerObject>(), + 8usize, + concat!("Alignment of ", stringify!(_TriggerObject)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_TriggerObject>())).as_pointer as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_TriggerObject), + "::", + stringify!(as_pointer) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_TriggerObject>())).as_descriptor as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_TriggerObject), + "::", + stringify!(as_descriptor) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_TriggerObject>())).as_file as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_TriggerObject), + "::", + stringify!(as_file) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_TriggerObject>())).as_futex as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_TriggerObject), + "::", + stringify!(as_futex) + ) + ); +} +pub type Trigger = _Trigger; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct _Trigger { + pub type_: TriggerType, + pub object: TriggerObject, + pub status: Status, +} +#[test] +fn bindgen_test_layout__Trigger() { + assert_eq!( + ::std::mem::size_of::<_Trigger>(), + 24usize, + concat!("Size of: ", stringify!(_Trigger)) + ); + assert_eq!( + ::std::mem::align_of::<_Trigger>(), + 8usize, + concat!("Alignment of ", stringify!(_Trigger)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_Trigger>())).type_ as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_Trigger), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_Trigger>())).object as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(_Trigger), + "::", + stringify!(object) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_Trigger>())).status as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(_Trigger), + "::", + stringify!(status) + ) + ); +} +extern "C" { + pub fn syscallcondition_new(trigger: Trigger, timeout: *mut Timer) -> *mut SysCallCondition; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _SysCallHandler { + pub host: *mut Host, + pub process: *mut Process, + pub thread: *mut Thread, + pub timer: *mut Timer, + pub blockedSyscallNR: ::std::os::raw::c_long, + pub perfTimer: *mut GTimer, + pub perfSecondsCurrent: gdouble, + pub perfSecondsTotal: gdouble, + pub numSyscalls: ::std::os::raw::c_long, + pub referenceCount: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout__SysCallHandler() { + assert_eq!( + ::std::mem::size_of::<_SysCallHandler>(), + 80usize, + concat!("Size of: ", stringify!(_SysCallHandler)) + ); + assert_eq!( + ::std::mem::align_of::<_SysCallHandler>(), + 8usize, + concat!("Alignment of ", stringify!(_SysCallHandler)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).host as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(host) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).process as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(process) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).thread as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(thread) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).timer as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(timer) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<_SysCallHandler>())).blockedSyscallNR as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(blockedSyscallNR) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).perfTimer as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(perfTimer) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<_SysCallHandler>())).perfSecondsCurrent as *const _ as usize + }, + 48usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(perfSecondsCurrent) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<_SysCallHandler>())).perfSecondsTotal as *const _ as usize + }, + 56usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(perfSecondsTotal) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).numSyscalls as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(numSyscalls) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<_SysCallHandler>())).referenceCount as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(_SysCallHandler), + "::", + stringify!(referenceCount) + ) + ); +} +extern "C" { + pub fn syscallhandler_close( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_getpid( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_pipe(sys: *mut SysCallHandler, args: *const SysCallArgs) + -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_pipe2( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_pread64( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_pwrite64( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_read(sys: *mut SysCallHandler, args: *const SysCallArgs) + -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_set_tid_address( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_uname( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} +extern "C" { + pub fn syscallhandler_write( + sys: *mut SysCallHandler, + args: *const SysCallArgs, + ) -> SysCallReturn; +} diff --git a/src/main/host/descriptor/channel.c b/src/main/host/descriptor/channel.c index be28acfca3..1442421b7b 100644 --- a/src/main/host/descriptor/channel.c +++ b/src/main/host/descriptor/channel.c @@ -33,14 +33,14 @@ struct _Channel { MAGIC_DECLARE; }; -static Channel* _channel_fromDescriptor(Descriptor* descriptor) { +static Channel* _channel_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_PIPE || descriptor_getType(descriptor) == DT_UNIXSOCKET); return (Channel*)descriptor; } -static gboolean channel_close(Descriptor* descriptor) { - Channel* channel = _channel_fromDescriptor(descriptor); +static gboolean channel_close(LegacyDescriptor* descriptor) { + Channel* channel = _channel_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(channel); /* tell our link that we are done */ if(channel->linkedChannel) { @@ -58,13 +58,13 @@ static gboolean channel_close(Descriptor* descriptor) { return TRUE; } -static void channel_free(Descriptor* descriptor) { - Channel* channel = _channel_fromDescriptor(descriptor); +static void channel_free(LegacyDescriptor* descriptor) { + Channel* channel = _channel_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(channel); bytequeue_free(channel->buffer); - descriptor_clear((Descriptor*)channel); + descriptor_clear((LegacyDescriptor*)channel); MAGIC_CLEAR(channel); g_free(channel); @@ -88,14 +88,14 @@ static gssize channel_linkedWrite(Channel* channel, gconstpointer buffer, gsize channel->bufferLength += copyLength; /* we just got some data in our buffer */ - descriptor_adjustStatus((Descriptor*)channel, STATUS_DESCRIPTOR_READABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)channel, STATUS_DESCRIPTOR_READABLE, TRUE); return copyLength; } static gssize channel_sendUserData(Transport* transport, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { - Channel* channel = _channel_fromDescriptor((Descriptor*)transport); + Channel* channel = _channel_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(channel); /* the read end of a unidirectional pipe can not write! */ utility_assert(channel->type != CT_READONLY); @@ -113,7 +113,7 @@ static gssize channel_sendUserData(Transport* transport, gconstpointer buffer, /* our end cant write anymore if they returned error */ if(result <= (gssize)0) { - descriptor_adjustStatus((Descriptor*)channel, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)channel, STATUS_DESCRIPTOR_WRITABLE, FALSE); } return result; @@ -122,7 +122,7 @@ static gssize channel_sendUserData(Transport* transport, gconstpointer buffer, static gssize channel_receiveUserData(Transport* transport, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { - Channel* channel = _channel_fromDescriptor((Descriptor*)transport); + Channel* channel = _channel_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(channel); /* the write end of a unidirectional pipe can not read! */ utility_assert(channel->type != CT_WRITEONLY); @@ -146,7 +146,7 @@ static gssize channel_receiveUserData(Transport* transport, gpointer buffer, /* we are no longer readable if we have nothing left */ if(channel->bufferLength <= 0) { - descriptor_adjustStatus((Descriptor*)channel, STATUS_DESCRIPTOR_READABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)channel, STATUS_DESCRIPTOR_READABLE, FALSE); } return (gssize)numCopied; @@ -156,7 +156,7 @@ TransportFunctionTable channel_functions = { channel_close, channel_free, channel_sendUserData, channel_receiveUserData, MAGIC_VALUE}; -Channel* channel_new(ChannelType type, DescriptorType dtype) { +Channel* channel_new(ChannelType type, LegacyDescriptorType dtype) { Channel* channel = g_new0(Channel, 1); MAGIC_INIT(channel); @@ -166,9 +166,9 @@ Channel* channel_new(ChannelType type, DescriptorType dtype) { channel->buffer = bytequeue_new(8192); channel->bufferSize = CONFIG_PIPE_BUFFER_SIZE; - descriptor_adjustStatus((Descriptor*)channel, STATUS_DESCRIPTOR_ACTIVE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)channel, STATUS_DESCRIPTOR_ACTIVE, TRUE); if(!(type & CT_READONLY)) { - descriptor_adjustStatus((Descriptor*)channel, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)channel, STATUS_DESCRIPTOR_WRITABLE, TRUE); } worker_countObject(OBJECT_TYPE_CHANNEL, COUNTER_TYPE_NEW); diff --git a/src/main/host/descriptor/channel.h b/src/main/host/descriptor/channel.h index adbe40fe4c..c9297cc679 100644 --- a/src/main/host/descriptor/channel.h +++ b/src/main/host/descriptor/channel.h @@ -18,7 +18,7 @@ enum _ChannelType { typedef struct _Channel Channel; -Channel* channel_new(ChannelType type, DescriptorType dtype); +Channel* channel_new(ChannelType type, LegacyDescriptorType dtype); void channel_setLinkedChannel(Channel* channel, Channel* linkedChannel); Channel* channel_getLinkedChannel(Channel* channel); diff --git a/src/main/host/descriptor/descriptor.c b/src/main/host/descriptor/descriptor.c index c756f0acd5..90a836d66a 100644 --- a/src/main/host/descriptor/descriptor.c +++ b/src/main/host/descriptor/descriptor.c @@ -16,7 +16,7 @@ #include "main/utility/utility.h" #include "support/logger/logger.h" -void descriptor_init(Descriptor* descriptor, DescriptorType type, +void descriptor_init(LegacyDescriptor* descriptor, LegacyDescriptorType type, DescriptorFunctionTable* funcTable) { utility_assert(descriptor && funcTable); @@ -33,7 +33,7 @@ void descriptor_init(Descriptor* descriptor, DescriptorType type, worker_countObject(OBJECT_TYPE_DESCRIPTOR, COUNTER_TYPE_NEW); } -void descriptor_clear(Descriptor* descriptor) { +void descriptor_clear(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); if (descriptor->listeners) { g_hash_table_destroy(descriptor->listeners); @@ -41,7 +41,7 @@ void descriptor_clear(Descriptor* descriptor) { MAGIC_CLEAR(descriptor); } -static void _descriptor_free(Descriptor* descriptor) { +static void _descriptor_free(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); MAGIC_ASSERT(descriptor->funcTable); @@ -52,7 +52,7 @@ static void _descriptor_free(Descriptor* descriptor) { } void descriptor_ref(gpointer data) { - Descriptor* descriptor = data; + LegacyDescriptor* descriptor = data; MAGIC_ASSERT(descriptor); (descriptor->referenceCount)++; @@ -61,7 +61,7 @@ void descriptor_ref(gpointer data) { } void descriptor_unref(gpointer data) { - Descriptor* descriptor = data; + LegacyDescriptor* descriptor = data; MAGIC_ASSERT(descriptor); (descriptor->referenceCount)--; @@ -75,48 +75,48 @@ void descriptor_unref(gpointer data) { } } -void descriptor_close(Descriptor* descriptor) { +void descriptor_close(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); MAGIC_ASSERT(descriptor->funcTable); debug("Descriptor %i calling vtable close now", descriptor->handle); descriptor_adjustStatus(descriptor, STATUS_DESCRIPTOR_CLOSED, TRUE); if (descriptor->funcTable->close(descriptor) && descriptor->ownerProcess) { - process_deregisterDescriptor(descriptor->ownerProcess, descriptor); + process_deregisterLegacyDescriptor(descriptor->ownerProcess, descriptor); } } -gint descriptor_compare(const Descriptor* foo, const Descriptor* bar, gpointer user_data) { +gint descriptor_compare(const LegacyDescriptor* foo, const LegacyDescriptor* bar, gpointer user_data) { MAGIC_ASSERT(foo); MAGIC_ASSERT(bar); return foo->handle > bar->handle ? +1 : foo->handle == bar->handle ? 0 : -1; } -DescriptorType descriptor_getType(Descriptor* descriptor) { +LegacyDescriptorType descriptor_getType(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return descriptor->type; } -void descriptor_setHandle(Descriptor* descriptor, gint handle) { +void descriptor_setHandle(LegacyDescriptor* descriptor, gint handle) { MAGIC_ASSERT(descriptor); descriptor->handle = handle; } -gint descriptor_getHandle(Descriptor* descriptor) { +gint descriptor_getHandle(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return descriptor->handle; } -void descriptor_setOwnerProcess(Descriptor* descriptor, Process* ownerProcess) { +void descriptor_setOwnerProcess(LegacyDescriptor* descriptor, Process* ownerProcess) { MAGIC_ASSERT(descriptor); descriptor->ownerProcess = ownerProcess; } -Process* descriptor_getOwnerProcess(Descriptor* descriptor) { +Process* descriptor_getOwnerProcess(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return descriptor->ownerProcess; } -gint* descriptor_getHandleReference(Descriptor* descriptor) { +gint* descriptor_getHandleReference(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return &(descriptor->handle); } @@ -144,7 +144,7 @@ static gchar* _descriptor_statusToString(Status ds) { } #endif -static void _descriptor_handleStatusChange(Descriptor* descriptor, Status oldStatus) { +static void _descriptor_handleStatusChange(LegacyDescriptor* descriptor, Status oldStatus) { MAGIC_ASSERT(descriptor); /* Identify which bits changed, if any. */ @@ -187,7 +187,7 @@ static void _descriptor_handleStatusChange(Descriptor* descriptor, Status oldSta g_list_free(listenerList); } -void descriptor_adjustStatus(Descriptor* descriptor, Status status, gboolean doSetBits) { +void descriptor_adjustStatus(LegacyDescriptor* descriptor, Status status, gboolean doSetBits) { MAGIC_ASSERT(descriptor); Status oldStatus = descriptor->status; @@ -205,35 +205,35 @@ void descriptor_adjustStatus(Descriptor* descriptor, Status status, gboolean doS _descriptor_handleStatusChange(descriptor, oldStatus); } -Status descriptor_getStatus(Descriptor* descriptor) { +Status descriptor_getStatus(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return descriptor->status; } -void descriptor_addListener(Descriptor* descriptor, StatusListener* listener) { +void descriptor_addListener(LegacyDescriptor* descriptor, StatusListener* listener) { MAGIC_ASSERT(descriptor); /* We are storing a listener instance, so count the ref. */ statuslistener_ref(listener); g_hash_table_insert(descriptor->listeners, listener, listener); } -void descriptor_removeListener(Descriptor* descriptor, StatusListener* listener) { +void descriptor_removeListener(LegacyDescriptor* descriptor, StatusListener* listener) { MAGIC_ASSERT(descriptor); /* This will automatically call descriptorlistener_unref on the instance. */ g_hash_table_remove(descriptor->listeners, listener); } -gint descriptor_getFlags(Descriptor* descriptor) { +gint descriptor_getFlags(LegacyDescriptor* descriptor) { MAGIC_ASSERT(descriptor); return descriptor->flags; } -void descriptor_setFlags(Descriptor* descriptor, gint flags) { +void descriptor_setFlags(LegacyDescriptor* descriptor, gint flags) { MAGIC_ASSERT(descriptor); descriptor->flags = flags; } -void descriptor_addFlags(Descriptor* descriptor, gint flags) { +void descriptor_addFlags(LegacyDescriptor* descriptor, gint flags) { MAGIC_ASSERT(descriptor); descriptor->flags |= flags; } diff --git a/src/main/host/descriptor/descriptor.h b/src/main/host/descriptor/descriptor.h index 61196fe756..947ead671f 100644 --- a/src/main/host/descriptor/descriptor.h +++ b/src/main/host/descriptor/descriptor.h @@ -15,27 +15,27 @@ /* Initialize the parent parts of a new descriptor subclass. This call should * be paired with a call to clear() before freeing the subclass object. */ -void descriptor_init(Descriptor* descriptor, DescriptorType type, +void descriptor_init(LegacyDescriptor* descriptor, LegacyDescriptorType type, DescriptorFunctionTable* funcTable); /* Clear the bits that were initialized in init(). Following this call, the * descriptor becomes invalid and the subclass should be freed. */ -void descriptor_clear(Descriptor* descriptor); +void descriptor_clear(LegacyDescriptor* descriptor); void descriptor_ref(gpointer data); void descriptor_unref(gpointer data); -void descriptor_close(Descriptor* descriptor); -gint descriptor_compare(const Descriptor* foo, const Descriptor* bar, gpointer user_data); +void descriptor_close(LegacyDescriptor* descriptor); +gint descriptor_compare(const LegacyDescriptor* foo, const LegacyDescriptor* bar, gpointer user_data); -void descriptor_setHandle(Descriptor* descriptor, gint handle); -gint descriptor_getHandle(Descriptor* descriptor); -void descriptor_setOwnerProcess(Descriptor* descriptor, Process* ownerProcess); -Process* descriptor_getOwnerProcess(Descriptor* descriptor); -DescriptorType descriptor_getType(Descriptor* descriptor); -gint* descriptor_getHandleReference(Descriptor* descriptor); +void descriptor_setHandle(LegacyDescriptor* descriptor, gint handle); +gint descriptor_getHandle(LegacyDescriptor* descriptor); +void descriptor_setOwnerProcess(LegacyDescriptor* descriptor, Process* ownerProcess); +Process* descriptor_getOwnerProcess(LegacyDescriptor* descriptor); +LegacyDescriptorType descriptor_getType(LegacyDescriptor* descriptor); +gint* descriptor_getHandleReference(LegacyDescriptor* descriptor); -gint descriptor_getFlags(Descriptor* descriptor); -void descriptor_setFlags(Descriptor* descriptor, gint flags); -void descriptor_addFlags(Descriptor* descriptor, gint flags); +gint descriptor_getFlags(LegacyDescriptor* descriptor); +void descriptor_setFlags(LegacyDescriptor* descriptor, gint flags); +void descriptor_addFlags(LegacyDescriptor* descriptor, gint flags); /* * One of the main functions of the descriptor is to track its poll status, @@ -61,18 +61,18 @@ void descriptor_addFlags(Descriptor* descriptor, gint flags); * listener will trigger notifications via callback functions if the listener is * configured to monitor a bit that flipped. */ -void descriptor_adjustStatus(Descriptor* descriptor, Status status, gboolean doSetBits); +void descriptor_adjustStatus(LegacyDescriptor* descriptor, Status status, gboolean doSetBits); /* Gets the current status of the descriptor. */ -Status descriptor_getStatus(Descriptor* descriptor); +Status descriptor_getStatus(LegacyDescriptor* descriptor); /* Adds a listener that will get notified via descriptorlistener_onStatusChanged * on status transitions (bit flips). */ -void descriptor_addListener(Descriptor* descriptor, StatusListener* listener); +void descriptor_addListener(LegacyDescriptor* descriptor, StatusListener* listener); /* Remove the listener for our set of listeners that get notified on status * transitions (bit flips). */ -void descriptor_removeListener(Descriptor* descriptor, StatusListener* listener); +void descriptor_removeListener(LegacyDescriptor* descriptor, StatusListener* listener); #endif /* SHD_DESCRIPTOR_H_ */ diff --git a/src/main/host/descriptor/descriptor_table.c b/src/main/host/descriptor/descriptor_table.c index a74a51d9c4..876ca944ad 100644 --- a/src/main/host/descriptor/descriptor_table.c +++ b/src/main/host/descriptor/descriptor_table.c @@ -37,12 +37,16 @@ struct _DescriptorTable { MAGIC_DECLARE; }; +static void _descriptortable_value_free(void* data) { + compatdescriptor_free(data); +} + DescriptorTable* descriptortable_new() { DescriptorTable* table = malloc(sizeof(DescriptorTable)); *table = (DescriptorTable){ .descriptors = g_hash_table_new_full( - g_direct_hash, g_direct_equal, NULL, descriptor_unref), + g_direct_hash, g_direct_equal, NULL, _descriptortable_value_free), .availableIndices = g_queue_new(), .indexCounter = STDERR_FILENO, // the first allocatable index is 3 .referenceCount = 1, @@ -82,7 +86,7 @@ void descriptortable_unref(DescriptorTable* table) { } } -int descriptortable_add(DescriptorTable* table, Descriptor* descriptor) { +int descriptortable_add(DescriptorTable* table, CompatDescriptor* descriptor) { MAGIC_ASSERT(table); int index = 0; @@ -98,7 +102,7 @@ int descriptortable_add(DescriptorTable* table, Descriptor* descriptor) { g_hash_table_insert(table->descriptors, GINT_TO_POINTER(index), descriptor); - descriptor_setHandle(descriptor, index); + compatdescriptor_setHandle(descriptor, index); return index; } @@ -121,15 +125,15 @@ static void _descriptortable_trimIndicesTail(DescriptorTable* table) { } } -bool descriptortable_remove(DescriptorTable* table, Descriptor* descriptor) { +bool descriptortable_remove(DescriptorTable* table, int index) { MAGIC_ASSERT(table); - int index = descriptor_getHandle(descriptor); - if (g_hash_table_contains(table->descriptors, GINT_TO_POINTER(index))) { + CompatDescriptor* descriptor = descriptortable_get(table, index); + /* Make sure we do not operate on the descriptor after we remove it, * because that could cause it to be freed and invalidate it. */ - descriptor_setHandle(descriptor, 0); + compatdescriptor_setHandle(descriptor, 0); g_hash_table_remove(table->descriptors, GINT_TO_POINTER(index)); g_queue_insert_sorted(table->availableIndices, GINT_TO_POINTER(index), _descriptortable_compareInts, NULL); @@ -140,24 +144,24 @@ bool descriptortable_remove(DescriptorTable* table, Descriptor* descriptor) { } } -Descriptor* descriptortable_get(DescriptorTable* table, int index) { +CompatDescriptor* descriptortable_get(DescriptorTable* table, int index) { MAGIC_ASSERT(table); return g_hash_table_lookup(table->descriptors, GINT_TO_POINTER(index)); } void descriptortable_set(DescriptorTable* table, int index, - Descriptor* descriptor) { + CompatDescriptor* descriptor) { MAGIC_ASSERT(table); /* We may be replacing a descriptor that is already set at index. */ - Descriptor* existing = descriptortable_get(table, index); + CompatDescriptor* existing = descriptortable_get(table, index); if (existing) { - descriptor_setHandle(existing, 0); + compatdescriptor_setHandle(existing, 0); } /* Store the new descriptor at the index. */ g_hash_table_insert(table->descriptors, GINT_TO_POINTER(index), descriptor); - descriptor_setHandle(descriptor, index); + compatdescriptor_setHandle(descriptor, index); } /* TODO: remove this once the TCP layer is better designed. */ @@ -172,16 +176,28 @@ void descriptortable_shutdownHelper(DescriptorTable* table) { gpointer key, value; g_hash_table_iter_init(&iter, table->descriptors); while (g_hash_table_iter_next(&iter, &key, &value)) { - Descriptor* desc = value; - if (desc && desc->type == DT_TCPSOCKET) { + CompatDescriptor* desc = value; + + if (desc == NULL) { + continue; + } + + LegacyDescriptor* legacyDesc = compatdescriptor_asLegacy(value); + + /* if the descriptor was not a legacy descriptor */ + if (legacyDesc == NULL) { + continue; + } + + if (legacyDesc->type == DT_TCPSOCKET) { /* tcp servers and their children holds refs to each other. make * sure they all get freed by removing the refs in one direction */ - tcp_clearAllChildrenIfServer((TCP*)desc); - } else if (desc && (desc->type == DT_UNIXSOCKET || desc->type == DT_PIPE)) { + tcp_clearAllChildrenIfServer((TCP*)legacyDesc); + } else if (legacyDesc->type == DT_UNIXSOCKET || legacyDesc->type == DT_PIPE) { /* we need to correctly update the linked channel refs */ - channel_setLinkedChannel((Channel*)desc, NULL); - } else if (desc && desc->type == DT_EPOLL) { - epoll_clearWatchListeners((Epoll*)desc); + channel_setLinkedChannel((Channel*)legacyDesc, NULL); + } else if (legacyDesc->type == DT_EPOLL) { + epoll_clearWatchListeners((Epoll*)legacyDesc); } } } diff --git a/src/main/host/descriptor/descriptor_table.h b/src/main/host/descriptor/descriptor_table.h index f96f510640..50884b7d13 100644 --- a/src/main/host/descriptor/descriptor_table.h +++ b/src/main/host/descriptor/descriptor_table.h @@ -8,6 +8,7 @@ #include +#include "main/bindings/c/bindings.h" #include "main/host/descriptor/descriptor_types.h" /* Opaque object to store the state needed to implement the module. */ @@ -31,7 +32,7 @@ void descriptortable_unref(DescriptorTable* table); * NOTE: that this consumes a reference to the descriptor, so if you are storing * it outside of the descriptor table you will need to ref it after calling * this function. */ -int descriptortable_add(DescriptorTable* table, Descriptor* descriptor); +int descriptortable_add(DescriptorTable* table, CompatDescriptor* descriptor); /* Stop storing the descriptor so that it can no longer be referenced. The table * index that was used to store the descriptor is cleared from the descriptor @@ -41,18 +42,18 @@ int descriptortable_add(DescriptorTable* table, Descriptor* descriptor); * * NOTE: this will unref the descriptor which may cause it to be freed. If you * still need access to it, you should ref it before calling this function. */ -bool descriptortable_remove(DescriptorTable* table, Descriptor* descriptor); +bool descriptortable_remove(DescriptorTable* table, int index); /* Returns the descriptor at the given table index, or NULL if we are not * storing a descriptor at the given index. */ -Descriptor* descriptortable_get(DescriptorTable* table, int index); +CompatDescriptor* descriptortable_get(DescriptorTable* table, int index); /* Store the given descriptor at given index. Any previous descriptor that was * stored there will be removed and its table index will be cleared. This * unrefs any existing descriptor stored at index as in remove(), and consumes * a ref to the existing descriptor as in add(). */ void descriptortable_set(DescriptorTable* table, int index, - Descriptor* descriptor); + CompatDescriptor* descriptor); /* This is a helper function that handles some corner cases where some * descriptors are linked to each other and we must remove that link in diff --git a/src/main/host/descriptor/descriptor_types.h b/src/main/host/descriptor/descriptor_types.h index ceada6917d..1d1da85c7a 100644 --- a/src/main/host/descriptor/descriptor_types.h +++ b/src/main/host/descriptor/descriptor_types.h @@ -11,8 +11,8 @@ #include "main/host/status.h" #include "main/utility/utility.h" -typedef enum _DescriptorType DescriptorType; -enum _DescriptorType { +typedef enum _LegacyDescriptorType LegacyDescriptorType; +enum _LegacyDescriptorType { DT_NONE, DT_TCPSOCKET, DT_UDPSOCKET, @@ -24,7 +24,7 @@ enum _DescriptorType { DT_FILE, }; -typedef struct _Descriptor Descriptor; +typedef struct _LegacyDescriptor LegacyDescriptor; typedef struct _DescriptorFunctionTable DescriptorFunctionTable; /* required functions */ @@ -32,8 +32,8 @@ typedef struct _DescriptorFunctionTable DescriptorFunctionTable; /* Returns TRUE if the descriptor should be deregistered from the owning * process upon return from the function, FALSE if the child will handle * deregistration on its own. */ -typedef gboolean (*DescriptorCloseFunc)(Descriptor* descriptor); -typedef void (*DescriptorFreeFunc)(Descriptor* descriptor); +typedef gboolean (*DescriptorCloseFunc)(LegacyDescriptor* descriptor); +typedef void (*DescriptorFreeFunc)(LegacyDescriptor* descriptor); /* * Virtual function table for base descriptor, storing pointers to required @@ -45,11 +45,11 @@ struct _DescriptorFunctionTable { MAGIC_DECLARE; }; -struct _Descriptor { +struct _LegacyDescriptor { DescriptorFunctionTable* funcTable; Process* ownerProcess; gint handle; - DescriptorType type; + LegacyDescriptorType type; Status status; GHashTable* listeners; gint referenceCount; diff --git a/src/main/host/descriptor/epoll.c b/src/main/host/descriptor/epoll.c index 61edb14c69..7a8af57579 100644 --- a/src/main/host/descriptor/epoll.c +++ b/src/main/host/descriptor/epoll.c @@ -10,6 +10,7 @@ #include #include +#include "main/bindings/c/bindings.h" #include "main/core/support/definitions.h" #include "main/core/support/object_counter.h" #include "main/core/work/task.h" @@ -55,11 +56,27 @@ enum _EpollWatchFlags { EWF_ONESHOT_REPORTED = 1 << 12, }; +typedef enum _EpollWatchTypes EpollWatchTypes; +enum _EpollWatchTypes { + EWT_LEGACY_DESCRIPTOR, + EWT_POSIX_FILE, +}; + +typedef union _EpollWatchObject EpollWatchObject; +union _EpollWatchObject { + LegacyDescriptor* as_descriptor; + const PosixFileArc* as_file; +}; + typedef struct _EpollWatch EpollWatch; struct _EpollWatch { - /* the shadow descriptor we are watching for events */ - Descriptor* descriptor; - /* The listener that notifies us when status changes. */ + /* the type of object we are watching (for example descriptor or file) */ + EpollWatchTypes watchType; + /* the object we are watching for events */ + EpollWatchObject watchObject; + /* the fd of the object we are watching */ + int fd; + /* the listener that notifies us when status changes */ StatusListener* listener; /* holds the actual event info */ struct epoll_event event; @@ -69,9 +86,19 @@ struct _EpollWatch { MAGIC_DECLARE; }; +/* the epoll tables are indexed by the (fd, objectPtr) tuple so you can add the same object + * multiple times under different fds, and you can add the same fd multiple times as long as the + * object is different */ +typedef struct _EpollKey EpollKey; +struct _EpollKey { + int fd; + /* store the pointer as an int so that we never accidentally de-reference it */ + uintptr_t objectPtr; +}; + struct _Epoll { /* epoll itself is also a descriptor */ - Descriptor super; + LegacyDescriptor super; /* holds the wrappers for the descriptors we are watching for events */ GHashTable* watching; @@ -82,29 +109,65 @@ struct _Epoll { MAGIC_DECLARE; }; +static EpollKey* _epollkey_new(int fd, uintptr_t objectPtr) { + EpollKey* key = g_new0(EpollKey, 1); + utility_assert(key); + + key->fd = fd; + key->objectPtr = objectPtr; + + return key; +} + +static guint _epollkey_hash(gconstpointer ptr) { + const EpollKey* key = ptr; + return g_int_hash(&key->fd) ^ g_int_hash(&key->objectPtr); +} + +static gboolean _epollkey_equal(gconstpointer ptr_1, gconstpointer ptr_2) { + const EpollKey* key_1 = ptr_1; + const EpollKey* key_2 = ptr_2; + return key_1->fd == key_2->fd && key_1->objectPtr == key_2->objectPtr; +} + /* forward declaration */ -static void _epoll_descriptorStatusChanged(Epoll* epoll, - Descriptor* descriptor); +static void _epoll_descriptorStatusChanged(Epoll* epoll, const EpollKey* key); -static EpollWatch* _epollwatch_new(Epoll* epoll, Descriptor* descriptor, - const struct epoll_event* event) { +static EpollWatch* _epollwatch_new(Epoll* epoll, int fd, EpollWatchTypes type, + EpollWatchObject object, const struct epoll_event* event) { EpollWatch* watch = g_new0(EpollWatch, 1); MAGIC_INIT(watch); utility_assert(event); /* ref it for the EpollWatch, which also covers the listener reference * (which is freed below in _epollwatch_free) */ - descriptor_ref(descriptor); + if (type == EWT_LEGACY_DESCRIPTOR) { + descriptor_ref(object.as_descriptor); + } - watch->descriptor = descriptor; + watch->watchType = type; + watch->watchObject = object; + watch->fd = fd; watch->event = *event; watch->referenceCount = 1; + uintptr_t objectPtr; + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + objectPtr = (uintptr_t)(void*)object.as_descriptor; + } else if (watch->watchType == EWT_POSIX_FILE) { + objectPtr = (uintptr_t)(void*)object.as_file; + } else { + warning("Unrecognized epoll watch type: %d", type); + objectPtr = (uintptr_t)NULL; + } + + EpollKey *key = _epollkey_new(fd, objectPtr); + /* Create the listener and ref the objects held by the listener. * The watch object already holds a ref to the descriptor so we * don't ref it again. */ watch->listener = statuslistener_new( - (StatusCallbackFunc)_epoll_descriptorStatusChanged, epoll, NULL, descriptor, NULL); + (StatusCallbackFunc)_epoll_descriptorStatusChanged, epoll, NULL, key, g_free); return watch; } @@ -113,7 +176,12 @@ static void _epollwatch_free(EpollWatch* watch) { MAGIC_ASSERT(watch); statuslistener_unref(watch->listener); - descriptor_unref(watch->descriptor); + + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + descriptor_unref(watch->watchObject.as_descriptor); + } else if (watch->watchType == EWT_POSIX_FILE) { + posixfile_drop(watch->watchObject.as_file); + } MAGIC_CLEAR(watch); g_free(watch); @@ -132,21 +200,21 @@ static void _epollwatch_unref(EpollWatch* watch) { } } -static Epoll* _epoll_fromDescriptor(Descriptor* descriptor) { +static Epoll* _epoll_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_EPOLL); return (Epoll*)descriptor; } /* should only be called from descriptor dereferencing the functionTable */ -static void _epoll_free(Descriptor* descriptor) { - Epoll* epoll = _epoll_fromDescriptor(descriptor); +static void _epoll_free(LegacyDescriptor* descriptor) { + Epoll* epoll = _epoll_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(epoll); /* this unrefs all of the remaining watches */ g_hash_table_destroy(epoll->watching); g_hash_table_destroy(epoll->ready); - descriptor_clear((Descriptor*)epoll); + descriptor_clear((LegacyDescriptor*)epoll); MAGIC_CLEAR(epoll); g_free(epoll); @@ -164,12 +232,17 @@ void epoll_clearWatchListeners(Epoll* epoll) { EpollWatch* watch = value; MAGIC_ASSERT(watch); statuslistener_setMonitorStatus(watch->listener, STATUS_NONE, SLF_NEVER); - descriptor_removeListener(watch->descriptor, watch->listener); + + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + descriptor_removeListener(watch->watchObject.as_descriptor, watch->listener); + } else if (watch->watchType == EWT_POSIX_FILE) { + posixfile_removeListener(watch->watchObject.as_file, watch->listener); + } } } -static gboolean _epoll_close(Descriptor* descriptor) { - Epoll* epoll = _epoll_fromDescriptor(descriptor); +static gboolean _epoll_close(LegacyDescriptor* descriptor) { + Epoll* epoll = _epoll_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(epoll); epoll_clearWatchListeners(epoll); return TRUE; @@ -185,8 +258,8 @@ Epoll* epoll_new() { descriptor_init(&(epoll->super), DT_EPOLL, &epollFunctions); /* allocate backend needed for managing events for this descriptor */ - epoll->watching = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, (GDestroyNotify)_epollwatch_unref); - epoll->ready = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, (GDestroyNotify)_epollwatch_unref); + epoll->watching = g_hash_table_new_full(_epollkey_hash, _epollkey_equal, g_free, (GDestroyNotify)_epollwatch_unref); + epoll->ready = g_hash_table_new_full(_epollkey_hash, _epollkey_equal, g_free, (GDestroyNotify)_epollwatch_unref); /* the epoll descriptor itself is always able to be epolled */ descriptor_adjustStatus(&(epoll->super), STATUS_DESCRIPTOR_ACTIVE, TRUE); @@ -212,7 +285,13 @@ static void _epollwatch_updateStatus(EpollWatch* watch) { watch->flags = 0; /* check shadow descriptor status */ - Status status = descriptor_getStatus(watch->descriptor); + Status status; + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + status = descriptor_getStatus(watch->watchObject.as_descriptor); + } else if (watch->watchType == EWT_POSIX_FILE) { + status = posixfile_getStatus(watch->watchObject.as_file); + } + watch->flags |= (status & STATUS_DESCRIPTOR_ACTIVE) ? EWF_ACTIVE : EWF_NONE; watch->flags |= (status & STATUS_DESCRIPTOR_READABLE) ? EWF_READABLE : EWF_NONE; watch->flags |= (status & STATUS_DESCRIPTOR_WRITABLE) ? EWF_WRITEABLE : EWF_NONE; @@ -285,16 +364,55 @@ static const gchar* _epoll_operationToStr(gint op) { } } -gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, +static void _getWatchObject(CompatDescriptor* descriptor, EpollWatchTypes* watchType, + EpollWatchObject* watchObject) { + LegacyDescriptor* legacyDescriptor = compatdescriptor_asLegacy(descriptor); + + /* if the compat descriptor is for a legacy descriptor */ + if (legacyDescriptor != NULL) { + *watchType = EWT_LEGACY_DESCRIPTOR; + watchObject->as_descriptor = legacyDescriptor; + } else { + const PosixFileArc* file = compatdescriptor_newRefPosixFile(descriptor); + /* if the compat descriptor is for a posix file object */ + if (file != NULL) { + *watchType = EWT_POSIX_FILE; + watchObject->as_file = file; + } else { + error("unrecognized watch object"); + } + } +} + +gint epoll_control(Epoll* epoll, gint operation, int fd, CompatDescriptor* descriptor, const struct epoll_event* event) { MAGIC_ASSERT(epoll); - debug("epoll descriptor %i, operation %s, descriptor %i", - epoll->super.handle, _epoll_operationToStr(operation), descriptor->handle); + debug("epoll descriptor %i, operation %s, descriptor %i", epoll->super.handle, + _epoll_operationToStr(operation), fd); + + EpollWatchTypes watchType; + EpollWatchObject watchObject; + + /* get the watch type and object for the provided descriptor object */ + _getWatchObject(descriptor, &watchType, &watchObject); - gint* watchHandleRef = descriptor_getHandleReference(descriptor); + /* this on-stack key can be used for lookups only, not new entries */ + EpollKey key; + key.fd = fd; + switch (watchType) { + case EWT_LEGACY_DESCRIPTOR: + key.objectPtr = (uintptr_t)(void*)watchObject.as_descriptor; + break; + case EWT_POSIX_FILE: + key.objectPtr = (uintptr_t)(void*)watchObject.as_file; + break; + default: + error("unrecognized watch type"); + return -ENOENT; + } - EpollWatch* watch = g_hash_table_lookup(epoll->watching, watchHandleRef); + EpollWatch* watch = g_hash_table_lookup(epoll->watching, &key); switch (operation) { case EPOLL_CTL_ADD: { @@ -305,9 +423,10 @@ gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, } /* start watching for status changes */ - watch = _epollwatch_new(epoll, descriptor, event); + watch = _epollwatch_new(epoll, fd, watchType, watchObject, event); watch->flags |= EWF_WATCHING; - g_hash_table_replace(epoll->watching, watchHandleRef, watch); + gpointer new_key = _epollkey_new(key.fd, key.objectPtr); + g_hash_table_replace(epoll->watching, new_key, watch); /* It's added, so we need to listen for changes. Here we listen for * all statuses, because epoll will filter what it needs. @@ -318,10 +437,14 @@ gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, STATUS_DESCRIPTOR_READABLE | STATUS_DESCRIPTOR_WRITABLE, SLF_ALWAYS); - descriptor_addListener(watch->descriptor, watch->listener); + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + descriptor_addListener(watch->watchObject.as_descriptor, watch->listener); + } else if (watch->watchType == EWT_POSIX_FILE) { + posixfile_addListener(watch->watchObject.as_file, watch->listener); + } - /* initiate a callback if the new watched descriptor is ready */ - _epoll_descriptorStatusChanged(epoll, descriptor); + /* initiate a callback if the new watched object is ready */ + _epoll_descriptorStatusChanged(epoll, &key); break; } @@ -341,8 +464,8 @@ gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, watch->flags &= ~EWF_EDGETRIGGER_REPORTED; watch->flags &= ~EWF_ONESHOT_REPORTED; - /* initiate a callback if the new event type on the watched descriptor is ready */ - _epoll_descriptorStatusChanged(epoll, descriptor); + /* initiate a callback if the new event type on the watched object is ready */ + _epoll_descriptorStatusChanged(epoll, &key); break; } @@ -358,11 +481,15 @@ gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, /* its deleted, so stop listening for updates */ statuslistener_setMonitorStatus(watch->listener, STATUS_NONE, SLF_NEVER); - descriptor_removeListener(watch->descriptor, watch->listener); + if (watch->watchType == EWT_LEGACY_DESCRIPTOR) { + descriptor_removeListener(watch->watchObject.as_descriptor, watch->listener); + } else if (watch->watchType == EWT_POSIX_FILE) { + posixfile_removeListener(watch->watchObject.as_file, watch->listener); + } /* unref gets called on the watch when it is removed from these tables */ - g_hash_table_remove(epoll->ready, watchHandleRef); - g_hash_table_remove(epoll->watching, watchHandleRef); + g_hash_table_remove(epoll->ready, &key); + g_hash_table_remove(epoll->watching, &key); break; } @@ -441,31 +568,25 @@ gint epoll_getEvents(Epoll* epoll, struct epoll_event* eventArray, gint eventArr return 0; } -static void _epoll_descriptorStatusChanged(Epoll* epoll, - Descriptor* descriptor) { +static void _epoll_descriptorStatusChanged(Epoll* epoll, const EpollKey* key) { MAGIC_ASSERT(epoll); - /* make sure we are actually watching the descriptor */ - gint* watchHandleRef = descriptor_getHandleReference(descriptor); - EpollWatch* watch = g_hash_table_lookup(epoll->watching, watchHandleRef); - - /* if we are not watching, its an error because we shouldn't be listening */ - utility_assert(watch && (watch->descriptor == descriptor)); - - debug("status changed in epoll %i for descriptor %i", descriptor_getHandle(&epoll->super), descriptor->handle); + EpollWatch* watch = g_hash_table_lookup(epoll->watching, key); + debug("status changed in epoll %i for descriptor %i", descriptor_getHandle(&epoll->super), watch->fd); /* update the status for the child watch fd */ _epollwatch_updateStatus(watch); /* check if its ready (has an event to report) now */ if(_epollwatch_isReady(watch)) { - if(!g_hash_table_contains(epoll->ready, watchHandleRef)) { + if(!g_hash_table_contains(epoll->ready, key)) { _epollwatch_ref(watch); - g_hash_table_replace(epoll->ready, watchHandleRef, watch); + gpointer keyCopy = _epollkey_new(key->fd, key->objectPtr); + g_hash_table_replace(epoll->ready, keyCopy, watch); } } else { /* this calls unref on the watch if its in the table */ - g_hash_table_remove(epoll->ready, watchHandleRef); + g_hash_table_remove(epoll->ready, key); } /* check the status on the parent epoll fd and adjust as needed */ diff --git a/src/main/host/descriptor/epoll.h b/src/main/host/descriptor/epoll.h index f2c900a337..e37c28f252 100644 --- a/src/main/host/descriptor/epoll.h +++ b/src/main/host/descriptor/epoll.h @@ -17,7 +17,7 @@ typedef struct _Epoll Epoll; /* free this with descriptor_free() */ Epoll* epoll_new(); -gint epoll_control(Epoll* epoll, gint operation, Descriptor* descriptor, +gint epoll_control(Epoll* epoll, gint operation, int fd, CompatDescriptor* descriptor, const struct epoll_event* event); gint epoll_getEvents(Epoll* epoll, struct epoll_event* eventArray, gint eventArrayLength, gint* nEvents); diff --git a/src/main/host/descriptor/eventd.c b/src/main/host/descriptor/eventd.c index 76d34acfab..3bf9b989de 100644 --- a/src/main/host/descriptor/eventd.c +++ b/src/main/host/descriptor/eventd.c @@ -17,7 +17,7 @@ #include "support/logger/logger.h" struct _EventD { - Descriptor super; + LegacyDescriptor super; uint64_t counter; bool is_closed; @@ -26,13 +26,13 @@ struct _EventD { MAGIC_DECLARE; }; -static EventD* _eventfd_fromDescriptor(Descriptor* descriptor) { +static EventD* _eventfd_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_EVENTD); return (EventD*)descriptor; } -static gboolean _eventd_close(Descriptor* descriptor) { - EventD* eventd = _eventfd_fromDescriptor(descriptor); +static gboolean _eventd_close(LegacyDescriptor* descriptor) { + EventD* eventd = _eventfd_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(eventd); debug("event fd %i closing now", eventd->super.handle); @@ -47,11 +47,11 @@ static gboolean _eventd_close(Descriptor* descriptor) { } } -static void _eventd_free(Descriptor* descriptor) { - EventD* eventd = _eventfd_fromDescriptor(descriptor); +static void _eventd_free(LegacyDescriptor* descriptor) { + EventD* eventd = _eventfd_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(eventd); - descriptor_clear((Descriptor*)eventd); + descriptor_clear((LegacyDescriptor*)eventd); MAGIC_CLEAR(eventd); free(eventd); diff --git a/src/main/host/descriptor/file.c b/src/main/host/descriptor/file.c index 072278380b..a500efe2fa 100644 --- a/src/main/host/descriptor/file.c +++ b/src/main/host/descriptor/file.c @@ -44,7 +44,7 @@ enum _FileType { struct _File { /* File is a sub-type of a descriptor. */ - Descriptor super; + LegacyDescriptor super; FileType type; /* Info related to our OS-backed file. */ struct { @@ -71,7 +71,7 @@ char* file_getAbsolutePath(File* file) { return file->osfile.abspath; } -static inline File* _file_descriptorToFile(Descriptor* desc) { +static inline File* _file_descriptorToFile(LegacyDescriptor* desc) { utility_assert(descriptor_getType(desc) == DT_FILE); File* file = (File*)desc; MAGIC_ASSERT(file); @@ -110,7 +110,7 @@ static void _file_closeHelper(File* file) { } } -static gboolean _file_close(Descriptor* desc) { +static gboolean _file_close(LegacyDescriptor* desc) { File* file = _file_descriptorToFile(desc); debug("Closing file %i with os-backed file %i", _file_getFD(file), @@ -124,7 +124,7 @@ static gboolean _file_close(Descriptor* desc) { return TRUE; } -static void _file_free(Descriptor* desc) { +static void _file_free(LegacyDescriptor* desc) { File* file = _file_descriptorToFile(desc); debug("Freeing file %i with os-backed file %i", _file_getFD(file), @@ -136,7 +136,7 @@ static void _file_free(Descriptor* desc) { free(file->osfile.abspath); } - descriptor_clear((Descriptor*)file); + descriptor_clear((LegacyDescriptor*)file); MAGIC_CLEAR(file); free(file); diff --git a/src/main/host/descriptor/mod.rs b/src/main/host/descriptor/mod.rs new file mode 100644 index 0000000000..93d554581b --- /dev/null +++ b/src/main/host/descriptor/mod.rs @@ -0,0 +1,466 @@ +use atomic_refcell::AtomicRefCell; +use std::sync::Arc; + +use crate::cshadow as c; +use crate::utility::event_queue::{EventQueue, EventSource, Handle}; + +pub mod pipe; + +/// A trait we can use as a compile-time check to make sure that an object is Send. +trait IsSend: Send {} + +/// A trait we can use as a compile-time check to make sure that an object is Sync. +trait IsSync: Sync {} + +/// A type that allows us to make a pointer Send + Sync since there is no way +/// to add these traits to the pointer itself. +#[derive(Clone, Copy)] +pub struct SyncSendPointer(*mut T); + +unsafe impl Send for SyncSendPointer {} +unsafe impl Sync for SyncSendPointer {} + +impl SyncSendPointer { + /// Get the pointer. + pub fn ptr(&self) -> *mut T { + self.0 + } + + /// Get a mutable reference to the pointer. + pub fn ptr_ref(&mut self) -> &mut *mut T { + &mut self.0 + } +} + +#[derive(PartialEq)] +pub enum SyscallReturn { + Success(i32), + Error(nix::errno::Errno), +} + +bitflags::bitflags! { + /// These are flags that can potentially be changed from the plugin (analagous to the Linux + /// `filp->f_flags` status flags). Not all `O_` flags are valid here. For example file access + /// mode flags (ex: `O_RDWR`) are stored elsewhere, and file creation flags (ex: `O_CREAT`) + /// are not stored anywhere. Many of these can be represented in different ways, for example: + /// `O_NONBLOCK`, `SOCK_NONBLOCK`, `EFD_NONBLOCK`, `GRND_NONBLOCK`, etc, and not all have the + /// same value. + pub struct FileFlags: libc::c_int { + const NONBLOCK = libc::O_NONBLOCK; + const APPEND = libc::O_APPEND; + const ASYNC = libc::O_ASYNC; + const DIRECT = libc::O_DIRECT; + const NOATIME = libc::O_NOATIME; + } +} + +bitflags::bitflags! { + /// These are flags that should generally not change (analagous to the Linux `filp->f_mode`). + /// Since the plugin will never see these values and they're not exposed by the kernel, we + /// don't match the kernel `FMODE_` values here. + /// + /// Examples: https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L111 + pub struct FileMode: u32 { + const READ = 0b00000001; + const WRITE = 0b00000010; + } +} + +#[derive(Clone)] +pub enum NewStatusListenerFilter { + Never, + OffToOn, + OnToOff, + Always, +} + +/// A wrapper for a `*mut c::StatusListener` that increments its ref count when created, +/// and decrements when dropped. +struct LegacyStatusListener(SyncSendPointer); + +impl LegacyStatusListener { + fn new(ptr: *mut c::StatusListener) -> Self { + assert!(!ptr.is_null()); + unsafe { c::statuslistener_ref(ptr) }; + Self(SyncSendPointer(ptr)) + } + + fn ptr(&self) -> *mut c::StatusListener { + self.0.ptr() + } +} + +impl Drop for LegacyStatusListener { + fn drop(&mut self) { + unsafe { c::statuslistener_unref(self.0.ptr()) }; + } +} + +/// Stores event listener handles so that `c::StatusListener` objects can subscribe to events. +struct LegacyListenerHelper { + handle_map: std::collections::HashMap>, +} + +impl LegacyListenerHelper { + fn new() -> Self { + Self { + handle_map: std::collections::HashMap::new(), + } + } + + fn add_listener( + &mut self, + ptr: *mut c::StatusListener, + event_source: &mut EventSource<(c::Status, c::Status)>, + ) { + assert!(!ptr.is_null()); + + // if it's already listening, don't add a second time + if self.handle_map.contains_key(&(ptr as usize)) { + return; + } + + // this will ref the pointer and unref it when the closure is dropped + let ptr_wrapper = LegacyStatusListener::new(ptr); + + let handle = event_source.add_listener(move |(status, changed), _event_queue| unsafe { + c::statuslistener_onStatusChanged(ptr_wrapper.ptr(), status, changed) + }); + + // use a usize as the key so we don't accidentally deref the pointer + self.handle_map.insert(ptr as usize, handle); + } + + fn remove_listener(&mut self, ptr: *mut c::StatusListener) { + assert!(!ptr.is_null()); + self.handle_map.remove(&(ptr as usize)); + } +} + +/// A specified event source that passes a status and the changed bits to the function, but only if +/// the monitored bits have changed and if the change the filter is satisfied. +struct StatusEventSource { + inner: EventSource<(c::Status, c::Status)>, + legacy_helper: LegacyListenerHelper, +} + +impl StatusEventSource { + pub fn new() -> Self { + Self { + inner: EventSource::new(), + legacy_helper: LegacyListenerHelper::new(), + } + } + + pub fn add_listener( + &mut self, + monitoring: c::Status, + filter: NewStatusListenerFilter, + notify_fn: impl Fn(c::Status, c::Status, &mut EventQueue) + Send + Sync + 'static, + ) -> Handle<(c::Status, c::Status)> { + self.inner + .add_listener(move |(status, changed), event_queue| { + // true if any of the bits we're monitoring have changed + let flipped = monitoring & changed != 0; + + // true if any of the bits we're monitoring are set + let on = monitoring & status != 0; + + let notify = match filter { + // at least one monitored bit is on, and at least one has changed + NewStatusListenerFilter::OffToOn => flipped && on, + // all monitored bits are off, and at least one has changed + NewStatusListenerFilter::OnToOff => flipped && !on, + // at least one monitored bit has changed + NewStatusListenerFilter::Always => flipped, + NewStatusListenerFilter::Never => false, + }; + + if !notify { + return; + } + + (notify_fn)(status, changed, event_queue) + }) + } + + pub fn add_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + self.legacy_helper.add_listener(ptr, &mut self.inner); + } + + pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + self.legacy_helper.remove_listener(ptr); + } + + pub fn notify_listeners( + &mut self, + status: c::Status, + changed: c::Status, + event_queue: &mut EventQueue, + ) { + self.inner.notify_listeners((status, changed), event_queue) + } +} + +/// Represents a POSIX description, or a Linux "struct file". +pub enum PosixFile { + Pipe(pipe::PipeFile), +} + +// will not compile if `PosixFile` is not Send + Sync +impl IsSend for PosixFile {} +impl IsSync for PosixFile {} + +impl PosixFile { + pub fn read(&mut self, bytes: &mut [u8], event_queue: &mut EventQueue) -> SyscallReturn { + match self { + Self::Pipe(f) => f.read(bytes, event_queue), + } + } + + pub fn write(&mut self, bytes: &[u8], event_queue: &mut EventQueue) -> SyscallReturn { + match self { + Self::Pipe(f) => f.write(bytes, event_queue), + } + } + + pub fn status(&self) -> c::Status { + match self { + Self::Pipe(f) => f.status(), + } + } + + pub fn get_flags(&self) -> FileFlags { + match self { + Self::Pipe(f) => f.get_flags(), + } + } + + pub fn set_flags(&mut self, flags: FileFlags) { + match self { + Self::Pipe(f) => f.set_flags(flags), + } + } + + pub fn add_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + match self { + Self::Pipe(f) => f.add_legacy_listener(ptr), + } + } + + pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + match self { + Self::Pipe(f) => f.remove_legacy_listener(ptr), + } + } +} + +bitflags::bitflags! { + // Linux only supports a single descriptor flag: + // https://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html + pub struct DescriptorFlags: u32 { + const CLOEXEC = libc::FD_CLOEXEC as u32; + } +} + +#[derive(Clone)] +pub struct Descriptor { + file: Arc>, + flags: DescriptorFlags, +} + +impl Descriptor { + pub fn new(file: Arc>) -> Self { + Self { + file, + flags: DescriptorFlags::empty(), + } + } + + pub fn get_file(&self) -> &Arc> { + &self.file + } + + pub fn get_flags(&self) -> DescriptorFlags { + self.flags + } + + pub fn set_flags(&mut self, flags: DescriptorFlags) { + self.flags = flags; + } +} + +// don't implement copy or clone without considering the legacy descriptor's ref count +pub enum CompatDescriptor { + New(Descriptor), + Legacy(SyncSendPointer), +} + +// will not compile if `CompatDescriptor` is not Send + Sync +impl IsSend for CompatDescriptor {} +impl IsSync for CompatDescriptor {} + +impl Drop for CompatDescriptor { + fn drop(&mut self) { + if let CompatDescriptor::Legacy(d) = self { + // unref the legacy descriptor object + unsafe { c::descriptor_unref(d.ptr() as *mut core::ffi::c_void) }; + } + } +} + +mod export { + use super::*; + + /// An opaque type used when passing `*const AtomicRefCell` to C. + #[no_mangle] + pub enum PosixFileArc {} + + /// The new compat descriptor takes ownership of the reference to the legacy descriptor and + /// does not increment its ref count, but will decrement the ref count when this compat + /// descriptor is freed/dropped. + #[no_mangle] + pub extern "C" fn compatdescriptor_fromLegacy( + legacy_descriptor: *mut c::LegacyDescriptor, + ) -> *mut CompatDescriptor { + assert!(!legacy_descriptor.is_null()); + + let descriptor = CompatDescriptor::Legacy(SyncSendPointer(legacy_descriptor)); + Box::into_raw(Box::new(descriptor)) + } + + /// If the compat descriptor is a legacy descriptor, returns a pointer to the legacy + /// descriptor object. Otherwise returns NULL. The legacy descriptor's ref count is not + /// modified, so the pointer must not outlive the lifetime of the compat descriptor. + #[no_mangle] + pub extern "C" fn compatdescriptor_asLegacy( + descriptor: *const CompatDescriptor, + ) -> *mut c::LegacyDescriptor { + assert!(!descriptor.is_null()); + + let descriptor = unsafe { &*descriptor }; + + if let CompatDescriptor::Legacy(d) = descriptor { + d.ptr() + } else { + std::ptr::null_mut() + } + } + + /// When the compat descriptor is freed/dropped, it will decrement the legacy descriptor's + /// ref count. + #[no_mangle] + pub extern "C" fn compatdescriptor_free(descriptor: *mut CompatDescriptor) { + if descriptor.is_null() { + return; + } + + let descriptor = unsafe { &mut *descriptor }; + unsafe { Box::from_raw(descriptor) }; + } + + /// This is a no-op for non-legacy descriptors. + #[no_mangle] + pub extern "C" fn compatdescriptor_setHandle( + descriptor: *mut CompatDescriptor, + handle: libc::c_int, + ) { + assert!(!descriptor.is_null()); + + let descriptor = unsafe { &mut *descriptor }; + + if let CompatDescriptor::Legacy(d) = descriptor { + unsafe { c::descriptor_setHandle(d.ptr(), handle) } + } + + // new descriptor types don't store their file handle, so do nothing + } + + /// If the compat descriptor is a new descriptor, returns a pointer to the reference-counted + /// posix file object. Otherwise returns NULL. The posix file object's ref count is not + /// modified, so the pointer must not outlive the lifetime of the compat descriptor. + #[no_mangle] + pub extern "C" fn compatdescriptor_borrowPosixFile( + descriptor: *mut CompatDescriptor, + ) -> *const PosixFileArc { + assert!(!descriptor.is_null()); + + let descriptor = unsafe { &mut *descriptor }; + + (match descriptor { + CompatDescriptor::Legacy(_) => std::ptr::null_mut(), + CompatDescriptor::New(d) => Arc::as_ptr(d.get_file()), + }) as *const PosixFileArc + } + + /// If the compat descriptor is a new descriptor, returns a pointer to the reference-counted + /// posix file object. Otherwise returns NULL. The posix file object's ref count is + /// incremented, so the pointer must always later be passed to `posixfile_drop()`, otherwise + /// the memory will leak. + #[no_mangle] + pub extern "C" fn compatdescriptor_newRefPosixFile( + descriptor: *mut CompatDescriptor, + ) -> *const PosixFileArc { + assert!(!descriptor.is_null()); + + let descriptor = unsafe { &mut *descriptor }; + + (match descriptor { + CompatDescriptor::Legacy(_) => std::ptr::null_mut(), + CompatDescriptor::New(d) => Arc::into_raw(Arc::clone(&d.get_file())), + }) as *const PosixFileArc + } + + /// Decrement the ref count of the posix file object. The pointer must not be used after + /// calling this function. + #[no_mangle] + pub extern "C" fn posixfile_drop(file: *const PosixFileArc) { + assert!(!file.is_null()); + + unsafe { Arc::from_raw(file as *const AtomicRefCell) }; + } + + /// Get the status of the posix file object. + #[allow(unused_variables)] + #[no_mangle] + pub extern "C" fn posixfile_getStatus(file: *const PosixFileArc) -> c::Status { + assert!(!file.is_null()); + + let file = file as *const AtomicRefCell; + let file = unsafe { &*file }; + + file.borrow().status() + } + + /// Add a status listener to the posix file object. This will increment the status + /// listener's ref count, and will decrement the ref count when this status listener is + /// removed or when the posix file is freed/dropped. + #[no_mangle] + pub extern "C" fn posixfile_addListener( + file: *const PosixFileArc, + listener: *mut c::StatusListener, + ) { + assert!(!file.is_null()); + assert!(!listener.is_null()); + + let file = file as *const AtomicRefCell; + let file = unsafe { &*file }; + + file.borrow_mut().add_legacy_listener(listener); + } + + /// Remove a listener from the posix file object. + #[no_mangle] + pub extern "C" fn posixfile_removeListener( + file: *const PosixFileArc, + listener: *mut c::StatusListener, + ) { + assert!(!file.is_null()); + assert!(!listener.is_null()); + + let file = file as *const AtomicRefCell; + let file = unsafe { &*file }; + + file.borrow_mut().remove_legacy_listener(listener); + } +} diff --git a/src/main/host/descriptor/pipe.rs b/src/main/host/descriptor/pipe.rs new file mode 100644 index 0000000000..cb7a24f1cf --- /dev/null +++ b/src/main/host/descriptor/pipe.rs @@ -0,0 +1,290 @@ +use atomic_refcell::AtomicRefCell; +use std::sync::Arc; + +use crate::cshadow as c; +use crate::host::descriptor::{ + FileFlags, FileMode, NewStatusListenerFilter, PosixFile, StatusEventSource, SyscallReturn, +}; +use crate::utility::byte_queue::ByteQueue; +use crate::utility::event_queue::{EventQueue, Handle}; + +pub struct PipeFile { + buffer: Arc>, + event_source: StatusEventSource, + status: c::Status, + mode: FileMode, + flags: FileFlags, + buffer_event_handle: Option>, +} + +impl PipeFile { + pub fn new(buffer: Arc>, mode: FileMode, flags: FileFlags) -> Self { + let mut rv = Self { + buffer, + event_source: StatusEventSource::new(), + status: c::_Status_STATUS_NONE, + mode, + flags, + buffer_event_handle: None, + }; + + rv.status = rv.filter_status(rv.buffer.borrow_mut().status()); + + rv + } + + pub fn get_flags(&self) -> FileFlags { + self.flags + } + + pub fn set_flags(&mut self, flags: FileFlags) { + self.flags = flags; + } + + pub fn read(&mut self, bytes: &mut [u8], event_queue: &mut EventQueue) -> SyscallReturn { + // if the file is not open for reading, return EBADF + if !self.mode.contains(FileMode::READ) { + return SyscallReturn::Error(nix::errno::Errno::EBADF); + } + + let num_read = { + let mut buffer = self.buffer.borrow_mut(); + buffer.read(bytes, event_queue) + }; + + SyscallReturn::Success(num_read as i32) + } + + pub fn write(&mut self, bytes: &[u8], event_queue: &mut EventQueue) -> SyscallReturn { + // if the file is not open for writing, return EBADF + if !self.mode.contains(FileMode::WRITE) { + return SyscallReturn::Error(nix::errno::Errno::EBADF); + } + + let count = { + let mut buffer = self.buffer.borrow_mut(); + buffer.write(bytes, event_queue) + }; + + // the write would block if we could not write any bytes, but were asked to + if count == 0 && bytes.len() > 0 { + SyscallReturn::Error(nix::errno::EWOULDBLOCK) + } else { + SyscallReturn::Success(count as i32) + } + } + + pub fn enable_notifications(arc: &Arc>) { + // we remove some of these later in this function + let monitoring = c::_Status_STATUS_DESCRIPTOR_ACTIVE + | c::_Status_STATUS_DESCRIPTOR_CLOSED + | c::_Status_STATUS_DESCRIPTOR_READABLE + | c::_Status_STATUS_DESCRIPTOR_WRITABLE; + + let weak = Arc::downgrade(arc); + match *arc.borrow_mut() { + PosixFile::Pipe(ref mut f) => { + // remove any status flags that aren't relevant to us + let monitoring = f.filter_status(monitoring); + + f.buffer_event_handle = Some(f.buffer.borrow_mut().add_listener( + monitoring, + NewStatusListenerFilter::Always, + move |status, _changed, event_queue| { + // if the file hasn't been dropped + if let Some(file) = weak.upgrade() { + let mut file = file.borrow_mut(); + match *file { + PosixFile::Pipe(ref mut f) => { + f.adjust_status(status, true, event_queue) + } + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + }, + )); + } + #[allow(unreachable_patterns)] + _ => unreachable!(), + }; + } + + pub fn add_listener( + &mut self, + monitoring: c::Status, + filter: NewStatusListenerFilter, + notify_fn: impl Fn(c::Status, c::Status, &mut EventQueue) + Send + Sync + 'static, + ) -> Handle<(c::Status, c::Status)> { + self.event_source + .add_listener(monitoring, filter, notify_fn) + } + + pub fn add_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + self.event_source.add_legacy_listener(ptr); + } + + pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) { + self.event_source.remove_legacy_listener(ptr); + } + + pub fn status(&self) -> c::Status { + self.status + } + + fn filter_status(&self, mut status: c::Status) -> c::Status { + // if not open for reading + if !self.mode.contains(FileMode::READ) { + // remove the readable flag + status &= !c::_Status_STATUS_DESCRIPTOR_READABLE; + } + + // if not open for writing + if !self.mode.contains(FileMode::WRITE) { + // remove the writable flag + status &= !c::_Status_STATUS_DESCRIPTOR_WRITABLE; + } + + status + } + + pub fn adjust_status( + &mut self, + status: c::Status, + do_set_bits: bool, + event_queue: &mut EventQueue, + ) { + let old_status = self.status; + + // remove any flags that aren't relevant + let status = self.filter_status(status); + + if do_set_bits { + self.status |= status; + } else { + self.status &= !status; + } + + self.handle_status_change(old_status, event_queue); + } + + fn handle_status_change(&mut self, old_status: c::Status, event_queue: &mut EventQueue) { + let statuses_changed = self.status ^ old_status; + + // if nothing changed + if statuses_changed == 0 { + return; + } + + self.event_source + .notify_listeners(self.status, statuses_changed, event_queue); + } +} + +pub struct SharedBuf { + queue: ByteQueue, + max_len: usize, + status: c::Status, + event_source: StatusEventSource, +} + +impl SharedBuf { + pub fn new() -> Self { + Self { + queue: ByteQueue::new(8192), + max_len: c::CONFIG_PIPE_BUFFER_SIZE as usize, + status: c::_Status_STATUS_DESCRIPTOR_ACTIVE | c::_Status_STATUS_DESCRIPTOR_WRITABLE, + event_source: StatusEventSource::new(), + } + } + + pub fn is_empty(&self) -> bool { + self.queue.len() == 0 + } + + pub fn space_available(&self) -> usize { + self.max_len - self.queue.len() + } + + pub fn read(&mut self, bytes: &mut [u8], event_queue: &mut EventQueue) -> usize { + let num = self.queue.pop(bytes); + + // readable if not empty + self.adjust_status( + c::_Status_STATUS_DESCRIPTOR_READABLE, + !self.is_empty(), + event_queue, + ); + + // writable if space is available + self.adjust_status( + c::_Status_STATUS_DESCRIPTOR_WRITABLE, + self.space_available() > 0, + event_queue, + ); + + num + } + + pub fn write(&mut self, bytes: &[u8], event_queue: &mut EventQueue) -> usize { + let available = self.space_available(); + let writable = &bytes[..std::cmp::min(bytes.len(), available)]; + self.queue.push(writable); + + self.adjust_status( + c::_Status_STATUS_DESCRIPTOR_READABLE, + !self.is_empty(), + event_queue, + ); + self.adjust_status( + c::_Status_STATUS_DESCRIPTOR_WRITABLE, + self.space_available() > 0, + event_queue, + ); + + writable.len() + } + + pub fn add_listener( + &mut self, + monitoring: c::Status, + filter: NewStatusListenerFilter, + notify_fn: impl Fn(c::Status, c::Status, &mut EventQueue) + Send + Sync + 'static, + ) -> Handle<(c::Status, c::Status)> { + self.event_source + .add_listener(monitoring, filter, notify_fn) + } + + pub fn status(&self) -> c::Status { + self.status + } + + fn adjust_status( + &mut self, + status: c::Status, + do_set_bits: bool, + event_queue: &mut EventQueue, + ) { + let old_status = self.status; + + if do_set_bits { + self.status |= status; + } else { + self.status &= !status; + } + + self.handle_status_change(old_status, event_queue); + } + + fn handle_status_change(&mut self, old_status: c::Status, event_queue: &mut EventQueue) { + let statuses_changed = self.status ^ old_status; + + // if nothing changed + if statuses_changed == 0 { + return; + } + + self.event_source + .notify_listeners(self.status, statuses_changed, event_queue); + } +} diff --git a/src/main/host/descriptor/socket.c b/src/main/host/descriptor/socket.c index ae27bb78b4..9c5a6e1f77 100644 --- a/src/main/host/descriptor/socket.c +++ b/src/main/host/descriptor/socket.c @@ -22,14 +22,14 @@ #include "main/routing/packet.h" #include "main/utility/utility.h" -static Socket* _socket_fromDescriptor(Descriptor* descriptor) { +static Socket* _socket_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_TCPSOCKET || descriptor_getType(descriptor) == DT_UDPSOCKET); return (Socket*)descriptor; } -static void _socket_free(Descriptor* descriptor) { - Socket* socket = _socket_fromDescriptor(descriptor); +static void _socket_free(LegacyDescriptor* descriptor) { + Socket* socket = _socket_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); @@ -63,23 +63,23 @@ static void _socket_free(Descriptor* descriptor) { // during the free call. This could be fixed by making all descriptor types // a direct child of the descriptor class. MAGIC_CLEAR(socket); - socket->vtable->free((Descriptor*)socket); + socket->vtable->free((LegacyDescriptor*)socket); } -static gboolean _socket_close(Descriptor* descriptor) { - Socket* socket = _socket_fromDescriptor(descriptor); +static gboolean _socket_close(LegacyDescriptor* descriptor) { + Socket* socket = _socket_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); Tracker* tracker = host_getTracker(worker_getActiveHost()); tracker_removeSocket(tracker, descriptor_getHandle(descriptor)); - return socket->vtable->close((Descriptor*)socket); + return socket->vtable->close((LegacyDescriptor*)socket); } static gssize _socket_sendUserData(Transport* transport, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { - Socket* socket = _socket_fromDescriptor((Descriptor*)transport); + Socket* socket = _socket_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); return socket->vtable->send((Transport*)socket, buffer, nBytes, ip, port); @@ -88,7 +88,7 @@ static gssize _socket_sendUserData(Transport* transport, gconstpointer buffer, static gssize _socket_receiveUserData(Transport* transport, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { - Socket* socket = _socket_fromDescriptor((Descriptor*)transport); + Socket* socket = _socket_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); return socket->vtable->receive((Transport*)socket, buffer, nBytes, ip, port); @@ -99,7 +99,7 @@ TransportFunctionTable socket_functions = { MAGIC_VALUE}; void socket_init(Socket* socket, SocketFunctionTable* vtable, - DescriptorType type, guint receiveBufferSize, + LegacyDescriptorType type, guint receiveBufferSize, guint sendBufferSize) { utility_assert(socket && vtable); @@ -118,7 +118,7 @@ void socket_init(Socket* socket, SocketFunctionTable* vtable, socket->outputBufferSize = sendBufferSize; Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_addSocket(tracker, descriptor->handle, socket->protocol, socket->inputBufferSize, socket->outputBufferSize); } @@ -140,7 +140,7 @@ gint socket_connectToPeer(Socket* socket, in_addr_t ip, in_port_t port, sa_famil MAGIC_ASSERT(socket->vtable); Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_updateSocketPeer(tracker, descriptor->handle, ip, ntohs(port)); return socket->vtable->connectToPeer(socket, ip, port, family); @@ -345,12 +345,12 @@ gboolean socket_addToInputBuffer(Socket* socket, Packet* packet) { /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_updateSocketInputBuffer(tracker, descriptor->handle, socket->inputBufferLength, socket->inputBufferSize); /* we just added a packet, so we are readable */ if(socket->inputBufferLength > 0) { - descriptor_adjustStatus((Descriptor*)socket, STATUS_DESCRIPTOR_READABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)socket, STATUS_DESCRIPTOR_READABLE, TRUE); } return TRUE; @@ -373,12 +373,12 @@ Packet* socket_removeFromInputBuffer(Socket* socket) { /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_updateSocketInputBuffer(tracker, descriptor->handle, socket->inputBufferLength, socket->inputBufferSize); /* we are not readable if we are now empty */ if(socket->inputBufferLength <= 0) { - descriptor_adjustStatus((Descriptor*)socket, STATUS_DESCRIPTOR_READABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)socket, STATUS_DESCRIPTOR_READABLE, FALSE); } } @@ -420,12 +420,12 @@ gboolean socket_addToOutputBuffer(Socket* socket, Packet* packet) { /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we just added a packet, we are no longer writable if full */ if(_socket_getOutputBufferSpaceIncludingTCP(socket) <= 0) { - descriptor_adjustStatus((Descriptor*)socket, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)socket, STATUS_DESCRIPTOR_WRITABLE, FALSE); } /* tell the interface to include us when sending out to the network */ @@ -455,12 +455,12 @@ Packet* socket_removeFromOutputBuffer(Socket* socket) { /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getActiveHost()); - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we are writable if we now have space */ if(_socket_getOutputBufferSpaceIncludingTCP(socket) > 0) { - descriptor_adjustStatus((Descriptor*)socket, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)socket, STATUS_DESCRIPTOR_WRITABLE, TRUE); } } diff --git a/src/main/host/descriptor/socket.h b/src/main/host/descriptor/socket.h index fa5f2c44d8..5bc85ff01a 100644 --- a/src/main/host/descriptor/socket.h +++ b/src/main/host/descriptor/socket.h @@ -78,7 +78,7 @@ struct _Socket { }; void socket_init(Socket* socket, SocketFunctionTable* vtable, - DescriptorType type, guint receiveBufferSize, + LegacyDescriptorType type, guint receiveBufferSize, guint sendBufferSize); void socket_pushInPacket(Socket* socket, Packet* packet); diff --git a/src/main/host/descriptor/tcp.c b/src/main/host/descriptor/tcp.c index 85b40df406..9f66686f99 100644 --- a/src/main/host/descriptor/tcp.c +++ b/src/main/host/descriptor/tcp.c @@ -257,7 +257,7 @@ static void _rswlog(const TCP *tcp, const char *format, ...) { static void _tcp_flush(TCP* tcp); -static TCP* _tcp_fromDescriptor(Descriptor* descriptor) { +static TCP* _tcp_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_TCPSOCKET); return (TCP*)descriptor; } @@ -621,7 +621,7 @@ static void _tcp_setState(TCP* tcp, enum TCPState state) { /* some state transitions require us to update the descriptor status */ switch (state) { case TCPS_LISTEN: { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, TRUE); break; } case TCPS_SYNSENT: { @@ -633,7 +633,7 @@ static void _tcp_setState(TCP* tcp, enum TCPState state) { case TCPS_ESTABLISHED: { tcp->flags |= TCPF_WAS_ESTABLISHED; descriptor_adjustStatus( - (Descriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE | STATUS_DESCRIPTOR_WRITABLE, TRUE); + (LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE | STATUS_DESCRIPTOR_WRITABLE, TRUE); break; } case TCPS_CLOSING: { @@ -646,7 +646,7 @@ static void _tcp_setState(TCP* tcp, enum TCPState state) { _tcp_clearRetransmit(tcp, (guint)-1); /* user can no longer use socket */ - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, FALSE); /* * servers have to wait for all children to close. @@ -664,15 +664,15 @@ static void _tcp_setState(TCP* tcp, enum TCPState state) { /* if i was the server's last child and its waiting to close, close it */ if((parent->state == TCPS_CLOSED) && (g_hash_table_size(parent->server->children) <= 0)) { /* this will unbind from the network interface and free socket */ - Descriptor* parentDesc = (Descriptor*)parent; - process_deregisterDescriptor( + LegacyDescriptor* parentDesc = (LegacyDescriptor*)parent; + process_deregisterLegacyDescriptor( descriptor_getOwnerProcess(parentDesc), parentDesc); } } /* this will unbind from the network interface and free socket */ - Descriptor* desc = (Descriptor*)tcp; - process_deregisterDescriptor( + LegacyDescriptor* desc = (LegacyDescriptor*)tcp; + process_deregisterLegacyDescriptor( descriptor_getOwnerProcess(desc), desc); } break; @@ -747,7 +747,7 @@ static void _tcp_bufferPacketOut(TCP* tcp, Packet* packet) { /* the packet takes up more space */ tcp->throttledOutputLength += packet_getPayloadLength(packet); if(_tcp_getBufferSpaceOut(tcp) == 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); } packet_addDeliveryStatus(packet, PDS_SND_TCP_ENQUEUE_THROTTLED); @@ -880,7 +880,7 @@ static void _tcp_addRetransmit(TCP* tcp, Packet* packet) { tcp->retransmit.queueLength += packet_getPayloadLength(packet); if(_tcp_getBufferSpaceOut(tcp) == 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); } } } @@ -905,7 +905,7 @@ static void _tcp_clearRetransmit(TCP* tcp, guint sequence) { } if(_tcp_getBufferSpaceOut(tcp) > 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); } } @@ -928,7 +928,7 @@ static void _tcp_clearRetransmitRange(TCP* tcp, guint begin, guint end) { } if(_tcp_getBufferSpaceOut(tcp) > 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); } } @@ -1062,7 +1062,7 @@ static void _tcp_retransmitPacket(TCP* tcp, gint sequence) { packet_addDeliveryStatus(packet, PDS_SND_TCP_DEQUEUE_RETRANSMIT); if(_tcp_getBufferSpaceOut(tcp) > 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); } /* reset retransmit timer since we are resending it now */ @@ -1251,7 +1251,7 @@ static void _tcp_flush(TCP* tcp) { /* update the tracker input/output buffer stats */ Tracker* tracker = host_getTracker(worker_getActiveHost()); Socket* socket = (Socket* )tcp; - Descriptor* descriptor = (Descriptor *)socket; + LegacyDescriptor* descriptor = (LegacyDescriptor *)socket; gsize inSize = socket_getInputBufferSize(&(tcp->super)); gsize outSize = socket_getOutputBufferSize(&(tcp->super)); tracker_updateSocketInputBuffer(tracker, descriptor->handle, inSize - _tcp_getBufferSpaceIn(tcp), inSize); @@ -1275,18 +1275,18 @@ static void _tcp_flush(TCP* tcp) { if((tcp->receive.next >= tcp->receive.end) && !(tcp->flags & TCPF_EOF_RD_SIGNALED)) { /* user needs to read a 0 so it knows we closed */ tcp->error |= TCPE_RECEIVE_EOF; - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_READABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_READABLE, TRUE); } } if((tcp->error & TCPE_CONNECTION_RESET) && (tcp->flags & TCPF_RESET_SIGNALED)) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); } else if((tcp->error & TCPE_SEND_EOF) && (tcp->flags & TCPF_EOF_WR_SIGNALED)) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); } else if(_tcp_getBufferSpaceOut(tcp) <= 0) { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, FALSE); } else { - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_WRITABLE, TRUE); } } @@ -1346,7 +1346,7 @@ static void _tcp_runRetransmitTimerExpiredTask(TCP* tcp, gpointer userData) { } static gboolean _tcp_isFamilySupported(Socket* socket, sa_family_t family) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)socket); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(tcp); return family == AF_INET || family == AF_UNIX ? TRUE : FALSE; } @@ -1493,7 +1493,7 @@ void tcp_getInfo(TCP* tcp, struct tcp_info *tcpinfo) { static gint _tcp_connectToPeer(Socket* socket, in_addr_t ip, in_port_t port, sa_family_t family) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)socket); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(tcp); /* Only try to connect if we haven't already started. */ @@ -1677,7 +1677,7 @@ TCPProcessFlags _tcp_dataProcessing(TCP* tcp, Packet* packet, PacketTCPHeader *h } } - Status s = descriptor_getStatus((Descriptor*)tcp); + Status s = descriptor_getStatus((LegacyDescriptor*)tcp); gboolean waitingUserRead = (s & STATUS_DESCRIPTOR_READABLE) ? TRUE : FALSE; if((isNextPacket && !waitingUserRead) || (packetFits)) { @@ -1819,7 +1819,7 @@ static void _tcp_sendACKTaskCallback(TCP* tcp, gpointer userData) { /* return TRUE if the packet should be retransmitted */ static void _tcp_processPacket(Socket* socket, Packet* packet) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)socket); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(tcp); /* fetch the TCP info from the packet */ @@ -1878,9 +1878,9 @@ static void _tcp_processPacket(Socket* socket, Packet* packet) { guint64 sendBufSize = host_getConfiguredSendBufSize(node); TCP* multiplexed = tcp_new(recvBufSize, sendBufSize); - process_registerDescriptor( - descriptor_getOwnerProcess((Descriptor*)tcp), - (Descriptor*)multiplexed); + process_registerLegacyDescriptor( + descriptor_getOwnerProcess((LegacyDescriptor*)tcp), + (LegacyDescriptor*)multiplexed); multiplexed->child = _tcpchild_new(multiplexed, tcp, header->sourceIP, header->sourcePort); utility_assert(g_hash_table_lookup(tcp->server->children, &(multiplexed->child->key)) == NULL); @@ -2165,7 +2165,7 @@ static void _tcp_processPacket(Socket* socket, Packet* packet) { } static void _tcp_dropPacket(Socket* socket, Packet* packet) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)socket); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(tcp); /* if we run a server, the packet could be for an existing child */ @@ -2192,7 +2192,7 @@ static void _tcp_endOfFileSignalled(TCP* tcp, enum TCPFlags flags) { static gssize _tcp_sendUserData(Transport* transport, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)transport); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(tcp); /* return 0 to signal close, if necessary */ @@ -2260,7 +2260,7 @@ static void _tcp_sendWindowUpdate(TCP* tcp, gpointer data) { static gssize _tcp_receiveUserData(Transport* transport, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { - TCP* tcp = _tcp_fromDescriptor((Descriptor*)transport); + TCP* tcp = _tcp_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(tcp); /* @@ -2417,8 +2417,8 @@ static gssize _tcp_receiveUserData(Transport* transport, gpointer buffer, return totalCopied; } -static void _tcp_free(Descriptor* descriptor) { - TCP* tcp = _tcp_fromDescriptor(descriptor); +static void _tcp_free(LegacyDescriptor* descriptor) { + TCP* tcp = _tcp_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(tcp); priorityqueue_free(tcp->throttledOutput); @@ -2446,15 +2446,15 @@ static void _tcp_free(Descriptor* descriptor) { tcp->cong.hooks->tcp_cong_delete(tcp); retransmit_tally_destroy(tcp->retransmit.tally); - descriptor_clear((Descriptor*)tcp); + descriptor_clear((LegacyDescriptor*)tcp); MAGIC_CLEAR(tcp); g_free(tcp); worker_countObject(OBJECT_TYPE_TCP, COUNTER_TYPE_FREE); } -static gboolean _tcp_close(Descriptor* descriptor) { - TCP* tcp = _tcp_fromDescriptor(descriptor); +static gboolean _tcp_close(LegacyDescriptor* descriptor) { + TCP* tcp = _tcp_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(tcp); /* We always return FALSE because we handle process deregististration @@ -2465,7 +2465,7 @@ static gboolean _tcp_close(Descriptor* descriptor) { tcp->flags |= TCPF_LOCAL_CLOSED_RD; /* the user closed the connection, so should never interact with the socket again */ - descriptor_adjustStatus((Descriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, FALSE); + descriptor_adjustStatus((LegacyDescriptor*)tcp, STATUS_DESCRIPTOR_ACTIVE, FALSE); switch (tcp->state) { case TCPS_LISTEN: diff --git a/src/main/host/descriptor/tcp_cong_reno.c b/src/main/host/descriptor/tcp_cong_reno.c index b75571b4d3..808790ff8f 100644 --- a/src/main/host/descriptor/tcp_cong_reno.c +++ b/src/main/host/descriptor/tcp_cong_reno.c @@ -41,7 +41,7 @@ static inline void transition_to_cong_avoid(TCP *tcp, CAReno *reno, guint32 n) { reno->cong_avoid_nacked = 0; reno->state_hooks = cong_avoid_hooks_(); reno->state_hooks->tcp_cong_new_ack_ev(tcp, n); - info("[CONG] fd %i transition_to_cong_avoid", ((Descriptor*)tcp)->handle); + info("[CONG] fd %i transition_to_cong_avoid", ((LegacyDescriptor*)tcp)->handle); } /* SLOW START *******************************************************/ @@ -53,7 +53,7 @@ static void ca_reno_slow_start_duplicate_ack_ev_(TCP *tcp) { if (reno->duplicate_ack_n == 3) { // transition to fast recovery debug("[CONG-AVOID] three duplicate acks"); - info("[CONG] fd %i three duplicate acks transition_to_fast_recovery", ((Descriptor*)tcp)->handle); + info("[CONG] fd %i three duplicate acks transition_to_fast_recovery", ((LegacyDescriptor*)tcp)->handle); ssthresh_halve(tcp, reno); tcp_cong(tcp)->cwnd = reno->ssthresh + 3; @@ -157,7 +157,7 @@ static void tcp_cong_reno_timeout_ev_(TCP *tcp) { // transition to slow start reno->state_hooks = slow_start_hooks_(); - info("[CONG] fd %i transition_to_slow_start", ((Descriptor*)tcp)->handle); + info("[CONG] fd %i transition_to_slow_start", ((LegacyDescriptor*)tcp)->handle); } static guint32 tcp_cong_reno_ssthresh_(TCP *tcp) { diff --git a/src/main/host/descriptor/timer.c b/src/main/host/descriptor/timer.c index 7ee2868e98..63dfc73f5e 100644 --- a/src/main/host/descriptor/timer.c +++ b/src/main/host/descriptor/timer.c @@ -22,7 +22,7 @@ #include "support/logger/logger.h" struct _Timer { - Descriptor super; + LegacyDescriptor super; /* the absolute time the timer will next expire */ SimulationTime nextExpireTime; @@ -42,13 +42,13 @@ struct _Timer { MAGIC_DECLARE; }; -static Timer* _timer_fromDescriptor(Descriptor* descriptor) { +static Timer* _timer_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_TIMER); return (Timer*)descriptor; } -static gboolean _timer_close(Descriptor* descriptor) { - Timer* timer = _timer_fromDescriptor(descriptor); +static gboolean _timer_close(LegacyDescriptor* descriptor) { + Timer* timer = _timer_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(timer); debug("timer fd %i closing now", timer->super.handle); timer->isClosed = TRUE; @@ -60,10 +60,10 @@ static gboolean _timer_close(Descriptor* descriptor) { } } -static void _timer_free(Descriptor* descriptor) { - Timer* timer = _timer_fromDescriptor(descriptor); +static void _timer_free(LegacyDescriptor* descriptor) { + Timer* timer = _timer_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(timer); - descriptor_clear((Descriptor*)timer); + descriptor_clear((LegacyDescriptor*)timer); MAGIC_CLEAR(timer); g_free(timer); worker_countObject(OBJECT_TYPE_TIMER, COUNTER_TYPE_FREE); diff --git a/src/main/host/descriptor/transport.c b/src/main/host/descriptor/transport.c index 6960b0acb4..e082adb2cc 100644 --- a/src/main/host/descriptor/transport.c +++ b/src/main/host/descriptor/transport.c @@ -12,7 +12,7 @@ #include "main/host/descriptor/transport.h" #include "main/utility/utility.h" -static Transport* _transport_fromDescriptor(Descriptor* descriptor) { +static Transport* _transport_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_TCPSOCKET || descriptor_getType(descriptor) == DT_UDPSOCKET || descriptor_getType(descriptor) == DT_UNIXSOCKET || @@ -20,8 +20,8 @@ static Transport* _transport_fromDescriptor(Descriptor* descriptor) { return (Transport*)descriptor; } -static void _transport_free(Descriptor* descriptor) { - Transport* transport = _transport_fromDescriptor(descriptor); +static void _transport_free(LegacyDescriptor* descriptor) { + Transport* transport = _transport_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(transport); MAGIC_ASSERT(transport->vtable); @@ -32,8 +32,8 @@ static void _transport_free(Descriptor* descriptor) { transport->vtable->free(descriptor); } -static gboolean _transport_close(Descriptor* descriptor) { - Transport* transport = _transport_fromDescriptor(descriptor); +static gboolean _transport_close(LegacyDescriptor* descriptor) { + Transport* transport = _transport_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(transport); MAGIC_ASSERT(transport->vtable); return transport->vtable->close(descriptor); @@ -43,7 +43,7 @@ DescriptorFunctionTable transport_functions = { _transport_close, _transport_free, MAGIC_VALUE}; void transport_init(Transport* transport, TransportFunctionTable* vtable, - DescriptorType type) { + LegacyDescriptorType type) { utility_assert(transport && vtable); descriptor_init(&(transport->super), type, &transport_functions); diff --git a/src/main/host/descriptor/transport.h b/src/main/host/descriptor/transport.h index 17c7eb32b8..8e11993207 100644 --- a/src/main/host/descriptor/transport.h +++ b/src/main/host/descriptor/transport.h @@ -29,14 +29,14 @@ struct _TransportFunctionTable { }; struct _Transport { - Descriptor super; + LegacyDescriptor super; TransportFunctionTable* vtable; MAGIC_DECLARE; }; void transport_init(Transport* transport, TransportFunctionTable* vtable, - DescriptorType type); + LegacyDescriptorType type); gssize transport_sendUserData(Transport* transport, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port); diff --git a/src/main/host/descriptor/udp.c b/src/main/host/descriptor/udp.c index 13df32c68c..68b17fd697 100644 --- a/src/main/host/descriptor/udp.c +++ b/src/main/host/descriptor/udp.c @@ -54,20 +54,20 @@ static void _udp_setState(UDP* udp, enum UDPState state) { _udp_stateToAscii(udp->stateLast), _udp_stateToAscii(udp->state)); } -static UDP* _udp_fromDescriptor(Descriptor* descriptor) { +static UDP* _udp_fromLegacyDescriptor(LegacyDescriptor* descriptor) { utility_assert(descriptor_getType(descriptor) == DT_UDPSOCKET); return (UDP*)descriptor; } static gboolean _udp_isFamilySupported(Socket* socket, sa_family_t family) { - UDP* udp = _udp_fromDescriptor((Descriptor*)socket); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(udp); return (family == AF_INET || family == AF_UNSPEC || family == AF_UNIX) ? TRUE : FALSE; } static gint _udp_connectToPeer(Socket* socket, in_addr_t ip, in_port_t port, sa_family_t family) { - UDP* udp = _udp_fromDescriptor((Descriptor*)socket); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(udp); @@ -86,7 +86,7 @@ static gint _udp_connectToPeer(Socket* socket, in_addr_t ip, in_port_t port, } static void _udp_processPacket(Socket* socket, Packet* packet) { - UDP* udp = _udp_fromDescriptor((Descriptor*)socket); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(udp); /* UDP packet can be buffered immediately */ @@ -96,7 +96,7 @@ static void _udp_processPacket(Socket* socket, Packet* packet) { } static void _udp_dropPacket(Socket* socket, Packet* packet) { - UDP* udp = _udp_fromDescriptor((Descriptor*)socket); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)socket); MAGIC_ASSERT(udp); /* do nothing */ @@ -109,7 +109,7 @@ static void _udp_dropPacket(Socket* socket, Packet* packet) { */ static gssize _udp_sendUserData(Transport* transport, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { - UDP* udp = _udp_fromDescriptor((Descriptor*)transport); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(udp); const gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; @@ -173,7 +173,7 @@ static gssize _udp_sendUserData(Transport* transport, gconstpointer buffer, static gssize _udp_receiveUserData(Transport* transport, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { - UDP* udp = _udp_fromDescriptor((Descriptor*)transport); + UDP* udp = _udp_fromLegacyDescriptor((LegacyDescriptor*)transport); MAGIC_ASSERT(udp); if (socket_peekNextInPacket(&(udp->super)) == NULL) { @@ -213,8 +213,8 @@ static gssize _udp_receiveUserData(Transport* transport, gpointer buffer, return bytesCopied; } -static void _udp_free(Descriptor* descriptor) { - UDP* udp = _udp_fromDescriptor(descriptor); +static void _udp_free(LegacyDescriptor* descriptor) { + UDP* udp = _udp_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(udp); descriptor_clear(descriptor); @@ -224,8 +224,8 @@ static void _udp_free(Descriptor* descriptor) { worker_countObject(OBJECT_TYPE_UDP, COUNTER_TYPE_FREE); } -static gboolean _udp_close(Descriptor* descriptor) { - UDP* udp = _udp_fromDescriptor(descriptor); +static gboolean _udp_close(LegacyDescriptor* descriptor) { + UDP* udp = _udp_fromLegacyDescriptor(descriptor); MAGIC_ASSERT(udp); /* Deregister us from the process upon return. */ _udp_setState(udp, UDPS_CLOSED); @@ -260,7 +260,7 @@ UDP* udp_new(guint receiveBufferSize, guint sendBufferSize) { /* we are immediately active because UDP doesnt wait for accept or connect */ descriptor_adjustStatus( - (Descriptor*)udp, STATUS_DESCRIPTOR_ACTIVE | STATUS_DESCRIPTOR_WRITABLE, TRUE); + (LegacyDescriptor*)udp, STATUS_DESCRIPTOR_ACTIVE | STATUS_DESCRIPTOR_WRITABLE, TRUE); worker_countObject(OBJECT_TYPE_UDP, COUNTER_TYPE_NEW); diff --git a/src/main/host/memory_manager.rs b/src/main/host/memory_manager.rs index 7c0877ab1b..5e7cea581e 100644 --- a/src/main/host/memory_manager.rs +++ b/src/main/host/memory_manager.rs @@ -1,6 +1,6 @@ use super::syscall_types::{PluginPtr, SysCallReg}; use super::thread::{CThread, Thread}; -use crate::cbindings as c; +use crate::cshadow as c; use crate::utility::interval_map::{Interval, IntervalMap, Mutation}; use crate::utility::proc_maps; use crate::utility::proc_maps::{MappingPath, Sharing}; diff --git a/src/main/host/mod.rs b/src/main/host/mod.rs index 5ef37f2438..1ffc2713ce 100644 --- a/src/main/host/mod.rs +++ b/src/main/host/mod.rs @@ -1,3 +1,5 @@ +pub mod descriptor; mod memory_manager; +pub mod syscall; pub mod syscall_types; pub mod thread; diff --git a/src/main/host/network_interface.c b/src/main/host/network_interface.c index 44a8a6b864..0e02879d59 100644 --- a/src/main/host/network_interface.c +++ b/src/main/host/network_interface.c @@ -405,7 +405,7 @@ static void _networkinterface_receivePacket(NetworkInterface* interface, Packet* /* if the socket closed, just drop the packet */ gint socketHandle = -1; if(socket) { - socketHandle = *descriptor_getHandleReference((Descriptor*)socket); + socketHandle = *descriptor_getHandleReference((LegacyDescriptor*)socket); socket_pushInPacket(socket, packet); } else { packet_addDeliveryStatus(packet, PDS_RCV_INTERFACE_DROPPED); @@ -454,8 +454,8 @@ void networkinterface_receivePackets(NetworkInterface* interface) { } } -static void _networkinterface_updatePacketHeader(Descriptor* descriptor, Packet* packet) { - DescriptorType type = descriptor_getType(descriptor); +static void _networkinterface_updatePacketHeader(LegacyDescriptor* descriptor, Packet* packet) { + LegacyDescriptorType type = descriptor_getType(descriptor); if(type == DT_TCPSOCKET) { TCP* tcp = (TCP*)descriptor; tcp_networkInterfaceIsAboutToSendPacket(tcp, packet); @@ -470,10 +470,10 @@ static Packet* _networkinterface_selectRoundRobin(NetworkInterface* interface, g /* do round robin to get the next packet from the next socket */ Socket* socket = g_queue_pop_head(interface->rrQueue); packet = socket_pullOutPacket(socket); - *socketHandle = *descriptor_getHandleReference((Descriptor*)socket); + *socketHandle = *descriptor_getHandleReference((LegacyDescriptor*)socket); if(socket && packet) { - _networkinterface_updatePacketHeader((Descriptor*)socket, packet); + _networkinterface_updatePacketHeader((LegacyDescriptor*)socket, packet); } if(socket_peekNextOutPacket(socket)) { @@ -481,7 +481,7 @@ static Packet* _networkinterface_selectRoundRobin(NetworkInterface* interface, g g_queue_push_tail(interface->rrQueue, socket); } else { /* socket has no more packets, unref it from the sendable queue */ - descriptor_unref((Descriptor*) socket); + descriptor_unref((LegacyDescriptor*) socket); } } @@ -498,10 +498,10 @@ static Packet* _networkinterface_selectFirstInFirstOut(NetworkInterface* interfa /* do fifo to get the next packet from the next socket */ Socket* socket = priorityqueue_pop(interface->fifoQueue); packet = socket_pullOutPacket(socket); - *socketHandle = *descriptor_getHandleReference((Descriptor*)socket); + *socketHandle = *descriptor_getHandleReference((LegacyDescriptor*)socket); if(socket && packet) { - _networkinterface_updatePacketHeader((Descriptor*)socket, packet); + _networkinterface_updatePacketHeader((LegacyDescriptor*)socket, packet); } if(socket_peekNextOutPacket(socket)) { @@ -509,7 +509,7 @@ static Packet* _networkinterface_selectFirstInFirstOut(NetworkInterface* interfa priorityqueue_push(interface->fifoQueue, socket); } else { /* socket has no more packets, unref it from the sendable queue */ - descriptor_unref((Descriptor*) socket); + descriptor_unref((LegacyDescriptor*) socket); } } diff --git a/src/main/host/process.c b/src/main/host/process.c index dbcbb2ecad..f8ad539d3c 100644 --- a/src/main/host/process.c +++ b/src/main/host/process.c @@ -292,23 +292,25 @@ static void _process_openStdIOFileHelper(Process* proc, bool isStdOut) { if (errcode < 0) { error("Opening %s: %s", fileName, strerror(-errcode)); /* Unref and free the file object. */ - descriptor_close((Descriptor*)stdfile); + descriptor_close((LegacyDescriptor*)stdfile); } else { debug("Successfully opened %s file at %s", isStdOut ? "stdout" : "stderr", fileName); + CompatDescriptor* compatDesc = compatdescriptor_fromLegacy((LegacyDescriptor*)stdfile); + if (isStdOut) { descriptortable_set( - proc->descTable, STDOUT_FILENO, (Descriptor*)stdfile); + proc->descTable, STDOUT_FILENO, compatDesc); proc->stdoutFile = stdfile; } else { descriptortable_set( - proc->descTable, STDERR_FILENO, (Descriptor*)stdfile); + proc->descTable, STDERR_FILENO, compatDesc); proc->stderrFile = stdfile; } /* Ref once since both the proc class and the table are storing it. */ - descriptor_ref((Descriptor*)stdfile); + descriptor_ref((LegacyDescriptor*)stdfile); } g_free(fileName); @@ -597,12 +599,12 @@ static void _process_free(Process* proc) { * Closing the descriptors will remove them from the table and the table * will release it's ref. We also need to release our proc ref. */ if (proc->stderrFile) { - descriptor_close((Descriptor*)proc->stderrFile); - descriptor_unref((Descriptor*)proc->stderrFile); + descriptor_close((LegacyDescriptor*)proc->stderrFile); + descriptor_unref((LegacyDescriptor*)proc->stderrFile); } if (proc->stdoutFile) { - descriptor_close((Descriptor*)proc->stdoutFile); - descriptor_unref((Descriptor*)proc->stdoutFile); + descriptor_close((LegacyDescriptor*)proc->stdoutFile); + descriptor_unref((LegacyDescriptor*)proc->stdoutFile); } /* Now free all remaining descriptors stored in our table. */ @@ -726,31 +728,63 @@ void process_flushPtrs(Process* proc, Thread* thread) { } // ****************************************************** -// Handler the descriptors owned by this process +// Handle the descriptors owned by this process // ****************************************************** -int process_registerDescriptor(Process* proc, Descriptor* desc) { +int process_registerCompatDescriptor(Process* proc, CompatDescriptor* compatDesc) { + MAGIC_ASSERT(proc); + utility_assert(compatDesc); + return descriptortable_add(proc->descTable, compatDesc); +} + +void process_deregisterCompatDescriptor(Process* proc, int handle) { + MAGIC_ASSERT(proc); + descriptortable_remove(proc->descTable, handle); +} + +CompatDescriptor* process_getRegisteredCompatDescriptor(Process* proc, int handle) { + MAGIC_ASSERT(proc); + CompatDescriptor* compatDesc = descriptortable_get(proc->descTable, handle); + return compatDesc; +} + +int process_registerLegacyDescriptor(Process* proc, LegacyDescriptor* desc) { MAGIC_ASSERT(proc); utility_assert(desc); + descriptor_setOwnerProcess(desc, proc); - return descriptortable_add(proc->descTable, desc); + CompatDescriptor* compatDesc = compatdescriptor_fromLegacy(desc); + + return process_registerCompatDescriptor(proc, compatDesc); } -void process_deregisterDescriptor(Process* proc, Descriptor* desc) { +void process_deregisterLegacyDescriptor(Process* proc, LegacyDescriptor* desc) { MAGIC_ASSERT(proc); if (desc) { - DescriptorType dType = descriptor_getType(desc); + LegacyDescriptorType dType = descriptor_getType(desc); if (dType == DT_TCPSOCKET || dType == DT_UDPSOCKET) { host_disassociateInterface(proc->host, (Socket*)desc); } descriptor_setOwnerProcess(desc, NULL); - descriptortable_remove(proc->descTable, desc); + descriptortable_remove(proc->descTable, descriptor_getHandle(desc)); } } -Descriptor* process_getRegisteredDescriptor(Process* proc, int handle) { +LegacyDescriptor* process_getRegisteredLegacyDescriptor(Process* proc, int handle) { MAGIC_ASSERT(proc); - return descriptortable_get(proc->descTable, handle); -} + CompatDescriptor* compatDesc = process_getRegisteredCompatDescriptor(proc, handle); + if (compatDesc == NULL) { + return NULL; + } + + // will return NULL if the descriptor is valid but is not a legacy descriptor + LegacyDescriptor* legacyDesc = compatdescriptor_asLegacy(compatDesc); + + if (legacyDesc == NULL) { + warning("Attempted to convert compat descriptor fd=%d to a legacy descriptor", handle); + } + + return legacyDesc; +} diff --git a/src/main/host/process.h b/src/main/host/process.h index 36a815c207..d7757356d2 100644 --- a/src/main/host/process.h +++ b/src/main/host/process.h @@ -71,9 +71,15 @@ const gchar* process_getPluginName(Process* proc); /* Returns the processID that was assigned to us in process_new */ guint process_getProcessID(Process* proc); -int process_registerDescriptor(Process* proc, Descriptor* desc); -void process_deregisterDescriptor(Process* proc, Descriptor* desc); -Descriptor* process_getRegisteredDescriptor(Process* proc, int handle); +/* Handle all of the descriptors owned by this process. */ +int process_registerCompatDescriptor(Process* proc, CompatDescriptor* compatDesc); +void process_deregisterCompatDescriptor(Process* proc, int handle); +CompatDescriptor* process_getRegisteredCompatDescriptor(Process* proc, int handle); + +/* Handle only the legacy descriptors owned by this process. */ +int process_registerLegacyDescriptor(Process* proc, LegacyDescriptor* desc); +void process_deregisterLegacyDescriptor(Process* proc, LegacyDescriptor* desc); +LegacyDescriptor* process_getRegisteredLegacyDescriptor(Process* proc, int handle); // Convert a virtual ptr in the plugin address space to a globally unique physical ptr PluginPhysicalPtr process_getPhysicalAddress(Process* proc, PluginVirtualPtr vPtr); diff --git a/src/main/host/syscall/epoll.c b/src/main/host/syscall/epoll.c index 2a6d3567a8..fc8259a50d 100644 --- a/src/main/host/syscall/epoll.c +++ b/src/main/host/syscall/epoll.c @@ -28,10 +28,10 @@ static int _syscallhandler_createEpollHelper(SysCallHandler* sys, int64_t size, } Epoll* epolld = epoll_new(); - int handle = process_registerDescriptor(sys->process, (Descriptor*)epolld); + int handle = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)epolld); if (flags & EPOLL_CLOEXEC) { - descriptor_addFlags((Descriptor*)epolld, EPOLL_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)epolld, EPOLL_CLOEXEC); } return handle; @@ -80,9 +80,8 @@ SysCallReturn syscallhandler_epoll_ctl(SysCallHandler* sys, } /* Get and check the epoll descriptor. */ - Descriptor* descriptor = - process_getRegisteredDescriptor(sys->process, epfd); - gint errorCode = _syscallhandler_validateDescriptor(descriptor, DT_EPOLL); + LegacyDescriptor* epollDescriptor = process_getRegisteredLegacyDescriptor(sys->process, epfd); + gint errorCode = _syscallhandler_validateDescriptor(epollDescriptor, DT_EPOLL); if (errorCode) { debug("Error when trying to validate epoll %i", epfd); @@ -91,17 +90,27 @@ SysCallReturn syscallhandler_epoll_ctl(SysCallHandler* sys, } /* It's now safe to cast. */ - Epoll* epoll = (Epoll*)descriptor; + Epoll* epoll = (Epoll*)epollDescriptor; utility_assert(epoll); /* Find the child descriptor that the epoll is monitoring. */ - descriptor = process_getRegisteredDescriptor(sys->process, fd); - errorCode = _syscallhandler_validateDescriptor(descriptor, DT_NONE); + CompatDescriptor* compatDescriptor = process_getRegisteredCompatDescriptor(sys->process, fd); - if (errorCode) { + if (compatDescriptor == NULL) { info("Child %i is not a shadow descriptor", fd); - return (SysCallReturn){ - .state = SYSCALL_DONE, .retval.as_i64 = errorCode}; + return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -EBADF}; + } + + LegacyDescriptor* legacyDescriptor = compatdescriptor_asLegacy(compatDescriptor); + + /* Validate only if a legacy descriptor */ + if (legacyDescriptor != NULL) { + errorCode = _syscallhandler_validateDescriptor(legacyDescriptor, DT_NONE); + + if (errorCode) { + info("Child %i is not a shadow descriptor", fd); + return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = errorCode}; + } } const struct epoll_event* event = NULL; @@ -110,7 +119,7 @@ SysCallReturn syscallhandler_epoll_ctl(SysCallHandler* sys, } debug("Calling epoll_control on epoll %i with child %i", epfd, fd); - errorCode = epoll_control(epoll, op, descriptor, event); + errorCode = epoll_control(epoll, op, fd, compatDescriptor, event); return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = errorCode}; } @@ -135,7 +144,7 @@ SysCallReturn syscallhandler_epoll_wait(SysCallHandler* sys, } /* Get and check the epoll descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, epfd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, epfd); gint errorCode = _syscallhandler_validateDescriptor(desc, DT_EPOLL); if (errorCode) { @@ -177,7 +186,7 @@ SysCallReturn syscallhandler_epoll_wait(SysCallHandler* sys, /* Block on epoll status. An epoll descriptor is readable when it * has events. We either use our timer as a timeout, or no timeout. */ Trigger trigger = (Trigger){.type = TRIGGER_DESCRIPTOR, - .object = (Descriptor*)epoll, + .object = (LegacyDescriptor*)epoll, .status = STATUS_DESCRIPTOR_READABLE}; return (SysCallReturn){ diff --git a/src/main/host/syscall/eventfd.c b/src/main/host/syscall/eventfd.c index 03480bfe9f..c974497bb3 100644 --- a/src/main/host/syscall/eventfd.c +++ b/src/main/host/syscall/eventfd.c @@ -29,7 +29,7 @@ static int _syscallhandler_validateEventFDHelper(SysCallHandler* sys, int efd, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, efd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, efd); if (desc && event_desc_out) { *event_desc_out = (EventD*)desc; } @@ -56,7 +56,7 @@ static SysCallReturn _syscallhandler_eventfdHelper(SysCallHandler* sys, unsigned /* Create the eventd object and double check that it's valid. */ EventD* eventd = eventd_new(initval, flags & EFD_SEMAPHORE ? 1 : 0); - int efd = process_registerDescriptor(sys->process, (Descriptor*)eventd); + int efd = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)eventd); #ifdef DEBUG /* This should always be a valid descriptor. */ @@ -69,10 +69,10 @@ static SysCallReturn _syscallhandler_eventfdHelper(SysCallHandler* sys, unsigned /* Set any options that were given. */ if (flags & EFD_NONBLOCK) { - descriptor_addFlags((Descriptor*)eventd, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)eventd, O_NONBLOCK); } if (flags & EFD_CLOEXEC) { - descriptor_addFlags((Descriptor*)eventd, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)eventd, O_CLOEXEC); } debug("eventfd() returning fd %i", efd); diff --git a/src/main/host/syscall/fcntl.c b/src/main/host/syscall/fcntl.c index f0b0325ba6..4342d567ca 100644 --- a/src/main/host/syscall/fcntl.c +++ b/src/main/host/syscall/fcntl.c @@ -176,7 +176,7 @@ SysCallReturn syscallhandler_fcntl(SysCallHandler* sys, debug("fcntl called on fd %d for command %lu", fd, command); - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); int errcode = _syscallhandler_validateDescriptor(desc, DT_NONE); if (errcode < 0) { return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = errcode}; diff --git a/src/main/host/syscall/file.c b/src/main/host/syscall/file.c index 05efd54a5d..e42586e2a3 100644 --- a/src/main/host/syscall/file.c +++ b/src/main/host/syscall/file.c @@ -31,7 +31,7 @@ static int _syscallhandler_validateFileHelper(SysCallHandler* sys, int filefd, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, filefd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, filefd); if (desc && file_desc_out) { *file_desc_out = (File*)desc; } @@ -66,13 +66,13 @@ static SysCallReturn _syscallhandler_openHelper(SysCallHandler* sys, /* Create the new descriptor for this file. */ File* filed = file_new(); - int handle = process_registerDescriptor(sys->process, (Descriptor*)filed); + int handle = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)filed); /* Now open the file. */ errcode = file_open(filed, pathname, flags, mode); if (errcode < 0) { /* This will remove the descriptor entry and unref/free the File. */ - descriptor_close((Descriptor*)filed); + descriptor_close((LegacyDescriptor*)filed); } else { utility_assert(errcode == handle); } diff --git a/src/main/host/syscall/fileat.c b/src/main/host/syscall/fileat.c index 6620180b9a..bd361f72b2 100644 --- a/src/main/host/syscall/fileat.c +++ b/src/main/host/syscall/fileat.c @@ -40,7 +40,7 @@ static int _syscallhandler_validateDirHelper(SysCallHandler* sys, int dirfd, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, dirfd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, dirfd); if (desc && dir_desc_out) { *dir_desc_out = (File*)desc; } @@ -131,13 +131,13 @@ SysCallReturn syscallhandler_openat(SysCallHandler* sys, /* Create the new descriptor for this file. */ File* file_desc = file_new(); int handle = - process_registerDescriptor(sys->process, (Descriptor*)file_desc); + process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)file_desc); /* Now open the file. */ errcode = file_openat(file_desc, dir_desc, pathname, flags, mode); if (errcode < 0) { /* This will remove the descriptor entry and unref/free the File. */ - descriptor_close((Descriptor*)file_desc); + descriptor_close((LegacyDescriptor*)file_desc); } else { utility_assert(errcode == handle); } diff --git a/src/main/host/syscall/ioctl.c b/src/main/host/syscall/ioctl.c index 41c51188f1..06558e120a 100644 --- a/src/main/host/syscall/ioctl.c +++ b/src/main/host/syscall/ioctl.c @@ -51,7 +51,7 @@ SysCallReturn syscallhandler_ioctl(SysCallHandler* sys, debug("ioctl called on fd %d for request %ld", fd, request); - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); int errcode = _syscallhandler_validateDescriptor(desc, DT_NONE); if (errcode < 0) { return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = errcode}; @@ -60,7 +60,7 @@ SysCallReturn syscallhandler_ioctl(SysCallHandler* sys, bool isInbufLenRequest = request == SIOCINQ || request == FIONREAD; bool isOutbufLenRequest = request == SIOCOUTQ || request == TIOCOUTQ; - DescriptorType dtype = descriptor_getType(desc); + LegacyDescriptorType dtype = descriptor_getType(desc); int result = 0; if (dtype == DT_FILE) { diff --git a/src/main/host/syscall/mman.c b/src/main/host/syscall/mman.c index f67c9959f2..e4161d5769 100644 --- a/src/main/host/syscall/mman.c +++ b/src/main/host/syscall/mman.c @@ -49,7 +49,7 @@ static int _syscallhandler_validateMmapArgsHelper(SysCallHandler* sys, int fd, /* We only need a file if it's not an anonymous mapping. */ if (!(flags & MAP_ANONYMOUS)) { - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); int errcode = _syscallhandler_validateDescriptor(desc, DT_NONE); if (errcode) { info("Invalid fd %i", fd); @@ -73,7 +73,7 @@ static int _syscallhandler_validateMmapArgsHelper(SysCallHandler* sys, int fd, static int _syscallhandler_openPluginFile(SysCallHandler* sys, File* file) { utility_assert(file); - int fd = descriptor_getHandle((Descriptor*)file); + int fd = descriptor_getHandle((LegacyDescriptor*)file); debug("Trying to open file %i in the plugin", fd); diff --git a/src/main/host/syscall/mod.rs b/src/main/host/syscall/mod.rs new file mode 100644 index 0000000000..a7413b302c --- /dev/null +++ b/src/main/host/syscall/mod.rs @@ -0,0 +1,60 @@ +use atomic_refcell::AtomicRefCell; +use std::sync::Arc; + +use crate::cshadow as c; +use crate::host::descriptor::{CompatDescriptor, PosixFile}; + +pub mod unistd; + +impl c::Trigger { + pub fn from_posix_file(file: &Arc>, status: c::Status) -> Self { + let file_ptr = Arc::into_raw(Arc::clone(file)); + + Self { + type_: c::_TriggerType_TRIGGER_POSIX_FILE, + object: c::TriggerObject { + as_file: file_ptr as *const c::PosixFileArc, + }, + status, + } + } +} + +impl c::SysCallReturn { + pub fn from_errno(errno: nix::errno::Errno) -> Self { + Self { + state: c::SysCallReturnState_SYSCALL_DONE, + retval: c::SysCallReg { + as_i64: -(errno as i64), + }, + cond: std::ptr::null_mut(), + } + } + + pub fn from_int(int: i64) -> Self { + Self { + state: c::SysCallReturnState_SYSCALL_DONE, + retval: c::SysCallReg { as_i64: int }, + cond: std::ptr::null_mut(), + } + } +} + +/// Returns a pointer to the `CompatDescriptor` for the fd. The pointer will never be NULL. +pub fn get_descriptor( + fd: libc::c_int, + process: *mut c::Process, +) -> Result<*mut CompatDescriptor, nix::errno::Errno> { + // check that fd is within bounds + if fd < 0 { + return Err(nix::errno::Errno::EBADF); + } + + // check that the fd exists + let desc = unsafe { c::process_getRegisteredCompatDescriptor(process, fd) }; + if desc.is_null() { + return Err(nix::errno::Errno::EBADF); + } + + Ok(desc) +} diff --git a/src/main/host/syscall/protected.c b/src/main/host/syscall/protected.c index 467fb20031..30386e0d1f 100644 --- a/src/main/host/syscall/protected.c +++ b/src/main/host/syscall/protected.c @@ -64,8 +64,8 @@ int _syscallhandler_wasBlocked(const SysCallHandler* sys) { return sys->blockedSyscallNR >= 0; } -int _syscallhandler_validateDescriptor(Descriptor* descriptor, - DescriptorType expectedType) { +int _syscallhandler_validateDescriptor(LegacyDescriptor* descriptor, + LegacyDescriptorType expectedType) { if (descriptor) { Status status = descriptor_getStatus(descriptor); @@ -75,7 +75,7 @@ int _syscallhandler_validateDescriptor(Descriptor* descriptor, return -EBADF; } - DescriptorType type = descriptor_getType(descriptor); + LegacyDescriptorType type = descriptor_getType(descriptor); if (expectedType != DT_NONE && type != expectedType) { warning("descriptor handle '%i' is of type %i, expected type %i", diff --git a/src/main/host/syscall/protected.h b/src/main/host/syscall/protected.h index 875286efce..9363435e7a 100644 --- a/src/main/host/syscall/protected.h +++ b/src/main/host/syscall/protected.h @@ -76,7 +76,7 @@ void _syscallhandler_setListenTimeoutMillis(SysCallHandler* sys, int _syscallhandler_isListenTimeoutPending(SysCallHandler* sys); int _syscallhandler_didListenTimeoutExpire(const SysCallHandler* sys); int _syscallhandler_wasBlocked(const SysCallHandler* sys); -int _syscallhandler_validateDescriptor(Descriptor* descriptor, - DescriptorType expectedType); +int _syscallhandler_validateDescriptor(LegacyDescriptor* descriptor, + LegacyDescriptorType expectedType); #endif /* SRC_MAIN_HOST_SYSCALL_PROTECTED_H_ */ diff --git a/src/main/host/syscall/socket.c b/src/main/host/syscall/socket.c index d6b9ec6726..8ff1e30833 100644 --- a/src/main/host/syscall/socket.c +++ b/src/main/host/syscall/socket.c @@ -35,7 +35,7 @@ * must be read from the socket). This function checks if the descriptor is * in this corner case and we should be allowed to read from it. */ static bool _syscallhandler_readableWhenClosed(SysCallHandler* sys, - Descriptor* desc) { + LegacyDescriptor* desc) { if (desc && descriptor_getType(desc) == DT_TCPSOCKET && (descriptor_getStatus(desc) & STATUS_DESCRIPTOR_CLOSED)) { /* Connection error will be -ENOTCONN when reading is done. */ @@ -55,7 +55,7 @@ static int _syscallhandler_validateSocketHelper(SysCallHandler* sys, int sockfd, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, sockfd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, sockfd); if (desc && sock_desc_out) { *sock_desc_out = (Socket*)desc; } @@ -66,7 +66,7 @@ static int _syscallhandler_validateSocketHelper(SysCallHandler* sys, int sockfd, return errcode; } - DescriptorType type = descriptor_getType(desc); + LegacyDescriptorType type = descriptor_getType(desc); if (type != DT_TCPSOCKET && type != DT_UDPSOCKET && type != DT_UNIXSOCKET) { info("descriptor %i with type %i is not a socket", sockfd, (int)type); return -ENOTSOCK; @@ -89,7 +89,7 @@ static int _syscallhandler_validateTCPSocketHelper(SysCallHandler* sys, return errcode; } - DescriptorType type = descriptor_getType((Descriptor*)sock_desc); + LegacyDescriptorType type = descriptor_getType((LegacyDescriptor*)sock_desc); if (type != DT_TCPSOCKET) { info("descriptor %i is not a TCP socket", sockfd); return -EOPNOTSUPP; @@ -112,7 +112,7 @@ static int _syscallhandler_validateUDPSocketHelper(SysCallHandler* sys, return errcode; } - DescriptorType type = descriptor_getType((Descriptor*)sock_desc); + LegacyDescriptorType type = descriptor_getType((LegacyDescriptor*)sock_desc); if (type != DT_UDPSOCKET) { info("descriptor %i is not a UDP socket", sockfd); return -EOPNOTSUPP; @@ -189,7 +189,7 @@ static SysCallReturn _syscallhandler_acceptHelper(SysCallHandler* sys, errcode = tcp_acceptServerPeer( tcp_desc, &inet_addr.sin_addr.s_addr, &inet_addr.sin_port, &accepted_fd); - Descriptor* desc = (Descriptor*)tcp_desc; + LegacyDescriptor* desc = (LegacyDescriptor*)tcp_desc; if (errcode == -EWOULDBLOCK && !(descriptor_getFlags(desc) & O_NONBLOCK)) { /* This is a blocking accept, and we don't have a connection yet. * The socket becomes readable when we have a connection to accept. @@ -214,10 +214,10 @@ static SysCallReturn _syscallhandler_acceptHelper(SysCallHandler* sys, /* Set the flags on the accepted socket if requested. */ if (flags & SOCK_NONBLOCK) { - descriptor_addFlags((Descriptor*)accepted_tcp_desc, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)accepted_tcp_desc, O_NONBLOCK); } if (flags & SOCK_CLOEXEC) { - descriptor_addFlags((Descriptor*)accepted_tcp_desc, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)accepted_tcp_desc, O_CLOEXEC); } /* check if they wanted to know where we got the data from */ @@ -237,7 +237,7 @@ static int _syscallhandler_bindHelper(SysCallHandler* sys, Socket* socket_desc, gchar* peerAddrStr = address_ipToNewString(peerAddr); debug("trying to bind to inet address %s:%u on socket %i with peer %s:%u", bindAddrStr, ntohs(port), - descriptor_getHandle((Descriptor*)socket_desc), peerAddrStr, + descriptor_getHandle((LegacyDescriptor*)socket_desc), peerAddrStr, ntohs(peerPort)); g_free(bindAddrStr); g_free(peerAddrStr); @@ -337,7 +337,7 @@ static int _syscallhandler_getSocketOptHelper(SysCallHandler* sys, Socket* sock, } case SO_ERROR: { *optval = 0; - if (descriptor_getType((Descriptor*)sock) == DT_TCPSOCKET) { + if (descriptor_getType((LegacyDescriptor*)sock) == DT_TCPSOCKET) { /* Return error for failed connect() attempts. */ int connerr = tcp_getConnectionError((TCP*)sock); if (connerr == -ECONNRESET || connerr == -ECONNREFUSED) { @@ -369,7 +369,7 @@ static int _syscallhandler_setSocketOptHelper(SysCallHandler* sys, Socket* sock, size_t newsize = (*val) * 2; // Linux kernel doubles this value upon setting socket_setOutputBufferSize(sock, newsize); - if (descriptor_getType((Descriptor*)sock) == DT_TCPSOCKET) { + if (descriptor_getType((LegacyDescriptor*)sock) == DT_TCPSOCKET) { tcp_disableSendBufferAutotuning((TCP*)sock); } return 0; @@ -380,7 +380,7 @@ static int _syscallhandler_setSocketOptHelper(SysCallHandler* sys, Socket* sock, size_t newsize = (*val) * 2; // Linux kernel doubles this value upon setting socket_setInputBufferSize(sock, newsize); - if (descriptor_getType((Descriptor*)sock) == DT_TCPSOCKET) { + if (descriptor_getType((LegacyDescriptor*)sock) == DT_TCPSOCKET) { tcp_disableReceiveBufferAutotuning((TCP*)sock); } return 0; @@ -429,7 +429,7 @@ SysCallReturn _syscallhandler_recvfromHelper(SysCallHandler* sys, int sockfd, int errcode = _syscallhandler_validateSocketHelper(sys, sockfd, &socket_desc); - Descriptor* desc = (Descriptor*)socket_desc; + LegacyDescriptor* desc = (LegacyDescriptor*)socket_desc; if (errcode < 0 && _syscallhandler_readableWhenClosed(sys, desc)) { errcode = 0; } @@ -561,7 +561,7 @@ SysCallReturn _syscallhandler_sendtoHelper(SysCallHandler* sys, int sockfd, dest_port = ((struct sockaddr_in*)dest_addr)->sin_port; } - Descriptor* desc = (Descriptor*)socket_desc; + LegacyDescriptor* desc = (LegacyDescriptor*)socket_desc; errcode = 0; if (descriptor_getType(desc) == DT_UDPSOCKET) { @@ -828,7 +828,7 @@ SysCallReturn syscallhandler_connect(SysCallHandler* sys, /* Now we are ready to connect. */ errcode = socket_connectToPeer(socket_desc, peerAddr, peerPort, family); - Descriptor* desc = (Descriptor*)socket_desc; + LegacyDescriptor* desc = (LegacyDescriptor*)socket_desc; if (descriptor_getType(desc) == DT_TCPSOCKET && !(descriptor_getFlags(desc) & O_NONBLOCK)) { /* This is a blocking connect call. */ @@ -882,7 +882,7 @@ SysCallReturn syscallhandler_getpeername(SysCallHandler* sys, // If we can validate that, we can delete this comment. // /* Only a TCP socket can be connected to a peer. // * TODO: Needs to be updated when we support AF_UNIX. */ - // DescriptorType type = descriptor_getType((Descriptor*)socket_desc); + // LegacyDescriptorType type = descriptor_getType((LegacyDescriptor*)socket_desc); // if(type != DT_TCPSOCKET) { // info("descriptor %i is not a TCP socket", sockfd); // return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = @@ -894,7 +894,7 @@ SysCallReturn syscallhandler_getpeername(SysCallHandler* sys, struct sockaddr saddr = {0}; size_t slen = 0; - if (descriptor_getType((Descriptor*)socket_desc) == DT_UNIXSOCKET) { + if (descriptor_getType((LegacyDescriptor*)socket_desc) == DT_UNIXSOCKET) { // TODO currently handles socketpair, but will need to be extended // in order to support traditional UNIX sockets struct sockaddr_un* unix_addr = (struct sockaddr_un*)&saddr; @@ -939,7 +939,7 @@ SysCallReturn syscallhandler_getsockname(SysCallHandler* sys, struct sockaddr saddr = {0}; size_t slen = 0; - if (descriptor_getType((Descriptor*)socket_desc) == DT_UNIXSOCKET) { + if (descriptor_getType((LegacyDescriptor*)socket_desc) == DT_UNIXSOCKET) { // TODO currently handles socketpair, but will need to be extended // in order to support traditional UNIX sockets struct sockaddr_un* unix_addr = (struct sockaddr_un*)&saddr; @@ -999,7 +999,7 @@ SysCallReturn syscallhandler_getsockopt(SysCallHandler* sys, errcode = 0; switch (level) { case SOL_TCP: { - if (descriptor_getType((Descriptor*)socket_desc) != DT_TCPSOCKET) { + if (descriptor_getType((LegacyDescriptor*)socket_desc) != DT_TCPSOCKET) { errcode = -EINVAL; break; } @@ -1210,7 +1210,7 @@ SysCallReturn syscallhandler_socket(SysCallHandler* sys, /* Now make sure it will be valid when we operate on it. */ int sockfd = - process_registerDescriptor(sys->process, &sock_desc->super.super); + process_registerLegacyDescriptor(sys->process, &sock_desc->super.super); int errcode = _syscallhandler_validateSocketHelper(sys, sockfd, NULL); if (errcode != 0) { @@ -1274,19 +1274,19 @@ SysCallReturn syscallhandler_socketpair(SysCallHandler* sys, const SysCallArgs* /* Set any options that were given. */ if (type & SOCK_NONBLOCK) { - descriptor_addFlags((Descriptor*)socketA, O_NONBLOCK); - descriptor_addFlags((Descriptor*)socketB, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)socketA, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)socketB, O_NONBLOCK); } if (type & SOCK_CLOEXEC) { - descriptor_addFlags((Descriptor*)socketA, O_CLOEXEC); - descriptor_addFlags((Descriptor*)socketB, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)socketA, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)socketB, O_CLOEXEC); } /* Return the socket fds to the caller. */ int* sockfd = process_getWriteablePtr(sys->process, sys->thread, fdsPtr, 2 * sizeof(int)); - sockfd[0] = process_registerDescriptor(sys->process, (Descriptor*)socketA); - sockfd[1] = process_registerDescriptor(sys->process, (Descriptor*)socketB); + sockfd[0] = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)socketA); + sockfd[1] = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)socketB); debug("Created socketpair with fd %i and fd %i", sockfd[0], sockfd[1]); diff --git a/src/main/host/syscall/timerfd.c b/src/main/host/syscall/timerfd.c index 083e1ebacc..44f7c5f3df 100644 --- a/src/main/host/syscall/timerfd.c +++ b/src/main/host/syscall/timerfd.c @@ -29,7 +29,7 @@ static int _syscallhandler_validateTimerHelper(SysCallHandler* sys, int tfd, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, tfd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, tfd); if (desc && timer_desc_out) { *timer_desc_out = (Timer*)desc; } @@ -67,7 +67,7 @@ SysCallReturn syscallhandler_timerfd_create(SysCallHandler* sys, /* Create the timer and double check that it's valid. */ Timer* timer = timer_new(); - int tfd = process_registerDescriptor(sys->process, (Descriptor*)timer); + int tfd = process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)timer); #ifdef DEBUG /* This should always be a valid descriptor. */ @@ -80,10 +80,10 @@ SysCallReturn syscallhandler_timerfd_create(SysCallHandler* sys, /* Set any options that were given. */ if (flags & TFD_NONBLOCK) { - descriptor_addFlags((Descriptor*)timer, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)timer, O_NONBLOCK); } if (flags & TFD_CLOEXEC) { - descriptor_addFlags((Descriptor*)timer, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)timer, O_CLOEXEC); } debug("timerfd_create() returning fd %i", tfd); diff --git a/src/main/host/syscall/uio.c b/src/main/host/syscall/uio.c index e5fc2614e6..520fa6e606 100644 --- a/src/main/host/syscall/uio.c +++ b/src/main/host/syscall/uio.c @@ -25,10 +25,10 @@ static int _syscallhandler_validateVecParams(SysCallHandler* sys, int fd, PluginPtr iovPtr, unsigned long iovlen, off_t offset, - Descriptor** desc_out, + LegacyDescriptor** desc_out, const struct iovec** iov_out) { /* Get the descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); if (!desc) { return -EBADF; } @@ -90,7 +90,7 @@ _syscallhandler_readvHelper(SysCallHandler* sys, int fd, PluginPtr iovPtr, "offset %ld, flags %d", fd, (void*)iovPtr.val, iovlen, pos_l, pos_h, offset, flags); - Descriptor* desc = NULL; + LegacyDescriptor* desc = NULL; const struct iovec* iov; int errcode = _syscallhandler_validateVecParams( sys, fd, iovPtr, iovlen, offset, &desc, &iov); @@ -99,7 +99,7 @@ _syscallhandler_readvHelper(SysCallHandler* sys, int fd, PluginPtr iovPtr, } /* Some logic depends on the descriptor type. */ - DescriptorType dType = descriptor_getType(desc); + LegacyDescriptorType dType = descriptor_getType(desc); ssize_t result = 0; @@ -209,7 +209,7 @@ _syscallhandler_writevHelper(SysCallHandler* sys, int fd, PluginPtr iovPtr, "offset %ld, flags %d", fd, (void*)iovPtr.val, iovlen, pos_l, pos_h, offset, flags); - Descriptor* desc = NULL; + LegacyDescriptor* desc = NULL; const struct iovec* iov; int errcode = _syscallhandler_validateVecParams( sys, fd, iovPtr, iovlen, offset, &desc, &iov); @@ -218,7 +218,7 @@ _syscallhandler_writevHelper(SysCallHandler* sys, int fd, PluginPtr iovPtr, } /* Some logic depends on the descriptor type. */ - DescriptorType dType = descriptor_getType(desc); + LegacyDescriptorType dType = descriptor_getType(desc); ssize_t result = 0; diff --git a/src/main/host/syscall/unistd.c b/src/main/host/syscall/unistd.c index 662457b6c8..c7a958ea35 100644 --- a/src/main/host/syscall/unistd.c +++ b/src/main/host/syscall/unistd.c @@ -54,12 +54,12 @@ static SysCallReturn _syscallhandler_pipeHelper(SysCallHandler* sys, /* Set any options that were given. */ if (flags & O_NONBLOCK) { - descriptor_addFlags((Descriptor*)pipeReader, O_NONBLOCK); - descriptor_addFlags((Descriptor*)pipeWriter, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)pipeReader, O_NONBLOCK); + descriptor_addFlags((LegacyDescriptor*)pipeWriter, O_NONBLOCK); } if (flags & O_CLOEXEC) { - descriptor_addFlags((Descriptor*)pipeReader, O_CLOEXEC); - descriptor_addFlags((Descriptor*)pipeWriter, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)pipeReader, O_CLOEXEC); + descriptor_addFlags((LegacyDescriptor*)pipeWriter, O_CLOEXEC); } /* Return the pipe fds to the caller. */ @@ -67,9 +67,9 @@ static SysCallReturn _syscallhandler_pipeHelper(SysCallHandler* sys, gint* pipefd = process_getWriteablePtr(sys->process, sys->thread, pipefdPtr, sizeNeeded); pipefd[0] = - process_registerDescriptor(sys->process, (Descriptor*)pipeReader); + process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)pipeReader); pipefd[1] = - process_registerDescriptor(sys->process, (Descriptor*)pipeWriter); + process_registerLegacyDescriptor(sys->process, (LegacyDescriptor*)pipeWriter); debug("Created pipe reader fd %i and writer fd %i", pipefd[0], pipefd[1]); @@ -83,13 +83,13 @@ static SysCallReturn _syscallhandler_readHelper(SysCallHandler* sys, int fd, "trying to read %zu bytes on fd %i at offset %li", bufSize, fd, offset); /* Get the descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); if (!desc) { return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -EBADF}; } /* Some logic depends on the descriptor type. */ - DescriptorType dType = descriptor_getType(desc); + LegacyDescriptorType dType = descriptor_getType(desc); /* We can only seek on files, otherwise its a pipe error. */ if (dType != DT_FILE && offset != 0) { @@ -183,13 +183,13 @@ static SysCallReturn _syscallhandler_writeHelper(SysCallHandler* sys, int fd, offset); /* Get the descriptor. */ - Descriptor* desc = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* desc = process_getRegisteredLegacyDescriptor(sys->process, fd); if (!desc) { return (SysCallReturn){.state = SYSCALL_DONE, .retval.as_i64 = -EBADF}; } /* Some logic depends on the descriptor type. */ - DescriptorType dType = descriptor_getType(desc); + LegacyDescriptorType dType = descriptor_getType(desc); /* We can only seek on files, otherwise its a pipe error. */ if (dType != DT_FILE && offset != 0) { @@ -284,7 +284,7 @@ SysCallReturn syscallhandler_close(SysCallHandler* sys, } /* Check if this is a virtual Shadow descriptor. */ - Descriptor* descriptor = process_getRegisteredDescriptor(sys->process, fd); + LegacyDescriptor* descriptor = process_getRegisteredLegacyDescriptor(sys->process, fd); errorCode = _syscallhandler_validateDescriptor(descriptor, DT_NONE); if (descriptor && !errorCode) { diff --git a/src/main/host/syscall/unistd.rs b/src/main/host/syscall/unistd.rs new file mode 100644 index 0000000000..2dee19d76d --- /dev/null +++ b/src/main/host/syscall/unistd.rs @@ -0,0 +1,337 @@ +use crate::cshadow as c; +use crate::host::descriptor::pipe; +use crate::host::descriptor::{ + CompatDescriptor, Descriptor, DescriptorFlags, FileFlags, FileMode, PosixFile, SyscallReturn, +}; +use crate::host::syscall; +use crate::utility::event_queue::EventQueue; + +use std::sync::Arc; + +use atomic_refcell::AtomicRefCell; +use log::*; + +pub fn close(sys: &mut c::SysCallHandler, args: &c::SysCallArgs) -> c::SysCallReturn { + let fd = unsafe { args.args[0].as_i64 } as libc::c_int; + + debug!("Trying to close fd {}", fd); + + // scope used to make sure that desc cannot be used after deregistering it + { + // get the descriptor, or return early if it doesn't exist + let desc = match syscall::get_descriptor(fd, sys.process) { + Ok(d) => unsafe { &mut *d }, + Err(errno) => return c::SysCallReturn::from_errno(errno), + }; + + // if it's a legacy descriptor, use the C syscall handler instead + if let CompatDescriptor::Legacy(_) = desc { + return unsafe { + c::syscallhandler_close( + sys as *mut c::SysCallHandler, + args as *const c::SysCallArgs, + ) + }; + } + } + + unsafe { c::process_deregisterCompatDescriptor(sys.process, fd) }; + + c::SysCallReturn::from_int(0) +} + +pub fn read(sys: &mut c::SysCallHandler, args: &c::SysCallArgs) -> c::SysCallReturn { + let fd = unsafe { args.args[0].as_i64 } as libc::c_int; + let buf_ptr = unsafe { args.args[1].as_ptr }; + let buf_size = unsafe { args.args[2].as_u64 } as libc::size_t; + let offset = 0 as libc::off_t; + + read_helper(sys, args, fd, buf_ptr, buf_size, offset) +} + +fn read_helper( + sys: &mut c::SysCallHandler, + args: &c::SysCallArgs, + fd: libc::c_int, + buf_ptr: c::PluginPtr, + buf_size: libc::size_t, + _offset: libc::off_t, +) -> c::SysCallReturn { + // get the descriptor, or return early if it doesn't exist + let desc = match syscall::get_descriptor(fd, sys.process) { + Ok(d) => unsafe { &mut *d }, + Err(errno) => return c::SysCallReturn::from_errno(errno), + }; + + // if it's a legacy descriptor, use the C syscall handler instead + let desc = match desc { + CompatDescriptor::New(d) => d, + CompatDescriptor::Legacy(_) => unsafe { + return c::syscallhandler_read( + sys as *mut c::SysCallHandler, + args as *const c::SysCallArgs, + ); + }, + }; + + // need a non-null buffer + if buf_ptr.val == 0 { + return c::SysCallReturn::from_errno(nix::errno::Errno::EFAULT); + } + + // need a non-zero size + if buf_size == 0 { + info!("Invalid length {} provided on descriptor {}", buf_size, fd); + return c::SysCallReturn::from_errno(nix::errno::Errno::EINVAL); + } + + // TODO: dynamically compute size based on how much data is actually available in the descriptor + let size_needed = std::cmp::min(buf_size, c::SYSCALL_IO_BUFSIZE as usize); + + let buf_ptr = + unsafe { c::process_getWriteablePtr(sys.process, sys.thread, buf_ptr, size_needed as u64) }; + let mut buf = unsafe { std::slice::from_raw_parts_mut(buf_ptr as *mut u8, size_needed) }; + + let posix_file = desc.get_file(); + let file_flags = posix_file.borrow().get_flags(); + + // call the file's read(), and run any resulting events + let result = EventQueue::queue_and_run(|event_queue| { + posix_file.borrow_mut().read(&mut buf, event_queue) + }); + + // if the syscall would block and it's a blocking descriptor + if result == SyscallReturn::Error(nix::errno::EWOULDBLOCK) + && !file_flags.contains(FileFlags::NONBLOCK) + { + let trigger = + c::Trigger::from_posix_file(posix_file, c::_Status_STATUS_DESCRIPTOR_READABLE); + + return c::SysCallReturn { + state: c::SysCallReturnState_SYSCALL_BLOCK, + retval: c::SysCallReg { as_i64: 0i64 }, + cond: unsafe { c::syscallcondition_new(trigger, std::ptr::null_mut()) }, + }; + } + + match result { + SyscallReturn::Success(x) => c::SysCallReturn::from_int(x as i64), + SyscallReturn::Error(x) => c::SysCallReturn::from_errno(x), + } +} + +pub fn write(sys: &mut c::SysCallHandler, args: &c::SysCallArgs) -> c::SysCallReturn { + let fd = unsafe { args.args[0].as_i64 } as libc::c_int; + let buf_ptr = unsafe { args.args[1].as_ptr }; + let buf_size = unsafe { args.args[2].as_u64 } as libc::size_t; + let offset = 0 as libc::off_t; + + write_helper(sys, args, fd, buf_ptr, buf_size, offset) +} + +fn write_helper( + sys: &mut c::SysCallHandler, + args: &c::SysCallArgs, + fd: libc::c_int, + buf_ptr: c::PluginPtr, + buf_size: libc::size_t, + _offset: libc::off_t, +) -> c::SysCallReturn { + // get the descriptor, or return early if it doesn't exist + let desc = match syscall::get_descriptor(fd, sys.process) { + Ok(d) => unsafe { &mut *d }, + Err(errno) => return c::SysCallReturn::from_errno(errno), + }; + + // if it's a legacy descriptor, use the C syscall handler instead + let desc = match desc { + CompatDescriptor::New(d) => d, + CompatDescriptor::Legacy(_) => unsafe { + return c::syscallhandler_write( + sys as *mut c::SysCallHandler, + args as *const c::SysCallArgs, + ); + }, + }; + + // need a non-null buffer + if buf_ptr.val == 0 { + return c::SysCallReturn::from_errno(nix::errno::Errno::EFAULT); + } + + // TODO: dynamically compute size based on how much data is actually available in the descriptor + let size_needed = std::cmp::min(buf_size, c::SYSCALL_IO_BUFSIZE as usize); + + let buf_ptr = + unsafe { c::process_getReadablePtr(sys.process, sys.thread, buf_ptr, size_needed as u64) }; + let buf = unsafe { std::slice::from_raw_parts(buf_ptr as *const u8, size_needed) }; + + let posix_file = desc.get_file(); + let file_flags = posix_file.borrow().get_flags(); + + // call the file's write(), and run any resulting events + let result = + EventQueue::queue_and_run(|event_queue| posix_file.borrow_mut().write(&buf, event_queue)); + + // if the syscall would block and it's a blocking descriptor + if result == SyscallReturn::Error(nix::errno::EWOULDBLOCK) + && !file_flags.contains(FileFlags::NONBLOCK) + { + let trigger = + c::Trigger::from_posix_file(posix_file, c::_Status_STATUS_DESCRIPTOR_WRITABLE); + + return c::SysCallReturn { + state: c::SysCallReturnState_SYSCALL_BLOCK, + retval: c::SysCallReg { as_i64: 0i64 }, + cond: unsafe { c::syscallcondition_new(trigger, std::ptr::null_mut()) }, + }; + } + + match result { + SyscallReturn::Success(x) => c::SysCallReturn::from_int(x as i64), + SyscallReturn::Error(x) => c::SysCallReturn::from_errno(x), + } +} + +pub fn pipe(sys: &mut c::SysCallHandler, args: &c::SysCallArgs) -> c::SysCallReturn { + let fd_ptr = unsafe { args.args[0].as_ptr }; + + pipe_helper(sys, fd_ptr, 0) +} + +pub fn pipe2(sys: &mut c::SysCallHandler, args: &c::SysCallArgs) -> c::SysCallReturn { + let fd_ptr = unsafe { args.args[0].as_ptr }; + let flags = unsafe { args.args[1].as_u64 } as libc::c_int; + + pipe_helper(sys, fd_ptr, flags) +} + +fn pipe_helper(sys: &mut c::SysCallHandler, fd_ptr: c::PluginPtr, flags: i32) -> c::SysCallReturn { + // make sure they didn't pass a NULL pointer + if fd_ptr.val == 0 { + return c::SysCallReturn::from_errno(nix::errno::Errno::EFAULT); + } + + let mut file_flags = FileFlags::empty(); + let mut descriptor_flags = DescriptorFlags::empty(); + + // keep track of which flags we use + let mut remaining_flags = flags; + + if flags & libc::O_NONBLOCK != 0 { + file_flags.insert(FileFlags::NONBLOCK); + remaining_flags &= !libc::O_NONBLOCK; + } + + if flags & libc::O_CLOEXEC != 0 { + descriptor_flags.insert(DescriptorFlags::CLOEXEC); + remaining_flags &= !libc::O_CLOEXEC; + } + + // the user requested flags that we don't support + if remaining_flags != 0 { + // exit early if the O_DIRECT flag was set + if remaining_flags & libc::O_DIRECT != 0 { + warn!("We don't currently support pipes in 'O_DIRECT' mode"); + return c::SysCallReturn::from_errno(nix::errno::Errno::EOPNOTSUPP); + } + warn!("Ignoring pipe flags"); + } + + // reference-counted buffer for the pipe + let buffer = pipe::SharedBuf::new(); + let buffer = Arc::new(AtomicRefCell::new(buffer)); + + // reference-counted file object for read end of the pipe + let reader = pipe::PipeFile::new(Arc::clone(&buffer), FileMode::READ, file_flags); + let reader = Arc::new(AtomicRefCell::new(PosixFile::Pipe(reader))); + + // reference-counted file object for write end of the pipe + let writer = pipe::PipeFile::new(Arc::clone(&buffer), FileMode::WRITE, file_flags); + let writer = Arc::new(AtomicRefCell::new(PosixFile::Pipe(writer))); + + // set the file objects to listen for events on the buffer + pipe::PipeFile::enable_notifications(&reader); + pipe::PipeFile::enable_notifications(&writer); + + // file descriptors for the read and write file objects + let mut reader_desc = Descriptor::new(reader); + let mut writer_desc = Descriptor::new(writer); + + // set the file descriptor flags + reader_desc.set_flags(descriptor_flags); + writer_desc.set_flags(descriptor_flags); + + // register the file descriptors and return them to the caller + let num_items = 2; + let size_needed = num_items * std::mem::size_of::(); + let fd_ptr = + unsafe { c::process_getWriteablePtr(sys.process, sys.thread, fd_ptr, size_needed as u64) }; + let fds = unsafe { std::slice::from_raw_parts_mut(fd_ptr as *mut libc::c_int, num_items) }; + + fds[0] = unsafe { + c::process_registerCompatDescriptor( + sys.process, + Box::into_raw(Box::new(CompatDescriptor::New(reader_desc))), + ) + }; + fds[1] = unsafe { + c::process_registerCompatDescriptor( + sys.process, + Box::into_raw(Box::new(CompatDescriptor::New(writer_desc))), + ) + }; + + debug!("Created pipe reader fd {} and writer fd {}", fds[0], fds[1]); + + c::SysCallReturn::from_int(0) +} + +mod export { + use super::*; + + #[no_mangle] + pub extern "C" fn rustsyscallhandler_close( + sys: *mut c::SysCallHandler, + args: *const c::SysCallArgs, + ) -> c::SysCallReturn { + assert!(!sys.is_null() && !args.is_null()); + close(unsafe { &mut *sys }, unsafe { &*args }) + } + + #[no_mangle] + pub extern "C" fn rustsyscallhandler_read( + sys: *mut c::SysCallHandler, + args: *const c::SysCallArgs, + ) -> c::SysCallReturn { + assert!(!sys.is_null() && !args.is_null()); + read(unsafe { &mut *sys }, unsafe { &*args }) + } + + #[no_mangle] + pub extern "C" fn rustsyscallhandler_write( + sys: *mut c::SysCallHandler, + args: *const c::SysCallArgs, + ) -> c::SysCallReturn { + assert!(!sys.is_null() && !args.is_null()); + write(unsafe { &mut *sys }, unsafe { &*args }) + } + + #[no_mangle] + pub extern "C" fn rustsyscallhandler_pipe( + sys: *mut c::SysCallHandler, + args: *const c::SysCallArgs, + ) -> c::SysCallReturn { + assert!(!sys.is_null() && !args.is_null()); + pipe(unsafe { &mut *sys }, unsafe { &*args }) + } + + #[no_mangle] + pub extern "C" fn rustsyscallhandler_pipe2( + sys: *mut c::SysCallHandler, + args: *const c::SysCallArgs, + ) -> c::SysCallReturn { + assert!(!sys.is_null() && !args.is_null()); + pipe2(unsafe { &mut *sys }, unsafe { &*args }) + } +} diff --git a/src/main/host/syscall_condition.c b/src/main/host/syscall_condition.c index cf3a925458..611abe7b0d 100644 --- a/src/main/host/syscall_condition.c +++ b/src/main/host/syscall_condition.c @@ -57,6 +57,12 @@ SysCallCondition* syscallcondition_new(Trigger trigger, Timer* timeout) { descriptor_ref(cond->trigger.object.as_descriptor); return cond; } + case TRIGGER_POSIX_FILE: { + /* The file represents an Arc, so the reference count is fine; + * Just need to remember to drop it later. + */ + return cond; + } case TRIGGER_FUTEX: { futex_ref(cond->trigger.object.as_futex); return cond; @@ -80,7 +86,7 @@ static void _syscallcondition_cleanupListeners(SysCallCondition* cond) { /* Destroy the listeners, which will also unref and free cond. */ if (cond->timeout && cond->timeoutListener) { descriptor_removeListener( - (Descriptor*)cond->timeout, cond->timeoutListener); + (LegacyDescriptor*)cond->timeout, cond->timeoutListener); statuslistener_setMonitorStatus(cond->timeoutListener, STATUS_NONE, SLF_NEVER); } @@ -96,6 +102,10 @@ static void _syscallcondition_cleanupListeners(SysCallCondition* cond) { cond->trigger.object.as_descriptor, cond->triggerListener); break; } + case TRIGGER_POSIX_FILE: { + posixfile_removeListener(cond->trigger.object.as_file, cond->triggerListener); + break; + } case TRIGGER_FUTEX: { futex_removeListener(cond->trigger.object.as_futex, cond->triggerListener); break; @@ -147,6 +157,10 @@ static void _syscallcondition_free(SysCallCondition* cond) { descriptor_unref(cond->trigger.object.as_descriptor); break; } + case TRIGGER_POSIX_FILE: { + posixfile_drop(cond->trigger.object.as_file); + break; + } case TRIGGER_FUTEX: { futex_unref(cond->trigger.object.as_futex); break; @@ -201,6 +215,12 @@ static void _syscallcondition_logListeningState(SysCallCondition* cond, cond->timeout ? " and " : ""); break; } + case TRIGGER_POSIX_FILE: { + g_string_append_printf(string, "status on posix file %p%s", + (void*)cond->trigger.object.as_file, + cond->timeout ? " and " : ""); + break; + } case TRIGGER_FUTEX: { g_string_append_printf(string, "status on futex %p%s", (void*)futex_getAddress(cond->trigger.object.as_futex).val, @@ -241,6 +261,12 @@ static bool _syscallcondition_statusIsValid(SysCallCondition* cond) { } break; } + case TRIGGER_POSIX_FILE: { + if (posixfile_getStatus(cond->trigger.object.as_file) & cond->trigger.status) { + return true; + } + break; + } case TRIGGER_FUTEX: { // Futex status doesn't change return true; @@ -355,7 +381,7 @@ void syscallcondition_waitNonblock(SysCallCondition* cond, Process* proc, /* Attach the listener to the timer. */ descriptor_addListener( - (Descriptor*)cond->timeout, cond->timeoutListener); + (LegacyDescriptor*)cond->timeout, cond->timeoutListener); } if (cond->trigger.object.as_pointer && !cond->triggerListener) { @@ -376,6 +402,15 @@ void syscallcondition_waitNonblock(SysCallCondition* cond, Process* proc, descriptor_addListener(cond->trigger.object.as_descriptor, cond->triggerListener); break; } + case TRIGGER_POSIX_FILE: { + /* Monitor the requested status when it transitions from off to on. */ + statuslistener_setMonitorStatus( + cond->triggerListener, cond->trigger.status, SLF_OFF_TO_ON); + + /* Attach the listener to the descriptor. */ + posixfile_addListener(cond->trigger.object.as_file, cond->triggerListener); + break; + } case TRIGGER_FUTEX: { /* Monitor the requested status an every status change. */ statuslistener_setMonitorStatus( diff --git a/src/main/host/syscall_condition.h b/src/main/host/syscall_condition.h index b07f607890..d54506e699 100644 --- a/src/main/host/syscall_condition.h +++ b/src/main/host/syscall_condition.h @@ -19,6 +19,7 @@ typedef enum _TriggerType TriggerType; enum _TriggerType { TRIGGER_NONE, TRIGGER_DESCRIPTOR, + TRIGGER_POSIX_FILE, TRIGGER_FUTEX, }; @@ -26,7 +27,8 @@ enum _TriggerType { typedef union _TriggerObject TriggerObject; union _TriggerObject { void* as_pointer; - Descriptor* as_descriptor; + LegacyDescriptor* as_descriptor; + const PosixFileArc* as_file; Futex* as_futex; }; diff --git a/src/main/host/syscall_handler.c b/src/main/host/syscall_handler.c index 847b88ca39..5bf8b59eca 100644 --- a/src/main/host/syscall_handler.c +++ b/src/main/host/syscall_handler.c @@ -170,6 +170,18 @@ static void _syscallhandler_post_syscall(SysCallHandler* sys, long number, debug("native syscall %ld " #s, args->number); \ scr = (SysCallReturn){.state = SYSCALL_NATIVE}; \ break + +#ifdef USE_C_SYSCALLS +#define HANDLE_RUST(s) HANDLE(s) +#else +#define HANDLE_RUST(s) \ + case SYS_##s: \ + _syscallhandler_pre_syscall(sys, args->number, #s); \ + scr = rustsyscallhandler_##s(sys, args); \ + _syscallhandler_post_syscall(sys, args->number, #s, &scr); \ + break +#endif + SysCallReturn syscallhandler_make_syscall(SysCallHandler* sys, const SysCallArgs* args) { MAGIC_ASSERT(sys); @@ -200,7 +212,7 @@ SysCallReturn syscallhandler_make_syscall(SysCallHandler* sys, HANDLE(brk); HANDLE(clock_gettime); HANDLE(clone); - HANDLE(close); + HANDLE_RUST(close); HANDLE(connect); HANDLE(creat); HANDLE(epoll_create); @@ -260,8 +272,8 @@ SysCallReturn syscallhandler_make_syscall(SysCallHandler* sys, HANDLE(newfstatat); HANDLE(open); HANDLE(openat); - HANDLE(pipe); - HANDLE(pipe2); + HANDLE_RUST(pipe); + HANDLE_RUST(pipe2); HANDLE(pread64); HANDLE(preadv); #ifdef SYS_preadv2 @@ -272,7 +284,7 @@ SysCallReturn syscallhandler_make_syscall(SysCallHandler* sys, #ifdef SYS_pwritev2 HANDLE(pwritev2); #endif - HANDLE(read); + HANDLE_RUST(read); HANDLE(readahead); HANDLE(readlinkat); HANDLE(readv); @@ -298,7 +310,7 @@ SysCallReturn syscallhandler_make_syscall(SysCallHandler* sys, HANDLE(uname); HANDLE(unlinkat); HANDLE(utimensat); - HANDLE(write); + HANDLE_RUST(write); HANDLE(writev); // ************************************** diff --git a/src/main/host/syscall_types.rs b/src/main/host/syscall_types.rs index 09590fc8c0..3ad6166054 100644 --- a/src/main/host/syscall_types.rs +++ b/src/main/host/syscall_types.rs @@ -1,4 +1,4 @@ -use crate::cbindings as c; +use crate::cshadow as c; use std::convert::From; #[derive(Copy, Clone)] diff --git a/src/main/host/thread.rs b/src/main/host/thread.rs index d0725ac10b..120a2afe0d 100644 --- a/src/main/host/thread.rs +++ b/src/main/host/thread.rs @@ -1,5 +1,5 @@ use super::syscall_types::{PluginPtr, SysCallReg}; -use crate::cbindings as c; +use crate::cshadow as c; use crate::utility::syscall; use nix::errno::Errno; diff --git a/src/main/lib.rs b/src/main/lib.rs index 1e2a0298c8..2fd351aab3 100644 --- a/src/main/lib.rs +++ b/src/main/lib.rs @@ -2,12 +2,15 @@ * The Shadow Simulator * See LICENSE for licensing information */ -mod cbindings { - // Inline the bindgen-generated bindings, suppressing warnings. + +mod cshadow { + // Inline the bindgen-generated Rust bindings, suppressing warnings. #![allow(unused)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] + // the following is added due to https://github.com/rust-lang/rust/issues/66220 + #![allow(improper_ctypes)] include!("bindings/rust/wrapper.rs"); } diff --git a/src/main/utility/byte_queue.rs b/src/main/utility/byte_queue.rs index 315b944bcc..dbd63e7234 100644 --- a/src/main/utility/byte_queue.rs +++ b/src/main/utility/byte_queue.rs @@ -45,6 +45,11 @@ impl ByteQueue { } } + /// Number of bytes in the queue. + pub fn len(&self) -> usize { + self.length + } + /// Push bytes to the head of the queue. pub fn push(&mut self, src: &[u8]) { // create new buffer head lazily as opposed to proactively @@ -187,6 +192,13 @@ mod export { } } + #[no_mangle] + pub extern "C" fn bytequeue_len(bq: *mut ByteQueue) -> libc::size_t { + assert!(!bq.is_null()); + let bq = unsafe { &mut *bq }; + bq.len() + } + #[no_mangle] pub extern "C" fn bytequeue_push( bq: *mut ByteQueue, diff --git a/src/main/utility/event_queue.rs b/src/main/utility/event_queue.rs new file mode 100644 index 0000000000..1ecad3589e --- /dev/null +++ b/src/main/utility/event_queue.rs @@ -0,0 +1,172 @@ +use atomic_refcell::AtomicRefCell; +use log::*; +use std::sync::{Arc, Weak}; + +/// A queue of events (functions/closures) which when run can add their own events to the queue. +pub struct EventQueue(std::collections::VecDeque>); + +impl EventQueue { + /// Create an empty event queue. + pub fn new() -> Self { + Self(std::collections::VecDeque::new()) + } + + /// Add an event to the queue. + pub fn add(&mut self, f: impl FnOnce(&mut Self) + 'static) { + self.0.push_back(Box::new(f)); + } + + /// Process all of the events in the queue (and any new events that are generated). + pub fn run(&mut self) { + // loop until there are no more events + let mut count = 0; + while let Some(f) = self.0.pop_front() { + // run the event and allow it to add new events + (f)(self); + + count += 1; + if count == 200 { + debug!("Possible infinite loop of event callbacks."); + } else if count == 10_000 { + warn!("Very likely an infinite loop of event callbacks."); + } + } + } + + /// A convenience function to create an EventQueue, allow the caller to add events, + /// and process them all before returning. + pub fn queue_and_run(f: F) -> U + where + F: FnOnce(&mut Self) -> U, + { + let mut event_queue = Self::new(); + let rv = (f)(&mut event_queue); + event_queue.run(); + rv + } +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +struct HandleId(u32); + +/// A handle allows you to stop listening for events. +pub struct Handle { + id: HandleId, + source: Weak>>, +} + +impl Handle { + fn new(id: HandleId, source: Weak>>) -> Self { + Self { id, source } + } + + pub fn stop_listening(self) {} +} + +impl Drop for Handle { + fn drop(&mut self) { + if let Some(x) = self.source.upgrade() { + x.borrow_mut().remove_listener(self.id); + } + } +} + +pub struct EventSource { + inner: Arc>>, +} + +impl EventSource { + pub fn new() -> Self { + Self { + inner: Arc::new(AtomicRefCell::new(EventSourceInner::new())), + } + } + + pub fn add_listener( + &mut self, + notify_fn: impl Fn(T, &mut EventQueue) + Send + Sync + 'static, + ) -> Handle { + let inner_ref = Arc::downgrade(&Arc::clone(&self.inner)); + self.inner.borrow_mut().add_listener(inner_ref, notify_fn) + } + + pub fn notify_listeners(&mut self, message: T, event_queue: &mut EventQueue) { + for (_, l) in &self.inner.borrow().listeners { + let l_clone = l.clone(); + event_queue.add(move |event_queue| (l_clone)(message, event_queue)); + } + } +} + +struct EventSourceInner { + listeners: std::vec::Vec<(HandleId, Arc>)>, + next_id: std::num::Wrapping, +} + +impl EventSourceInner { + pub fn new() -> Self { + Self { + listeners: std::vec::Vec::new(), + next_id: std::num::Wrapping(0), + } + } + + fn get_unused_id(&mut self) -> HandleId { + // it's very unlikely that there will be collisions, but we loop anyways since we + // don't care about worst-case performance here + let handle_id = loop { + let id = HandleId(self.next_id.0); + self.next_id += std::num::Wrapping(1); + + if !self.listeners.iter().any(|x| x.0 == id) { + break id; + } + }; + handle_id + } + + pub fn add_listener( + &mut self, + inner: std::sync::Weak>, + notify_fn: impl Fn(T, &mut EventQueue) + Send + Sync + 'static, + ) -> Handle { + let handle_id = self.get_unused_id(); + + self.listeners + .push((handle_id, Arc::new(Box::new(notify_fn)))); + + Handle::new(handle_id, inner) + } + + pub fn remove_listener(&mut self, id: HandleId) { + self.listeners + .remove(self.listeners.iter().position(|x| x.0 == id).unwrap()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_eventqueue() { + let counter = Arc::new(AtomicRefCell::new(0u32)); + let counter_clone = Arc::clone(&counter); + + let mut source = EventSource::new(); + + let handle = source.add_listener(move |inc, _| { + *counter_clone.borrow_mut() += inc; + }); + + EventQueue::queue_and_run(|queue| source.notify_listeners(1, queue)); + EventQueue::queue_and_run(|queue| source.notify_listeners(3, queue)); + + handle.stop_listening(); + + EventQueue::queue_and_run(|queue| source.notify_listeners(5, queue)); + EventQueue::queue_and_run(|queue| source.notify_listeners(7, queue)); + + assert_eq!(*counter.borrow(), 4); + } +} diff --git a/src/main/utility/mod.rs b/src/main/utility/mod.rs index d293f53f8b..b4b93a8a97 100644 --- a/src/main/utility/mod.rs +++ b/src/main/utility/mod.rs @@ -1,4 +1,5 @@ pub mod byte_queue; +pub mod event_queue; pub mod interval_map; pub mod proc_maps; pub mod syscall; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 598eb74498..dabe535b88 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -47,6 +47,7 @@ add_subdirectory(file) add_subdirectory(futex) add_subdirectory(memory) add_subdirectory(phold) +add_subdirectory(pipe) add_subdirectory(poll) add_subdirectory(pthreads) add_subdirectory(random) diff --git a/src/test/Cargo.toml b/src/test/Cargo.toml index 5a02f33bd6..5493397ae1 100644 --- a/src/test/Cargo.toml +++ b/src/test/Cargo.toml @@ -73,6 +73,10 @@ path = "memory/test_mmap.rs" name = "test_eventfd" path = "eventfd/test_eventfd.rs" +[[bin]] +name = "test_pipe" +path = "pipe/test_pipe.rs" + [dependencies] libc = "0.2" nix = "0.19.0" diff --git a/src/test/pipe/CMakeLists.txt b/src/test/pipe/CMakeLists.txt new file mode 100644 index 0000000000..44b4537945 --- /dev/null +++ b/src/test/pipe/CMakeLists.txt @@ -0,0 +1,18 @@ +add_test(NAME pipe COMMAND ../target/debug/test_pipe --libc-passing) +list(APPEND TestNames pipe) + +foreach(Method ptrace preload) + # runs the shadow tests with only the passing tests, not all tests + add_test( + NAME pipe-shadow-${Method} + COMMAND sh -c "\ + ${yaml2xml} ${CMAKE_CURRENT_SOURCE_DIR}/pipe.shadow.config.yaml --output - \ + | ${CMAKE_BINARY_DIR}/src/main/shadow --interpose-method=${Method} -l debug -d pipe.shadow-${Method}.data - \ + " + ) + list(APPEND TestNames pipe-shadow-${Method}) +endforeach() + +set_property(TEST ${TestNames} PROPERTY ENVIRONMENT "RUST_BACKTRACE=1") +# need to check the test's return code, not just shadow's (see shadow/shadow#902) +set_property(TEST ${TestNames} PROPERTY FAIL_REGULAR_EXPRESSION "main error code '.*' for process") diff --git a/src/test/pipe/pipe.shadow.config.yaml b/src/test/pipe/pipe.shadow.config.yaml new file mode 100644 index 0000000000..eff9c65c01 --- /dev/null +++ b/src/test/pipe/pipe.shadow.config.yaml @@ -0,0 +1,32 @@ +options: + stoptime: 5 +topology: +- graphml: | + + + + + + + + + US + 10240 + 10240 + + + 50.0 + 0.0 + + + +plugins: +- id: testpipe + path: ../target/debug/test_pipe +hosts: +- id: testnode + quantity: 1 + processes: + - plugin: testpipe + starttime: 1 + arguments: --shadow-passing diff --git a/src/test/pipe/test_pipe.rs b/src/test/pipe/test_pipe.rs new file mode 100644 index 0000000000..b1e1b732b4 --- /dev/null +++ b/src/test/pipe/test_pipe.rs @@ -0,0 +1,246 @@ +/* + * The Shadow Simulator + * See LICENSE for licensing information + */ + +use test_utils::set; +use test_utils::TestEnvironment as TestEnv; + +fn main() -> Result<(), String> { + // should we restrict the tests we run? + let filter_shadow_passing = std::env::args().any(|x| x == "--shadow-passing"); + let filter_libc_passing = std::env::args().any(|x| x == "--libc-passing"); + // should we summarize the results rather than exit on a failed test + let summarize = std::env::args().any(|x| x == "--summarize"); + + let mut tests = get_tests(); + if filter_shadow_passing { + tests = tests + .into_iter() + .filter(|x| x.passing(TestEnv::Shadow)) + .collect() + } + if filter_libc_passing { + tests = tests + .into_iter() + .filter(|x| x.passing(TestEnv::Libc)) + .collect() + } + + test_utils::run_tests(&tests, summarize)?; + + println!("Success."); + Ok(()) +} + +fn get_tests() -> Vec> { + let tests: Vec> = vec![ + test_utils::ShadowTest::new("test_pipe", test_pipe, set![TestEnv::Libc, TestEnv::Shadow]), + test_utils::ShadowTest::new( + "test_read_write", + test_read_write, + set![TestEnv::Libc, TestEnv::Shadow], + ), + // TODO: support dup() in Shadow + test_utils::ShadowTest::new("test_dup", test_dup, set![TestEnv::Libc]), + test_utils::ShadowTest::new( + "test_write_to_read_end", + test_write_to_read_end, + set![TestEnv::Libc, TestEnv::Shadow], + ), + test_utils::ShadowTest::new( + "test_read_from_write_end", + test_read_from_write_end, + set![TestEnv::Libc, TestEnv::Shadow], + ), + ]; + + tests +} + +fn test_pipe() -> Result<(), String> { + let mut fds = [0 as libc::c_int; 2]; + test_utils::check_system_call!(|| { unsafe { libc::pipe(fds.as_mut_ptr()) } }, &[])?; + + test_utils::result_assert(fds[0] > 0, "fds[0] not set")?; + test_utils::result_assert(fds[1] > 0, "fds[1] not set")?; + + Ok(()) +} + +fn test_read_write() -> Result<(), String> { + let mut fds = [0 as libc::c_int; 2]; + test_utils::check_system_call!(|| { unsafe { libc::pipe(fds.as_mut_ptr()) } }, &[])?; + + test_utils::result_assert(fds[0] > 0, "fds[0] not set")?; + test_utils::result_assert(fds[1] > 0, "fds[1] not set")?; + + let (read_fd, write_fd) = (fds[0], fds[1]); + + test_utils::run_and_close_fds(&[write_fd, read_fd], || { + let write_buf = [1u8, 2, 3, 4]; + + let rv = test_utils::check_system_call!( + || { + unsafe { + libc::write( + write_fd, + write_buf.as_ptr() as *const libc::c_void, + write_buf.len(), + ) + } + }, + &[] + )?; + + test_utils::result_assert_eq(rv, 4, "Expected to write 4 bytes")?; + + let mut read_buf = [0u8; 4]; + + let rv = test_utils::check_system_call!( + || { + unsafe { + libc::read( + read_fd, + read_buf.as_mut_ptr() as *mut libc::c_void, + read_buf.len(), + ) + } + }, + &[] + )?; + + test_utils::result_assert_eq(rv, 4, "Expected to read 4 bytes")?; + + test_utils::result_assert_eq(write_buf, read_buf, "Buffers differ")?; + + Ok(()) + }) +} + +fn test_dup() -> Result<(), String> { + let mut fds = [0 as libc::c_int; 2]; + test_utils::check_system_call!(|| { unsafe { libc::pipe(fds.as_mut_ptr()) } }, &[])?; + + test_utils::result_assert(fds[0] > 0, "fds[0] not set")?; + test_utils::result_assert(fds[1] > 0, "fds[1] not set")?; + + let (read_fd, write_fd) = (fds[0], fds[1]); + + // TODO: use nix instead here + fn write(fd: libc::c_int, buf: &[u8]) -> Result { + test_utils::check_system_call!( + || { unsafe { libc::write(fd, buf.as_ptr() as *const libc::c_void, buf.len(),) } }, + &[] + ) + } + + fn read(fd: libc::c_int, buf: &mut [u8]) -> Result { + test_utils::check_system_call!( + || { unsafe { libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len(),) } }, + &[] + ) + } + + test_utils::run_and_close_fds(&[write_fd, read_fd], || { + let write_fd_dup = + test_utils::check_system_call!(|| { unsafe { libc::dup(write_fd) } }, &[])?; + + test_utils::run_and_close_fds(&[write_fd_dup], || { + let write_buf = [1u8, 2, 3, 4]; + + // write 4 bytes to original fd + let rv = write(write_fd, &write_buf)?; + test_utils::result_assert_eq(rv, 4, "Expected to write 4 bytes")?; + + // write 5 bytes to duped fd + let rv = write(write_fd_dup, &write_buf)?; + test_utils::result_assert_eq(rv, 4, "Expected to write 4 bytes")?; + + // read 8 bytes + let mut read_buf = [0u8; 8]; + let rv = read(read_fd, &mut read_buf)?; + test_utils::result_assert_eq(rv, 8, "Expected to read 8 bytes")?; + + test_utils::result_assert_eq(&write_buf[..], &read_buf[..4], "First 4 bytes differ")?; + test_utils::result_assert_eq(&write_buf[..], &read_buf[4..8], "Last 4 bytes differ")?; + + Ok(()) + }) + }) +} + +fn test_write_to_read_end() -> Result<(), String> { + let mut fds = [0 as libc::c_int; 2]; + test_utils::check_system_call!(|| { unsafe { libc::pipe(fds.as_mut_ptr()) } }, &[])?; + + test_utils::result_assert(fds[0] > 0, "fds[0] not set")?; + test_utils::result_assert(fds[1] > 0, "fds[1] not set")?; + + let (read_fd, write_fd) = (fds[0], fds[1]); + + test_utils::run_and_close_fds(&[write_fd, read_fd], || { + let write_buf = [1u8, 2, 3, 4]; + + test_utils::check_system_call!( + || { + unsafe { + libc::write( + read_fd, + write_buf.as_ptr() as *const libc::c_void, + write_buf.len(), + ) + } + }, + &[libc::EBADF] + )?; + + Ok(()) + }) +} + +fn test_read_from_write_end() -> Result<(), String> { + let mut fds = [0 as libc::c_int; 2]; + test_utils::check_system_call!(|| { unsafe { libc::pipe(fds.as_mut_ptr()) } }, &[])?; + + test_utils::result_assert(fds[0] > 0, "fds[0] not set")?; + test_utils::result_assert(fds[1] > 0, "fds[1] not set")?; + + let (read_fd, write_fd) = (fds[0], fds[1]); + + test_utils::run_and_close_fds(&[write_fd, read_fd], || { + let write_buf = [1u8, 2, 3, 4]; + + let rv = test_utils::check_system_call!( + || { + unsafe { + libc::write( + write_fd, + write_buf.as_ptr() as *const libc::c_void, + write_buf.len(), + ) + } + }, + &[] + )?; + + test_utils::result_assert_eq(rv, 4, "Expected to write 4 bytes")?; + + let mut read_buf = [0u8; 4]; + + test_utils::check_system_call!( + || { + unsafe { + libc::read( + write_fd, + read_buf.as_mut_ptr() as *mut libc::c_void, + read_buf.len(), + ) + } + }, + &[libc::EBADF] + )?; + + Ok(()) + }) +}