From: Felix Krull Date: Tue, 25 Aug 2020 22:45:57 +0000 (+0200) Subject: src: add CommitSizesEntry X-Git-Tag: archive/raspbian/2022.4-1+rpi1^2~9^2^2~20^2~104 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=5b1bc5041890954b4b779bf873b29a981e884456;p=ostree.git src: add CommitSizesEntry --- diff --git a/rust-bindings/rust/conf/ostree.toml b/rust-bindings/rust/conf/ostree.toml index 8b905fcd..ae0a5710 100644 --- a/rust-bindings/rust/conf/ostree.toml +++ b/rust-bindings/rust/conf/ostree.toml @@ -14,6 +14,7 @@ generate = [ "OSTree.AsyncProgress", "OSTree.BootconfigParser", "OSTree.ChecksumFlags", + "OSTree.CommitSizesEntry", "OSTree.Deployment", "OSTree.DeploymentUnlockedState", "OSTree.DiffFlags", diff --git a/rust-bindings/rust/src/auto/commit_sizes_entry.rs b/rust-bindings/rust/src/auto/commit_sizes_entry.rs new file mode 100644 index 00000000..0dc86912 --- /dev/null +++ b/rust-bindings/rust/src/auto/commit_sizes_entry.rs @@ -0,0 +1,29 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// DO NOT EDIT + +#[cfg(any(feature = "v2020_1", feature = "dox"))] +use glib::translate::*; +use ostree_sys; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +use ObjectType; + +glib_wrapper! { + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct CommitSizesEntry(Boxed); + + match fn { + copy => |ptr| ostree_sys::ostree_commit_sizes_entry_copy(mut_override(ptr)), + free => |ptr| ostree_sys::ostree_commit_sizes_entry_free(ptr), + get_type => || ostree_sys::ostree_commit_sizes_entry_get_type(), + } +} + +impl CommitSizesEntry { + #[cfg(any(feature = "v2020_1", feature = "dox"))] + pub fn new(checksum: &str, objtype: ObjectType, unpacked: u64, archived: u64) -> Option { + unsafe { + from_glib_full(ostree_sys::ostree_commit_sizes_entry_new(checksum.to_glib_none().0, objtype.to_glib(), unpacked, archived)) + } + } +} diff --git a/rust-bindings/rust/src/auto/functions.rs b/rust-bindings/rust/src/auto/functions.rs index 82e398b6..8749b57b 100644 --- a/rust-bindings/rust/src/auto/functions.rs +++ b/rust-bindings/rust/src/auto/functions.rs @@ -10,6 +10,8 @@ use glib::GString; use ostree_sys; use std::mem; use std::ptr; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +use CommitSizesEntry; use DiffFlags; use DiffItem; use ObjectType; @@ -105,10 +107,15 @@ pub fn commit_get_content_checksum(commit_variant: &glib::Variant) -> Option) -> Result<(), glib::Error> { -// unsafe { TODO: call ostree_sys:ostree_commit_get_object_sizes() } -//} +#[cfg(any(feature = "v2020_1", feature = "dox"))] +pub fn commit_get_object_sizes(commit_variant: &glib::Variant) -> Result, glib::Error> { + unsafe { + let mut out_sizes_entries = ptr::null_mut(); + let mut error = ptr::null_mut(); + let _ = ostree_sys::ostree_commit_get_object_sizes(commit_variant.to_glib_none().0, &mut out_sizes_entries, &mut error); + if error.is_null() { Ok(FromGlibPtrContainer::from_glib_container(out_sizes_entries)) } else { Err(from_glib_full(error)) } + } +} pub fn commit_get_parent(commit_variant: &glib::Variant) -> Option { unsafe { diff --git a/rust-bindings/rust/src/auto/mod.rs b/rust-bindings/rust/src/auto/mod.rs index 468b873b..e6494dca 100644 --- a/rust-bindings/rust/src/auto/mod.rs +++ b/rust-bindings/rust/src/auto/mod.rs @@ -80,6 +80,11 @@ mod collection_ref; #[cfg(any(feature = "v2018_6", feature = "dox"))] pub use self::collection_ref::CollectionRef; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +mod commit_sizes_entry; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +pub use self::commit_sizes_entry::CommitSizesEntry; + mod diff_item; pub use self::diff_item::DiffItem; diff --git a/rust-bindings/rust/src/auto/versions.txt b/rust-bindings/rust/src/auto/versions.txt index 85a1b25d..557e5d3e 100644 --- a/rust-bindings/rust/src/auto/versions.txt +++ b/rust-bindings/rust/src/auto/versions.txt @@ -1,2 +1,2 @@ Generated by gir (https://github.com/gtk-rs/gir @ 2d1ffab1) -from gir-files (https://github.com/gtk-rs/gir-files @ eec42a9) +from gir-files (https://github.com/gtk-rs/gir-files @ ff904f0) diff --git a/rust-bindings/rust/src/checksum.rs b/rust-bindings/rust/src/checksum.rs index 097438b9..1058f718 100644 --- a/rust-bindings/rust/src/checksum.rs +++ b/rust-bindings/rust/src/checksum.rs @@ -1,9 +1,10 @@ -use glib::translate::{from_glib_full, FromGlibPtrFull}; -use glib::GString; +use glib::{ + translate::{from_glib_full, FromGlibPtrFull, FromGlibPtrNone}, + GString, +}; use glib_sys::{g_free, g_malloc, g_malloc0, gpointer}; use libc::c_char; -use std::fmt; -use std::ptr::copy_nonoverlapping; +use std::{fmt, ptr::copy_nonoverlapping}; const BYTES_LEN: usize = ostree_sys::OSTREE_SHA256_DIGEST_LEN as usize; const HEX_LEN: usize = ostree_sys::OSTREE_SHA256_STRING_LEN as usize; @@ -16,6 +17,8 @@ pub struct Checksum { } impl Checksum { + pub const DIGEST_LEN: usize = BYTES_LEN; + /// Create a `Checksum` value, taking ownership of the given memory location. /// /// # Safety @@ -23,11 +26,20 @@ impl Checksum { /// `g_free` (this is e.g. the case if the memory was allocated with `g_malloc`). The value /// takes ownership of the memory, i.e. the memory is freed when the value is dropped. The /// memory must not be freed by other code. - unsafe fn new(bytes: *mut [u8; BYTES_LEN]) -> Checksum { + unsafe fn new(bytes: *mut [u8; Self::DIGEST_LEN]) -> Checksum { assert!(!bytes.is_null()); Checksum { bytes } } + /// Create a `Checksum` from a byte array. + pub fn from_bytes(checksum: &[u8; Self::DIGEST_LEN]) -> Checksum { + let ptr = checksum as *const [u8; BYTES_LEN] as *mut [u8; BYTES_LEN]; + unsafe { + // Safety: we know this byte array is long enough. + Checksum::from_glib_none(ptr) + } + } + /// Create a `Checksum` from a hexadecimal SHA256 string. /// /// Unfortunately, the underlying libostree function has no way to report parsing errors. If the @@ -93,12 +105,7 @@ impl Drop for Checksum { impl Clone for Checksum { fn clone(&self) -> Self { - unsafe { - let cloned = g_malloc(BYTES_LEN) as *mut [u8; BYTES_LEN]; - // copy one array of 32 elements - copy_nonoverlapping::<[u8; BYTES_LEN]>(self.bytes, cloned, 1); - Checksum::new(cloned) - } + unsafe { Checksum::from_glib_none(self.bytes) } } } @@ -140,6 +147,15 @@ impl FromGlibPtrFull<*mut u8> for Checksum { } } +impl FromGlibPtrNone<*mut [u8; BYTES_LEN]> for Checksum { + unsafe fn from_glib_none(ptr: *mut [u8; BYTES_LEN]) -> Self { + let cloned = g_malloc(BYTES_LEN) as *mut [u8; BYTES_LEN]; + // copy one array of 32 elements + copy_nonoverlapping::<[u8; BYTES_LEN]>(ptr, cloned, 1); + Checksum::new(cloned) + } +} + #[cfg(test)] mod tests { use super::*; @@ -156,6 +172,13 @@ mod tests { assert_eq!(checksum.to_string(), "00".repeat(BYTES_LEN)); } + #[test] + fn should_create_checksum_from_bytes_copy() { + let bytes = [0u8; BYTES_LEN]; + let checksum = Checksum::from_bytes(&bytes); + assert_eq!(checksum.to_string(), "00".repeat(BYTES_LEN)); + } + #[test] fn should_parse_checksum_string_to_bytes() { let csum = Checksum::from_hex(CHECKSUM_STRING); diff --git a/rust-bindings/rust/src/commit_sizes_entry.rs b/rust-bindings/rust/src/commit_sizes_entry.rs new file mode 100644 index 00000000..d96b4f0f --- /dev/null +++ b/rust-bindings/rust/src/commit_sizes_entry.rs @@ -0,0 +1,48 @@ +use crate::{auto::CommitSizesEntry, auto::ObjectType}; +use glib::{ + translate::{FromGlib, FromGlibPtrNone, ToGlibPtr}, + GString, +}; + +impl CommitSizesEntry { + /// Object checksum as hex string. + pub fn checksum(&self) -> GString { + let underlying = self.to_glib_none(); + unsafe { GString::from_glib_none((*underlying.0).checksum) } + } + + /// The object type. + pub fn objtype(&self) -> ObjectType { + let underlying = self.to_glib_none(); + unsafe { ObjectType::from_glib((*underlying.0).objtype) } + } + + /// Unpacked object size. + pub fn unpacked(&self) -> u64 { + let underlying = self.to_glib_none(); + unsafe { (*underlying.0).unpacked } + } + + /// Compressed object size. + pub fn archived(&self) -> u64 { + let underlying = self.to_glib_none(); + unsafe { (*underlying.0).archived } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const CHECKSUM_STRING: &str = + "bf875306783efdc5bcab37ea10b6ca4e9b6aea8b94580d0ca94af120565c0e8a"; + + #[test] + fn should_get_values_from_commit_sizes_entry() { + let entry = CommitSizesEntry::new(CHECKSUM_STRING, ObjectType::Commit, 15, 16).unwrap(); + assert_eq!(entry.checksum(), CHECKSUM_STRING); + assert_eq!(entry.objtype(), ObjectType::Commit); + assert_eq!(entry.unpacked(), 15); + assert_eq!(entry.archived(), 16); + } +} diff --git a/rust-bindings/rust/src/lib.rs b/rust-bindings/rust/src/lib.rs index 6046c7c3..64f435cc 100644 --- a/rust-bindings/rust/src/lib.rs +++ b/rust-bindings/rust/src/lib.rs @@ -29,33 +29,30 @@ pub use crate::auto::*; // handwritten code mod checksum; pub use crate::checksum::*; - #[cfg(any(feature = "v2018_6", feature = "dox"))] mod collection_ref; #[cfg(any(feature = "v2018_6", feature = "dox"))] pub use crate::collection_ref::*; - mod functions; pub use crate::functions::*; - #[cfg(any(feature = "v2019_3", feature = "dox"))] mod kernel_args; #[cfg(any(feature = "v2019_3", feature = "dox"))] pub use crate::kernel_args::*; - mod object_name; pub use crate::object_name::*; - mod repo; pub use crate::repo::*; - #[cfg(any(feature = "v2016_8", feature = "dox"))] mod repo_checkout_at_options; #[cfg(any(feature = "v2016_8", feature = "dox"))] pub use crate::repo_checkout_at_options::*; - mod se_policy; pub use crate::se_policy::*; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +mod commit_sizes_entry; +#[cfg(any(feature = "v2020_1", feature = "dox"))] +pub use crate::commit_sizes_entry::*; // tests #[cfg(test)] diff --git a/rust-bindings/rust/src/repo_checkout_at_options.rs b/rust-bindings/rust/src/repo_checkout_at_options.rs deleted file mode 100644 index 7d92e546..00000000 --- a/rust-bindings/rust/src/repo_checkout_at_options.rs +++ /dev/null @@ -1,263 +0,0 @@ -use crate::{RepoCheckoutMode, RepoCheckoutOverwriteMode, RepoDevInoCache, SePolicy}; -use glib::translate::*; -use libc::c_char; -use ostree_sys::*; -use std::path::PathBuf; - -#[cfg(any(feature = "v2018_2", feature = "dox"))] -mod repo_checkout_filter; -#[cfg(any(feature = "v2018_2", feature = "dox"))] -pub use self::repo_checkout_filter::RepoCheckoutFilter; - -pub struct RepoCheckoutAtOptions { - pub mode: RepoCheckoutMode, - pub overwrite_mode: RepoCheckoutOverwriteMode, - pub enable_uncompressed_cache: bool, - pub enable_fsync: bool, - pub process_whiteouts: bool, - pub no_copy_fallback: bool, - #[cfg(any(feature = "v2017_6", feature = "dox"))] - pub force_copy: bool, - #[cfg(any(feature = "v2017_7", feature = "dox"))] - pub bareuseronly_dirs: bool, - #[cfg(any(feature = "v2018_9", feature = "dox"))] - pub force_copy_zerosized: bool, - pub subpath: Option, - pub devino_to_csum_cache: Option, - /// A callback function to decide which files and directories will be checked out from the - /// repo. See the documentation on [RepoCheckoutFilter](struct.RepoCheckoutFilter.html) for more - /// information on the signature. - /// - /// # Panics - /// This callback may not panic. If it does, `abort()` will be called to avoid unwinding across - /// an FFI boundary and into the libostree C code (which is Undefined Behavior). If you prefer to - /// swallow the panic rather than aborting, you can use `std::panic::catch_unwind` inside your - /// callback to catch and silence any panics that occur. - #[cfg(any(feature = "v2018_2", feature = "dox"))] - pub filter: Option, - #[cfg(any(feature = "v2017_6", feature = "dox"))] - pub sepolicy: Option, - pub sepolicy_prefix: Option, -} - -impl Default for RepoCheckoutAtOptions { - fn default() -> Self { - RepoCheckoutAtOptions { - mode: RepoCheckoutMode::None, - overwrite_mode: RepoCheckoutOverwriteMode::None, - enable_uncompressed_cache: false, - enable_fsync: false, - process_whiteouts: false, - no_copy_fallback: false, - #[cfg(feature = "v2017_6")] - force_copy: false, - #[cfg(feature = "v2017_7")] - bareuseronly_dirs: false, - #[cfg(feature = "v2018_9")] - force_copy_zerosized: false, - subpath: None, - devino_to_csum_cache: None, - #[cfg(feature = "v2018_2")] - filter: None, - #[cfg(feature = "v2017_6")] - sepolicy: None, - sepolicy_prefix: None, - } - } -} - -type StringStash<'a, T> = Stash<'a, *const c_char, Option>; -type WrapperStash<'a, GlibT, WrappedT> = Stash<'a, *mut GlibT, Option>; - -impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions { - #[allow(clippy::type_complexity)] - type Storage = ( - Box, - StringStash<'a, PathBuf>, - StringStash<'a, String>, - WrapperStash<'a, OstreeRepoDevInoCache, RepoDevInoCache>, - WrapperStash<'a, OstreeSePolicy, SePolicy>, - ); - - // We need to make sure that all memory pointed to by the returned pointer is kept alive by - // either the `self` reference or the returned Stash. - fn to_glib_none(&'a self) -> Stash<*const OstreeRepoCheckoutAtOptions, Self> { - // Creating this struct from zeroed memory is fine since it's `repr(C)` and only contains - // primitive types. In fact, the libostree docs say to zero the struct. This means we handle - // the unused bytes correctly. - // The struct needs to be boxed so the pointer we return remains valid even as the Stash is - // moved around. - let mut options = Box::new(unsafe { std::mem::zeroed::() }); - options.mode = self.mode.to_glib(); - options.overwrite_mode = self.overwrite_mode.to_glib(); - options.enable_uncompressed_cache = self.enable_uncompressed_cache.to_glib(); - options.enable_fsync = self.enable_fsync.to_glib(); - options.process_whiteouts = self.process_whiteouts.to_glib(); - options.no_copy_fallback = self.no_copy_fallback.to_glib(); - - #[cfg(feature = "v2017_6")] - { - options.force_copy = self.force_copy.to_glib(); - } - - #[cfg(feature = "v2017_7")] - { - options.bareuseronly_dirs = self.bareuseronly_dirs.to_glib(); - } - - #[cfg(feature = "v2018_9")] - { - options.force_copy_zerosized = self.force_copy_zerosized.to_glib(); - } - - // We keep these complex values alive by returning them in our Stash. Technically, some of - // these are being kept alive by `self` already, but it's better to be consistent here. - let subpath = self.subpath.to_glib_none(); - options.subpath = subpath.0; - let sepolicy_prefix = self.sepolicy_prefix.to_glib_none(); - options.sepolicy_prefix = sepolicy_prefix.0; - let devino_to_csum_cache = self.devino_to_csum_cache.to_glib_none(); - options.devino_to_csum_cache = devino_to_csum_cache.0; - - #[cfg(feature = "v2017_6")] - let sepolicy = { - let sepolicy = self.sepolicy.to_glib_none(); - options.sepolicy = sepolicy.0; - sepolicy - }; - #[cfg(not(feature = "v2017_6"))] - let sepolicy = None.to_glib_none(); - - #[cfg(feature = "v2018_2")] - { - if let Some(filter) = &self.filter { - options.filter_user_data = filter.to_glib_none().0; - options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe); - } - } - - Stash( - options.as_ref(), - ( - options, - subpath, - sepolicy_prefix, - devino_to_csum_cache, - sepolicy, - ), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use glib_sys::{GFALSE, GTRUE}; - use std::ffi::{CStr, CString}; - use std::ptr; - - #[test] - fn should_convert_default_options() { - let options = RepoCheckoutAtOptions::default(); - let stash = options.to_glib_none(); - let ptr = stash.0; - unsafe { - assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_NONE); - assert_eq!((*ptr).overwrite_mode, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE); - assert_eq!((*ptr).enable_uncompressed_cache, GFALSE); - assert_eq!((*ptr).enable_fsync, GFALSE); - assert_eq!((*ptr).process_whiteouts, GFALSE); - assert_eq!((*ptr).no_copy_fallback, GFALSE); - #[cfg(feature = "v2017_6")] - assert_eq!((*ptr).force_copy, GFALSE); - #[cfg(feature = "v2017_7")] - assert_eq!((*ptr).bareuseronly_dirs, GFALSE); - #[cfg(feature = "v2018_9")] - assert_eq!((*ptr).force_copy_zerosized, GFALSE); - assert_eq!((*ptr).unused_bools, [GFALSE; 4]); - assert_eq!((*ptr).subpath, ptr::null()); - assert_eq!((*ptr).devino_to_csum_cache, ptr::null_mut()); - assert_eq!((*ptr).unused_ints, [0; 6]); - assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]); - #[cfg(feature = "v2018_2")] - assert_eq!((*ptr).filter, None); - #[cfg(feature = "v2018_2")] - assert_eq!((*ptr).filter_user_data, ptr::null_mut()); - #[cfg(feature = "v2017_6")] - assert_eq!((*ptr).sepolicy, ptr::null_mut()); - assert_eq!((*ptr).sepolicy_prefix, ptr::null()); - } - } - - #[test] - fn should_convert_non_default_options() { - let options = RepoCheckoutAtOptions { - mode: RepoCheckoutMode::User, - overwrite_mode: RepoCheckoutOverwriteMode::UnionIdentical, - enable_uncompressed_cache: true, - enable_fsync: true, - process_whiteouts: true, - no_copy_fallback: true, - #[cfg(feature = "v2017_6")] - force_copy: true, - #[cfg(feature = "v2017_7")] - bareuseronly_dirs: true, - #[cfg(feature = "v2018_9")] - force_copy_zerosized: true, - subpath: Some("sub/path".into()), - devino_to_csum_cache: Some(RepoDevInoCache::new()), - #[cfg(feature = "v2018_2")] - filter: RepoCheckoutFilter::new(|_repo, _path, _stat| { - crate::RepoCheckoutFilterResult::Skip - }), - #[cfg(feature = "v2017_6")] - sepolicy: Some( - SePolicy::new(&gio::File::new_for_path("a/b"), gio::NONE_CANCELLABLE).unwrap(), - ), - sepolicy_prefix: Some("prefix".into()), - }; - let stash = options.to_glib_none(); - let ptr = stash.0; - unsafe { - assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_USER); - assert_eq!( - (*ptr).overwrite_mode, - OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL - ); - assert_eq!((*ptr).enable_uncompressed_cache, GTRUE); - assert_eq!((*ptr).enable_fsync, GTRUE); - assert_eq!((*ptr).process_whiteouts, GTRUE); - assert_eq!((*ptr).no_copy_fallback, GTRUE); - #[cfg(feature = "v2017_6")] - assert_eq!((*ptr).force_copy, GTRUE); - #[cfg(feature = "v2017_7")] - assert_eq!((*ptr).bareuseronly_dirs, GTRUE); - #[cfg(feature = "v2018_9")] - assert_eq!((*ptr).force_copy_zerosized, GTRUE); - assert_eq!((*ptr).unused_bools, [GFALSE; 4]); - assert_eq!( - CStr::from_ptr((*ptr).subpath), - CString::new("sub/path").unwrap().as_c_str() - ); - assert_eq!( - (*ptr).devino_to_csum_cache, - options.devino_to_csum_cache.to_glib_none().0 - ); - assert_eq!((*ptr).unused_ints, [0; 6]); - assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]); - #[cfg(feature = "v2018_2")] - assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe)); - #[cfg(feature = "v2018_2")] - assert_eq!( - (*ptr).filter_user_data, - options.filter.as_ref().unwrap().to_glib_none().0, - ); - #[cfg(feature = "v2017_6")] - assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0); - assert_eq!( - CStr::from_ptr((*ptr).sepolicy_prefix), - CString::new("prefix").unwrap().as_c_str() - ); - } - } -} diff --git a/rust-bindings/rust/src/repo_checkout_at_options/mod.rs b/rust-bindings/rust/src/repo_checkout_at_options/mod.rs new file mode 100644 index 00000000..7d92e546 --- /dev/null +++ b/rust-bindings/rust/src/repo_checkout_at_options/mod.rs @@ -0,0 +1,263 @@ +use crate::{RepoCheckoutMode, RepoCheckoutOverwriteMode, RepoDevInoCache, SePolicy}; +use glib::translate::*; +use libc::c_char; +use ostree_sys::*; +use std::path::PathBuf; + +#[cfg(any(feature = "v2018_2", feature = "dox"))] +mod repo_checkout_filter; +#[cfg(any(feature = "v2018_2", feature = "dox"))] +pub use self::repo_checkout_filter::RepoCheckoutFilter; + +pub struct RepoCheckoutAtOptions { + pub mode: RepoCheckoutMode, + pub overwrite_mode: RepoCheckoutOverwriteMode, + pub enable_uncompressed_cache: bool, + pub enable_fsync: bool, + pub process_whiteouts: bool, + pub no_copy_fallback: bool, + #[cfg(any(feature = "v2017_6", feature = "dox"))] + pub force_copy: bool, + #[cfg(any(feature = "v2017_7", feature = "dox"))] + pub bareuseronly_dirs: bool, + #[cfg(any(feature = "v2018_9", feature = "dox"))] + pub force_copy_zerosized: bool, + pub subpath: Option, + pub devino_to_csum_cache: Option, + /// A callback function to decide which files and directories will be checked out from the + /// repo. See the documentation on [RepoCheckoutFilter](struct.RepoCheckoutFilter.html) for more + /// information on the signature. + /// + /// # Panics + /// This callback may not panic. If it does, `abort()` will be called to avoid unwinding across + /// an FFI boundary and into the libostree C code (which is Undefined Behavior). If you prefer to + /// swallow the panic rather than aborting, you can use `std::panic::catch_unwind` inside your + /// callback to catch and silence any panics that occur. + #[cfg(any(feature = "v2018_2", feature = "dox"))] + pub filter: Option, + #[cfg(any(feature = "v2017_6", feature = "dox"))] + pub sepolicy: Option, + pub sepolicy_prefix: Option, +} + +impl Default for RepoCheckoutAtOptions { + fn default() -> Self { + RepoCheckoutAtOptions { + mode: RepoCheckoutMode::None, + overwrite_mode: RepoCheckoutOverwriteMode::None, + enable_uncompressed_cache: false, + enable_fsync: false, + process_whiteouts: false, + no_copy_fallback: false, + #[cfg(feature = "v2017_6")] + force_copy: false, + #[cfg(feature = "v2017_7")] + bareuseronly_dirs: false, + #[cfg(feature = "v2018_9")] + force_copy_zerosized: false, + subpath: None, + devino_to_csum_cache: None, + #[cfg(feature = "v2018_2")] + filter: None, + #[cfg(feature = "v2017_6")] + sepolicy: None, + sepolicy_prefix: None, + } + } +} + +type StringStash<'a, T> = Stash<'a, *const c_char, Option>; +type WrapperStash<'a, GlibT, WrappedT> = Stash<'a, *mut GlibT, Option>; + +impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions { + #[allow(clippy::type_complexity)] + type Storage = ( + Box, + StringStash<'a, PathBuf>, + StringStash<'a, String>, + WrapperStash<'a, OstreeRepoDevInoCache, RepoDevInoCache>, + WrapperStash<'a, OstreeSePolicy, SePolicy>, + ); + + // We need to make sure that all memory pointed to by the returned pointer is kept alive by + // either the `self` reference or the returned Stash. + fn to_glib_none(&'a self) -> Stash<*const OstreeRepoCheckoutAtOptions, Self> { + // Creating this struct from zeroed memory is fine since it's `repr(C)` and only contains + // primitive types. In fact, the libostree docs say to zero the struct. This means we handle + // the unused bytes correctly. + // The struct needs to be boxed so the pointer we return remains valid even as the Stash is + // moved around. + let mut options = Box::new(unsafe { std::mem::zeroed::() }); + options.mode = self.mode.to_glib(); + options.overwrite_mode = self.overwrite_mode.to_glib(); + options.enable_uncompressed_cache = self.enable_uncompressed_cache.to_glib(); + options.enable_fsync = self.enable_fsync.to_glib(); + options.process_whiteouts = self.process_whiteouts.to_glib(); + options.no_copy_fallback = self.no_copy_fallback.to_glib(); + + #[cfg(feature = "v2017_6")] + { + options.force_copy = self.force_copy.to_glib(); + } + + #[cfg(feature = "v2017_7")] + { + options.bareuseronly_dirs = self.bareuseronly_dirs.to_glib(); + } + + #[cfg(feature = "v2018_9")] + { + options.force_copy_zerosized = self.force_copy_zerosized.to_glib(); + } + + // We keep these complex values alive by returning them in our Stash. Technically, some of + // these are being kept alive by `self` already, but it's better to be consistent here. + let subpath = self.subpath.to_glib_none(); + options.subpath = subpath.0; + let sepolicy_prefix = self.sepolicy_prefix.to_glib_none(); + options.sepolicy_prefix = sepolicy_prefix.0; + let devino_to_csum_cache = self.devino_to_csum_cache.to_glib_none(); + options.devino_to_csum_cache = devino_to_csum_cache.0; + + #[cfg(feature = "v2017_6")] + let sepolicy = { + let sepolicy = self.sepolicy.to_glib_none(); + options.sepolicy = sepolicy.0; + sepolicy + }; + #[cfg(not(feature = "v2017_6"))] + let sepolicy = None.to_glib_none(); + + #[cfg(feature = "v2018_2")] + { + if let Some(filter) = &self.filter { + options.filter_user_data = filter.to_glib_none().0; + options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe); + } + } + + Stash( + options.as_ref(), + ( + options, + subpath, + sepolicy_prefix, + devino_to_csum_cache, + sepolicy, + ), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use glib_sys::{GFALSE, GTRUE}; + use std::ffi::{CStr, CString}; + use std::ptr; + + #[test] + fn should_convert_default_options() { + let options = RepoCheckoutAtOptions::default(); + let stash = options.to_glib_none(); + let ptr = stash.0; + unsafe { + assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_NONE); + assert_eq!((*ptr).overwrite_mode, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE); + assert_eq!((*ptr).enable_uncompressed_cache, GFALSE); + assert_eq!((*ptr).enable_fsync, GFALSE); + assert_eq!((*ptr).process_whiteouts, GFALSE); + assert_eq!((*ptr).no_copy_fallback, GFALSE); + #[cfg(feature = "v2017_6")] + assert_eq!((*ptr).force_copy, GFALSE); + #[cfg(feature = "v2017_7")] + assert_eq!((*ptr).bareuseronly_dirs, GFALSE); + #[cfg(feature = "v2018_9")] + assert_eq!((*ptr).force_copy_zerosized, GFALSE); + assert_eq!((*ptr).unused_bools, [GFALSE; 4]); + assert_eq!((*ptr).subpath, ptr::null()); + assert_eq!((*ptr).devino_to_csum_cache, ptr::null_mut()); + assert_eq!((*ptr).unused_ints, [0; 6]); + assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]); + #[cfg(feature = "v2018_2")] + assert_eq!((*ptr).filter, None); + #[cfg(feature = "v2018_2")] + assert_eq!((*ptr).filter_user_data, ptr::null_mut()); + #[cfg(feature = "v2017_6")] + assert_eq!((*ptr).sepolicy, ptr::null_mut()); + assert_eq!((*ptr).sepolicy_prefix, ptr::null()); + } + } + + #[test] + fn should_convert_non_default_options() { + let options = RepoCheckoutAtOptions { + mode: RepoCheckoutMode::User, + overwrite_mode: RepoCheckoutOverwriteMode::UnionIdentical, + enable_uncompressed_cache: true, + enable_fsync: true, + process_whiteouts: true, + no_copy_fallback: true, + #[cfg(feature = "v2017_6")] + force_copy: true, + #[cfg(feature = "v2017_7")] + bareuseronly_dirs: true, + #[cfg(feature = "v2018_9")] + force_copy_zerosized: true, + subpath: Some("sub/path".into()), + devino_to_csum_cache: Some(RepoDevInoCache::new()), + #[cfg(feature = "v2018_2")] + filter: RepoCheckoutFilter::new(|_repo, _path, _stat| { + crate::RepoCheckoutFilterResult::Skip + }), + #[cfg(feature = "v2017_6")] + sepolicy: Some( + SePolicy::new(&gio::File::new_for_path("a/b"), gio::NONE_CANCELLABLE).unwrap(), + ), + sepolicy_prefix: Some("prefix".into()), + }; + let stash = options.to_glib_none(); + let ptr = stash.0; + unsafe { + assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_USER); + assert_eq!( + (*ptr).overwrite_mode, + OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL + ); + assert_eq!((*ptr).enable_uncompressed_cache, GTRUE); + assert_eq!((*ptr).enable_fsync, GTRUE); + assert_eq!((*ptr).process_whiteouts, GTRUE); + assert_eq!((*ptr).no_copy_fallback, GTRUE); + #[cfg(feature = "v2017_6")] + assert_eq!((*ptr).force_copy, GTRUE); + #[cfg(feature = "v2017_7")] + assert_eq!((*ptr).bareuseronly_dirs, GTRUE); + #[cfg(feature = "v2018_9")] + assert_eq!((*ptr).force_copy_zerosized, GTRUE); + assert_eq!((*ptr).unused_bools, [GFALSE; 4]); + assert_eq!( + CStr::from_ptr((*ptr).subpath), + CString::new("sub/path").unwrap().as_c_str() + ); + assert_eq!( + (*ptr).devino_to_csum_cache, + options.devino_to_csum_cache.to_glib_none().0 + ); + assert_eq!((*ptr).unused_ints, [0; 6]); + assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]); + #[cfg(feature = "v2018_2")] + assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe)); + #[cfg(feature = "v2018_2")] + assert_eq!( + (*ptr).filter_user_data, + options.filter.as_ref().unwrap().to_glib_none().0, + ); + #[cfg(feature = "v2017_6")] + assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0); + assert_eq!( + CStr::from_ptr((*ptr).sepolicy_prefix), + CString::new("prefix").unwrap().as_c_str() + ); + } + } +} diff --git a/rust-bindings/rust/sys/src/auto/versions.txt b/rust-bindings/rust/sys/src/auto/versions.txt index 85a1b25d..557e5d3e 100644 --- a/rust-bindings/rust/sys/src/auto/versions.txt +++ b/rust-bindings/rust/sys/src/auto/versions.txt @@ -1,2 +1,2 @@ Generated by gir (https://github.com/gtk-rs/gir @ 2d1ffab1) -from gir-files (https://github.com/gtk-rs/gir-files @ eec42a9) +from gir-files (https://github.com/gtk-rs/gir-files @ ff904f0)