diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index 2337b84d0ae..05854c93e1a 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -81,17 +81,6 @@ typedef enum opendal_code { OPENDAL_RANGE_NOT_SATISFIED, } opendal_code; -/** - * BlockingLister is designed to list entries at given path in a blocking - * manner. - * - * Users can construct Lister by [`BlockingOperator::lister`] or [`BlockingOperator::lister_with`]. - * - * - Lister implements `Iterator>`. - * - Lister will return `None` if there is no more entries or error has been returned. - */ -typedef struct BlockingLister BlockingLister; - /** * BlockingOperator is the entry for all public blocking APIs. * @@ -146,82 +135,6 @@ typedef struct BlockingLister BlockingLister; */ typedef struct BlockingOperator BlockingOperator; -/** - * BlockingWriter is designed to write data into given path in an blocking - * manner. - */ -typedef struct BlockingWriter BlockingWriter; - -/** - * Entry returned by [`Lister`] or [`BlockingLister`] to represent a path and it's relative metadata. - * - * # Notes - * - * Entry returned by [`Lister`] or [`BlockingLister`] may carry some already known metadata. - * Lister by default only make sure that `Mode` is fetched. To make sure the entry contains - * metadata you want, please use `list_with` or `lister_with` and `metakey`. - * - * For example: - * - * ```no_run - * # use anyhow::Result; - * use opendal::EntryMode; - * use opendal::Metakey; - * use opendal::Operator; - * # async fn test(op: Operator) -> Result<()> { - * let mut entries = op - * .list_with("dir/") - * .metakey(Metakey::ContentLength | Metakey::LastModified) - * .await?; - * for entry in entries { - * let meta = entry.metadata(); - * match meta.mode() { - * EntryMode::FILE => { - * println!( - * "Handling file {} with size {}", - * entry.path(), - * meta.content_length() - * ) - * } - * EntryMode::DIR => { - * println!("Handling dir {}", entry.path()) - * } - * EntryMode::Unknown => continue, - * } - * } - * # Ok(()) - * # } - * ``` - */ -typedef struct Entry Entry; - -typedef struct HashMap_String__String HashMap_String__String; - -/** - * Metadata carries all metadata associated with a path. - * - * # Notes - * - * mode and content_length are required metadata that all services - * should provide during `stat` operation. But in `list` operation, - * a.k.a., `Entry`'s content length could be `None`. - */ -typedef struct Metadata Metadata; - -/** - * Metadata for operator, users can use this metadata to get information of operator. - */ -typedef struct OperatorInfo OperatorInfo; - -/** - * StdReader is the adapter of [`Read`], [`Seek`] and [`BufRead`] for [`BlockingReader`][crate::BlockingReader]. - * - * Users can use this adapter in cases where they need to use [`Read`] or [`BufRead`] trait. - * - * StdReader also implements [`Send`] and [`Sync`]. - */ -typedef struct StdReader StdReader; - /** * \brief opendal_bytes carries raw-bytes with its length * @@ -275,7 +188,7 @@ typedef struct opendal_error { * @see opendal_list_entry_name() */ typedef struct opendal_entry { - struct Entry *inner; + void *inner; } opendal_entry; /** @@ -306,7 +219,7 @@ typedef struct opendal_result_lister_next { * @see opendal_operator_list() */ typedef struct opendal_lister { - struct BlockingLister *inner; + void *inner; } opendal_lister; /** @@ -325,18 +238,18 @@ typedef struct opendal_metadata { * The pointer to the opendal::Metadata in the Rust code. * Only touch this on judging whether it is NULL. */ - struct Metadata *inner; + void *inner; } opendal_metadata; /** - * \brief Used to access almost all OpenDAL APIs. It represents a + * \brief Used to access almost all OpenDAL APIs. It represents an * operator that provides the unified interfaces provided by OpenDAL. * * @see opendal_operator_new This function construct the operator * @see opendal_operator_free This function frees the heap memory of the operator * * \note The opendal_operator actually owns a pointer to - * a opendal::BlockingOperator, which is inside the Rust core code. + * an opendal::BlockingOperator, which is inside the Rust core code. * * \remark You may use the field `ptr` to check whether this is a NULL * operator. @@ -387,7 +300,7 @@ typedef struct opendal_operator_options { * The pointer to the Rust HashMap * Only touch this on judging whether it is NULL. */ - struct HashMap_String__String *inner; + void *inner; } opendal_operator_options; /** @@ -416,7 +329,7 @@ typedef struct opendal_result_read { * a opendal::BlockingReader, which is inside the Rust core code. */ typedef struct opendal_reader { - struct StdReader *inner; + void *inner; } opendal_reader; /** @@ -439,10 +352,10 @@ typedef struct opendal_result_operator_reader { /** * \brief The result type returned by opendal's writer operation. * \note The opendal_writer actually owns a pointer to - * a opendal::BlockingWriter, which is inside the Rust core code. + * an opendal::BlockingWriter, which is inside the Rust core code. */ typedef struct opendal_writer { - struct BlockingWriter *inner; + void *inner; } opendal_writer; /** @@ -523,7 +436,7 @@ typedef struct opendal_result_list { * of operator. */ typedef struct opendal_operator_info { - struct OperatorInfo *inner; + void *inner; } opendal_operator_info; /** @@ -750,7 +663,7 @@ struct opendal_result_lister_next opendal_lister_next(const struct opendal_liste /** * \brief Free the heap-allocated metadata used by opendal_lister */ -void opendal_lister_free(const struct opendal_lister *p); +void opendal_lister_free(struct opendal_lister *ptr); /** * \brief Free the heap-allocated metadata used by opendal_metadata @@ -850,7 +763,7 @@ void opendal_operator_free(const struct opendal_operator *op); * each service, especially for the **Configuration Part**. * * @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" - * @param options the pointer to the options for this operators, it could be NULL, which means no + * @param options the pointer to the options for this operator, it could be NULL, which means no * option is set * @see opendal_operator_options * @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction @@ -878,15 +791,15 @@ void opendal_operator_free(const struct opendal_operator *op); * * # Safety * - * The only unsafe case is passing a invalid c string pointer to the `scheme` argument. + * The only unsafe case is passing an invalid c string pointer to the `scheme` argument. */ struct opendal_result_operator_new opendal_operator_new(const char *scheme, const struct opendal_operator_options *options); /** - * \brief Blockingly write raw bytes to `path`. + * \brief Blocking write raw bytes to `path`. * - * Write the `bytes` into the `path` blockingly by `op_ptr`. + * Write the `bytes` into the `path` blocking by `op_ptr`. * Error is NULL if successful, otherwise it contains the error code and error message. * * \note It is important to notice that the `bytes` that is passes in will be consumed by this @@ -934,9 +847,9 @@ struct opendal_error *opendal_operator_write(const struct opendal_operator *op, struct opendal_bytes bytes); /** - * \brief Blockingly read the data from `path`. + * \brief Blocking read the data from `path`. * - * Read the data out from `path` blockingly by operator. + * Read the data out from `path` blocking by operator. * * @param op The opendal_operator created previously * @param path The path you want to read the data out @@ -977,9 +890,9 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator * const char *path); /** - * \brief Blockingly read the data from `path`. + * \brief Blocking read the data from `path`. * - * Read the data out from `path` blockingly by operator, returns + * Read the data out from `path` blocking by operator, returns * an opendal_result_read with error code. * * @param op The opendal_operator created previously @@ -1018,7 +931,7 @@ struct opendal_result_operator_reader opendal_operator_reader(const struct opend const char *path); /** - * \brief Blockingly create a writer for the specified path. + * \brief Blocking create a writer for the specified path. * * This function prepares a writer that can be used to write data to the specified path * using the provided operator. If successful, it returns a valid writer; otherwise, it @@ -1059,9 +972,9 @@ struct opendal_result_operator_writer opendal_operator_writer(const struct opend const char *path); /** - * \brief Blockingly delete the object in `path`. + * \brief Blocking delete the object in `path`. * - * Delete the object in `path` blockingly by `op_ptr`. + * Delete the object in `path` blocking by `op_ptr`. * Error is NULL if successful, otherwise it contains the error code and error message. * * @param op The opendal_operator created previously @@ -1115,7 +1028,7 @@ struct opendal_error *opendal_operator_delete(const struct opendal_operator *op, * @see opendal_result_is_exist * @see opendal_error * @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. - * However, it the operation fails, the `is_exist` will contains false and the error will be set. + * However, it the operation fails, the `is_exist` will contain false and the error will be set. * * # Example * @@ -1155,8 +1068,8 @@ struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_op * @see opendal_result_stat * @see opendal_metadata * @return Returns opendal_result_stat, containing a metadata and an opendal_error. - * If the operation succeeds, the `meta` field would holds a valid metadata and - * the `error` field should hold nullptr. Otherwise the metadata will contain a + * If the operation succeeds, the `meta` field would hold a valid metadata and + * the `error` field should hold nullptr. Otherwise, the metadata will contain a * NULL pointer, i.e. invalid, and the `error` will be set correspondingly. * * # Example @@ -1186,9 +1099,9 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator * const char *path); /** - * \brief Blockingly list the objects in `path`. + * \brief Blocking list the objects in `path`. * - * List the object in `path` blockingly by `op_ptr`, return a result with a + * List the object in `path` blocking by `op_ptr`, return a result with an * opendal_lister. Users should call opendal_lister_next() on the * lister. * @@ -1196,8 +1109,8 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator * * @param path The designated path you want to list * @see opendal_lister * @return Returns opendal_result_list, containing a lister and an opendal_error. - * If the operation succeeds, the `lister` field would holds a valid lister and - * the `error` field should hold nullptr. Otherwise the `lister`` will contain a + * If the operation succeeds, the `lister` field would hold a valid lister and + * the `error` field should hold nullptr. Otherwise, the `lister`` will contain a * NULL pointer, i.e. invalid, and the `error` will be set correspondingly. * * # Example @@ -1239,9 +1152,9 @@ struct opendal_result_list opendal_operator_list(const struct opendal_operator * const char *path); /** - * \brief Blockingly create the directory in `path`. + * \brief Blocking create the directory in `path`. * - * Create the directory in `path` blockingly by `op_ptr`. + * Create the directory in `path` blocking by `op_ptr`. * Error is NULL if successful, otherwise it contains the error code and error message. * * @param op The opendal_operator created previously @@ -1277,9 +1190,9 @@ struct opendal_error *opendal_operator_create_dir(const struct opendal_operator const char *path); /** - * \brief Blockingly rename the object in `path`. + * \brief Blocking rename the object in `path`. * - * Rename the object in `src` to `dest` blockingly by `op`. + * Rename the object in `src` to `dest` blocking by `op`. * Error is NULL if successful, otherwise it contains the error code and error message. * * @param op The opendal_operator created previously @@ -1324,9 +1237,9 @@ struct opendal_error *opendal_operator_rename(const struct opendal_operator *op, const char *dest); /** - * \brief Blockingly copy the object in `path`. + * \brief Blocking copy the object in `path`. * - * Copy the object in `src` to `dest` blockingly by `op`. + * Copy the object in `src` to `dest` blocking by `op`. * Error is NULL if successful, otherwise it contains the error code and error message. * * @param op The opendal_operator created previously @@ -1468,7 +1381,7 @@ void opendal_operator_options_set(struct opendal_operator_options *self, /** * \brief Free the allocated memory used by [`opendal_operator_options`] */ -void opendal_operator_options_free(const struct opendal_operator_options *options); +void opendal_operator_options_free(struct opendal_operator_options *ptr); /** * \brief Path of entry. @@ -1498,7 +1411,7 @@ void opendal_entry_free(struct opendal_entry *ptr); /** * \brief Read data from the reader. */ -struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *reader, +struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *self, uint8_t *buf, uintptr_t len); @@ -1510,7 +1423,7 @@ void opendal_reader_free(struct opendal_reader *ptr); /** * \brief Write data to the writer. */ -struct opendal_result_writer_write opendal_writer_write(const struct opendal_writer *writer, +struct opendal_result_writer_write opendal_writer_write(const struct opendal_writer *self, struct opendal_bytes bytes); /** diff --git a/bindings/c/src/entry.rs b/bindings/c/src/entry.rs index f71b6533774..f4c62792e08 100644 --- a/bindings/c/src/entry.rs +++ b/bindings/c/src/entry.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use std::ffi::CString; +use std::ffi::{c_void, CString}; use std::os::raw::c_char; use ::opendal as core; @@ -28,14 +28,22 @@ use ::opendal as core; /// @see opendal_list_entry_name() #[repr(C)] pub struct opendal_entry { - inner: *mut core::Entry, + inner: *mut c_void, +} + +impl opendal_entry { + fn deref(&self) -> &core::Entry { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &*(self.inner as *mut core::Entry) } + } } impl opendal_entry { /// Used to convert the Rust type into C type pub(crate) fn new(entry: core::Entry) -> Self { Self { - inner: Box::into_raw(Box::new(entry)), + inner: Box::into_raw(Box::new(entry)) as _, } } @@ -46,7 +54,7 @@ impl opendal_entry { /// \note To free the string, you can directly call free() #[no_mangle] pub unsafe extern "C" fn opendal_entry_path(&self) -> *mut c_char { - let s = (*self.inner).path(); + let s = self.deref().path(); let c_str = CString::new(s).unwrap(); c_str.into_raw() } @@ -60,7 +68,7 @@ impl opendal_entry { /// \note To free the string, you can directly call free() #[no_mangle] pub unsafe extern "C" fn opendal_entry_name(&self) -> *mut c_char { - let s = (*self.inner).name(); + let s = self.deref().name(); let c_str = CString::new(s).unwrap(); c_str.into_raw() } @@ -69,8 +77,8 @@ impl opendal_entry { #[no_mangle] pub unsafe extern "C" fn opendal_entry_free(ptr: *mut opendal_entry) { if !ptr.is_null() { - let _ = unsafe { Box::from_raw((*ptr).inner) }; - let _ = unsafe { Box::from_raw(ptr) }; + let _ = Box::from_raw((*ptr).inner as *mut core::Entry); + let _ = Box::from_raw(ptr); } } } diff --git a/bindings/c/src/lister.rs b/bindings/c/src/lister.rs index 08f787480e3..3376a011dd3 100644 --- a/bindings/c/src/lister.rs +++ b/bindings/c/src/lister.rs @@ -16,6 +16,7 @@ // under the License. use ::opendal as core; +use std::ffi::c_void; use super::*; @@ -28,13 +29,21 @@ use super::*; /// @see opendal_operator_list() #[repr(C)] pub struct opendal_lister { - inner: *mut core::BlockingLister, + inner: *mut c_void, +} + +impl opendal_lister { + fn deref_mut(&self) -> &mut core::BlockingLister { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &mut *(self.inner as *mut core::BlockingLister) } + } } impl opendal_lister { pub(crate) fn new(lister: core::BlockingLister) -> Self { Self { - inner: Box::into_raw(Box::new(lister)), + inner: Box::into_raw(Box::new(lister)) as _, } } @@ -47,7 +56,7 @@ impl opendal_lister { /// @see opendal_operator_list() #[no_mangle] pub unsafe extern "C" fn opendal_lister_next(&self) -> opendal_result_lister_next { - let e = (*self.inner).next(); + let e = self.deref_mut().next(); if e.is_none() { return opendal_result_lister_next { entry: std::ptr::null_mut(), @@ -72,10 +81,10 @@ impl opendal_lister { /// \brief Free the heap-allocated metadata used by opendal_lister #[no_mangle] - pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_lister) { - unsafe { - let _ = Box::from_raw((*p).inner); - let _ = Box::from_raw(p as *mut opendal_lister); + pub unsafe extern "C" fn opendal_lister_free(ptr: *mut opendal_lister) { + if !ptr.is_null() { + let _ = Box::from_raw((*ptr).inner as *mut core::BlockingLister); + let _ = Box::from_raw(ptr); } } } diff --git a/bindings/c/src/metadata.rs b/bindings/c/src/metadata.rs index b413759d3cf..60a3c42a3f7 100644 --- a/bindings/c/src/metadata.rs +++ b/bindings/c/src/metadata.rs @@ -16,6 +16,7 @@ // under the License. use ::opendal as core; +use std::ffi::c_void; /// \brief Carries all metadata associated with a **path**. /// @@ -30,7 +31,15 @@ use ::opendal as core; pub struct opendal_metadata { /// The pointer to the opendal::Metadata in the Rust code. /// Only touch this on judging whether it is NULL. - pub inner: *mut core::Metadata, + inner: *mut c_void, +} + +impl opendal_metadata { + fn deref(&self) -> &core::Metadata { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &*(self.inner as *mut core::Metadata) } + } } impl opendal_metadata { @@ -38,15 +47,15 @@ impl opendal_metadata { /// [`opendal_metadata`] pub(crate) fn new(m: core::Metadata) -> Self { Self { - inner: Box::into_raw(Box::new(m)), + inner: Box::into_raw(Box::new(m)) as _, } } /// \brief Free the heap-allocated metadata used by opendal_metadata #[no_mangle] pub unsafe extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { - unsafe { - let _ = Box::from_raw((*ptr).inner); + if !ptr.is_null() { + let _ = Box::from_raw((*ptr).inner as *mut core::Metadata); let _ = Box::from_raw(ptr); } } @@ -64,9 +73,7 @@ impl opendal_metadata { /// ``` #[no_mangle] pub extern "C" fn opendal_metadata_content_length(&self) -> u64 { - // Safety: the inner should never be null once constructed - // The use-after-free is undefined behavior - unsafe { (*self.inner).content_length() } + self.deref().content_length() } /// \brief Return whether the path represents a file @@ -82,11 +89,7 @@ impl opendal_metadata { /// ``` #[no_mangle] pub extern "C" fn opendal_metadata_is_file(&self) -> bool { - // Safety: the inner should never be null once constructed - // The use-after-free is undefined behavior - let m = unsafe { &*self.inner }; - - m.is_file() + self.deref().is_file() } /// \brief Return whether the path represents a directory @@ -107,9 +110,7 @@ impl opendal_metadata { /// after we support opendal_operator_mkdir() #[no_mangle] pub extern "C" fn opendal_metadata_is_dir(&self) -> bool { - // Safety: the inner should never be null once constructed - // The use-after-free is undefined behavior - unsafe { (*self.inner).is_dir() } + self.deref().is_dir() } /// \brief Return the last_modified of the metadata, in milliseconds @@ -125,7 +126,7 @@ impl opendal_metadata { /// ``` #[no_mangle] pub extern "C" fn opendal_metadata_last_modified_ms(&self) -> i64 { - let mtime = unsafe { (*self.inner).last_modified() }; + let mtime = self.deref().last_modified(); match mtime { None => -1, Some(time) => time.timestamp_millis(), diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs index ebfca45fdcc..b3e070af3e3 100644 --- a/bindings/c/src/operator.rs +++ b/bindings/c/src/operator.rs @@ -31,14 +31,14 @@ static RUNTIME: Lazy = Lazy::new(|| { .unwrap() }); -/// \brief Used to access almost all OpenDAL APIs. It represents a +/// \brief Used to access almost all OpenDAL APIs. It represents an /// operator that provides the unified interfaces provided by OpenDAL. /// /// @see opendal_operator_new This function construct the operator /// @see opendal_operator_free This function frees the heap memory of the operator /// /// \note The opendal_operator actually owns a pointer to -/// a opendal::BlockingOperator, which is inside the Rust core code. +/// an opendal::BlockingOperator, which is inside the Rust core code. /// /// \remark You may use the field `ptr` to check whether this is a NULL /// operator. @@ -124,7 +124,7 @@ fn build_operator( /// each service, especially for the **Configuration Part**. /// /// @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" -/// @param options the pointer to the options for this operators, it could be NULL, which means no +/// @param options the pointer to the options for this operator, it could be NULL, which means no /// option is set /// @see opendal_operator_options /// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction @@ -152,7 +152,7 @@ fn build_operator( /// /// # Safety /// -/// The only unsafe case is passing a invalid c string pointer to the `scheme` argument. +/// The only unsafe case is passing an invalid c string pointer to the `scheme` argument. #[no_mangle] pub unsafe extern "C" fn opendal_operator_new( scheme: *const c_char, @@ -175,7 +175,7 @@ pub unsafe extern "C" fn opendal_operator_new( let mut map = HashMap::::default(); if !options.is_null() { - for (k, v) in (*options).as_ref() { + for (k, v) in (*options).deref() { map.insert(k.to_string(), v.to_string()); } } @@ -195,9 +195,9 @@ pub unsafe extern "C" fn opendal_operator_new( } } -/// \brief Blockingly write raw bytes to `path`. +/// \brief Blocking write raw bytes to `path`. /// -/// Write the `bytes` into the `path` blockingly by `op_ptr`. +/// Write the `bytes` into the `path` blocking by `op_ptr`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// /// \note It is important to notice that the `bytes` that is passes in will be consumed by this @@ -257,9 +257,9 @@ pub unsafe extern "C" fn opendal_operator_write( } } -/// \brief Blockingly read the data from `path`. +/// \brief Blocking read the data from `path`. /// -/// Read the data out from `path` blockingly by operator. +/// Read the data out from `path` blocking by operator. /// /// @param op The opendal_operator created previously /// @param path The path you want to read the data out @@ -322,9 +322,9 @@ pub unsafe extern "C" fn opendal_operator_read( } } -/// \brief Blockingly read the data from `path`. +/// \brief Blocking read the data from `path`. /// -/// Read the data out from `path` blockingly by operator, returns +/// Read the data out from `path` blocking by operator, returns /// an opendal_result_read with error code. /// /// @param op The opendal_operator created previously @@ -391,7 +391,7 @@ pub unsafe extern "C" fn opendal_operator_reader( } } -/// \brief Blockingly create a writer for the specified path. +/// \brief Blocking create a writer for the specified path. /// /// This function prepares a writer that can be used to write data to the specified path /// using the provided operator. If successful, it returns a valid writer; otherwise, it @@ -454,9 +454,9 @@ pub unsafe extern "C" fn opendal_operator_writer( } } -/// \brief Blockingly delete the object in `path`. +/// \brief Blocking delete the object in `path`. /// -/// Delete the object in `path` blockingly by `op_ptr`. +/// Delete the object in `path` blocking by `op_ptr`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// /// @param op The opendal_operator created previously @@ -523,7 +523,7 @@ pub unsafe extern "C" fn opendal_operator_delete( /// @see opendal_result_is_exist /// @see opendal_error /// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. -/// However, it the operation fails, the `is_exist` will contains false and the error will be set. +/// However, it the operation fails, the `is_exist` will contain false and the error will be set. /// /// # Example /// @@ -581,8 +581,8 @@ pub unsafe extern "C" fn opendal_operator_is_exist( /// @see opendal_result_stat /// @see opendal_metadata /// @return Returns opendal_result_stat, containing a metadata and an opendal_error. -/// If the operation succeeds, the `meta` field would holds a valid metadata and -/// the `error` field should hold nullptr. Otherwise the metadata will contain a +/// If the operation succeeds, the `meta` field would hold a valid metadata and +/// the `error` field should hold nullptr. Otherwise, the metadata will contain a /// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. /// /// # Example @@ -630,9 +630,9 @@ pub unsafe extern "C" fn opendal_operator_stat( } } -/// \brief Blockingly list the objects in `path`. +/// \brief Blocking list the objects in `path`. /// -/// List the object in `path` blockingly by `op_ptr`, return a result with a +/// List the object in `path` blocking by `op_ptr`, return a result with an /// opendal_lister. Users should call opendal_lister_next() on the /// lister. /// @@ -640,8 +640,8 @@ pub unsafe extern "C" fn opendal_operator_stat( /// @param path The designated path you want to list /// @see opendal_lister /// @return Returns opendal_result_list, containing a lister and an opendal_error. -/// If the operation succeeds, the `lister` field would holds a valid lister and -/// the `error` field should hold nullptr. Otherwise the `lister`` will contain a +/// If the operation succeeds, the `lister` field would hold a valid lister and +/// the `error` field should hold nullptr. Otherwise, the `lister`` will contain a /// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. /// /// # Example @@ -701,9 +701,9 @@ pub unsafe extern "C" fn opendal_operator_list( } } -/// \brief Blockingly create the directory in `path`. +/// \brief Blocking create the directory in `path`. /// -/// Create the directory in `path` blockingly by `op_ptr`. +/// Create the directory in `path` blocking by `op_ptr`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// /// @param op The opendal_operator created previously @@ -751,9 +751,9 @@ pub unsafe extern "C" fn opendal_operator_create_dir( } } -/// \brief Blockingly rename the object in `path`. +/// \brief Blocking rename the object in `path`. /// -/// Rename the object in `src` to `dest` blockingly by `op`. +/// Rename the object in `src` to `dest` blocking by `op`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// /// @param op The opendal_operator created previously @@ -814,9 +814,9 @@ pub unsafe extern "C" fn opendal_operator_rename( } } -/// \brief Blockingly copy the object in `path`. +/// \brief Blocking copy the object in `path`. /// -/// Copy the object in `src` to `dest` blockingly by `op`. +/// Copy the object in `src` to `dest` blocking by `op`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// /// @param op The opendal_operator created previously diff --git a/bindings/c/src/operator_info.rs b/bindings/c/src/operator_info.rs index c67092ad761..36efe0c8b4e 100644 --- a/bindings/c/src/operator_info.rs +++ b/bindings/c/src/operator_info.rs @@ -15,8 +15,8 @@ // specific language governing permissions and limitations // under the License. -use std::ffi::c_char; use std::ffi::CString; +use std::ffi::{c_char, c_void}; use ::opendal as core; @@ -26,7 +26,15 @@ use crate::opendal_operator; /// of operator. #[repr(C)] pub struct opendal_operator_info { - pub inner: *mut core::OperatorInfo, + inner: *mut c_void, +} + +impl opendal_operator_info { + fn deref(&self) -> &core::OperatorInfo { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &*(self.inner as *mut core::OperatorInfo) } + } } /// \brief Capability is used to describe what operations are supported @@ -158,15 +166,15 @@ impl opendal_operator_info { let info = op.info(); Box::into_raw(Box::new(Self { - inner: Box::into_raw(Box::new(info)), + inner: Box::into_raw(Box::new(info)) as _, })) } /// \brief Free the heap-allocated opendal_operator_info #[no_mangle] pub unsafe extern "C" fn opendal_operator_info_free(ptr: *mut Self) { - unsafe { - let _ = Box::from_raw((*ptr).inner); + if !ptr.is_null() { + let _ = Box::from_raw((*ptr).inner as *mut core::OperatorInfo); let _ = Box::from_raw(ptr); } } @@ -176,7 +184,7 @@ impl opendal_operator_info { /// \note: The string is on heap, remember to free it #[no_mangle] pub unsafe extern "C" fn opendal_operator_info_get_scheme(&self) -> *mut c_char { - let scheme = (*self.inner).scheme().to_string(); + let scheme = self.deref().scheme().to_string(); CString::new(scheme) .expect("CString::new failed in opendal_operator_info_get_root") .into_raw() @@ -187,7 +195,7 @@ impl opendal_operator_info { /// \note: The string is on heap, remember to free it #[no_mangle] pub unsafe extern "C" fn opendal_operator_info_get_root(&self) -> *mut c_char { - let root = (*self.inner).root(); + let root = self.deref().root(); CString::new(root) .expect("CString::new failed in opendal_operator_info_get_root") .into_raw() @@ -199,7 +207,7 @@ impl opendal_operator_info { /// \note: The string is on heap, remember to free it #[no_mangle] pub unsafe extern "C" fn opendal_operator_info_get_name(&self) -> *mut c_char { - let name = (*self.inner).name(); + let name = self.deref().name(); CString::new(name) .expect("CString::new failed in opendal_operator_info_get_name") .into_raw() @@ -210,7 +218,7 @@ impl opendal_operator_info { pub unsafe extern "C" fn opendal_operator_info_get_full_capability( &self, ) -> opendal_capability { - let cap = (*self.inner).full_capability(); + let cap = self.deref().full_capability(); cap.into() } @@ -219,7 +227,7 @@ impl opendal_operator_info { pub unsafe extern "C" fn opendal_operator_info_get_native_capability( &self, ) -> opendal_capability { - let cap = (*self.inner).native_capability(); + let cap = self.deref().native_capability(); cap.into() } } diff --git a/bindings/c/src/reader.rs b/bindings/c/src/reader.rs index 40007f99901..5d15fcba0a8 100644 --- a/bindings/c/src/reader.rs +++ b/bindings/c/src/reader.rs @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +use std::ffi::c_void; use std::io::Read; use ::opendal as core; @@ -27,32 +28,37 @@ use super::*; /// a opendal::BlockingReader, which is inside the Rust core code. #[repr(C)] pub struct opendal_reader { - inner: *mut core::StdReader, + inner: *mut c_void, +} + +impl opendal_reader { + fn deref_mut(&self) -> &mut core::StdReader { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &mut *(self.inner as *mut core::StdReader) } + } } impl opendal_reader { pub(crate) fn new(reader: core::StdReader) -> Self { Self { - inner: Box::into_raw(Box::new(reader)), + inner: Box::into_raw(Box::new(reader)) as _, } } /// \brief Read data from the reader. #[no_mangle] pub unsafe extern "C" fn opendal_reader_read( - reader: *const Self, + &self, buf: *mut u8, len: usize, ) -> opendal_result_reader_read { if buf.is_null() { - panic!("The buffer given is pointing at NULL"); + panic!("buf is NULL"); } let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) }; - - let inner = unsafe { &mut *(*reader).inner }; - let n = inner.read(buf); - match n { + match self.deref_mut().read(buf) { Ok(n) => opendal_result_reader_read { size: n, error: std::ptr::null_mut(), @@ -71,8 +77,8 @@ impl opendal_reader { #[no_mangle] pub unsafe extern "C" fn opendal_reader_free(ptr: *mut opendal_reader) { if !ptr.is_null() { - let _ = unsafe { Box::from_raw((*ptr).inner) }; - let _ = unsafe { Box::from_raw(ptr) }; + let _ = Box::from_raw((*ptr).inner as *mut core::StdReader); + let _ = Box::from_raw(ptr); } } } diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index 26d7f29ffb9..932f63b6275 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -16,6 +16,7 @@ // under the License. use std::collections::HashMap; +use std::ffi::c_void; use std::os::raw::c_char; use opendal::Buffer; @@ -78,7 +79,21 @@ impl Into for opendal_bytes { pub struct opendal_operator_options { /// The pointer to the Rust HashMap /// Only touch this on judging whether it is NULL. - inner: *mut HashMap, + inner: *mut c_void, +} + +impl opendal_operator_options { + pub(crate) fn deref(&self) -> &HashMap { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &*(self.inner as *mut HashMap) } + } + + pub(crate) fn deref_mut(&mut self) -> &mut HashMap { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &mut *(self.inner as *mut HashMap) } + } } impl opendal_operator_options { @@ -92,9 +107,8 @@ impl opendal_operator_options { pub extern "C" fn opendal_operator_options_new() -> *mut Self { let map: HashMap = HashMap::default(); let options = Self { - inner: Box::into_raw(Box::new(map)), + inner: Box::into_raw(Box::new(map)) as _, }; - Box::into_raw(Box::new(options)) } @@ -129,20 +143,15 @@ impl opendal_operator_options { .to_str() .unwrap() .to_string(); - (*self.inner).insert(k, v); - } - - /// Returns a reference to the underlying [`HashMap`] - pub(crate) fn as_ref(&self) -> &HashMap { - unsafe { &*(self.inner) } + self.deref_mut().insert(k, v); } /// \brief Free the allocated memory used by [`opendal_operator_options`] #[no_mangle] - pub unsafe extern "C" fn opendal_operator_options_free( - options: *const opendal_operator_options, - ) { - let _ = unsafe { Box::from_raw((*options).inner) }; - let _ = unsafe { Box::from_raw(options as *mut opendal_operator_options) }; + pub unsafe extern "C" fn opendal_operator_options_free(ptr: *mut opendal_operator_options) { + if !ptr.is_null() { + let _ = Box::from_raw((*ptr).inner as *mut HashMap); + let _ = Box::from_raw(ptr); + } } } diff --git a/bindings/c/src/writer.rs b/bindings/c/src/writer.rs index 5113e55c753..36cc5787909 100644 --- a/bindings/c/src/writer.rs +++ b/bindings/c/src/writer.rs @@ -16,33 +16,41 @@ // under the License. use ::opendal as core; +use std::ffi::c_void; use super::*; /// \brief The result type returned by opendal's writer operation. /// \note The opendal_writer actually owns a pointer to -/// a opendal::BlockingWriter, which is inside the Rust core code. +/// an opendal::BlockingWriter, which is inside the Rust core code. #[repr(C)] pub struct opendal_writer { - inner: *mut core::BlockingWriter, + inner: *mut c_void, +} + +impl opendal_writer { + fn deref_mut(&self) -> &mut core::BlockingWriter { + // Safety: the inner should never be null once constructed + // The use-after-free is undefined behavior + unsafe { &mut *(self.inner as *mut core::BlockingWriter) } + } } impl opendal_writer { pub(crate) fn new(writer: core::BlockingWriter) -> Self { Self { - inner: Box::into_raw(Box::new(writer)), + inner: Box::into_raw(Box::new(writer)) as _, } } /// \brief Write data to the writer. #[no_mangle] pub unsafe extern "C" fn opendal_writer_write( - writer: *const Self, + &self, bytes: opendal_bytes, ) -> opendal_result_writer_write { - let inner = unsafe { &mut *(*writer).inner }; let size = bytes.len; - match inner.write(bytes) { + match self.deref_mut().write(bytes) { Ok(()) => opendal_result_writer_write { size, error: std::ptr::null_mut(), @@ -62,8 +70,8 @@ impl opendal_writer { #[no_mangle] pub unsafe extern "C" fn opendal_writer_free(ptr: *mut opendal_writer) { if !ptr.is_null() { - let mut w = unsafe { Box::from_raw((*ptr).inner) }; - let _ = w.close(); + let _ = (&*ptr).deref_mut().close(); + let _ = unsafe { Box::from_raw((*ptr).inner as *mut core::BlockingWriter) }; let _ = unsafe { Box::from_raw(ptr) }; } }