From eec4a2287d0ba2e00f4c4acc95f8d84fb41402c6 Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Sun, 26 May 2019 19:18:12 +0200 Subject: [PATCH] lib: RepoCheckoutAtOptions --- rust-bindings/rust/Cargo.toml | 1 + rust-bindings/rust/conf/ostree.toml | 13 +- rust-bindings/rust/src/auto/repo.rs | 15 +- rust-bindings/rust/src/lib.rs | 2 + .../rust/src/repo_checkout_at_options.rs | 167 ++++++++++++++++++ rust-bindings/rust/tests/repo.rs | 60 ++++++- rust-bindings/rust/tests/util/mod.rs | 9 + 7 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 rust-bindings/rust/src/repo_checkout_at_options.rs diff --git a/rust-bindings/rust/Cargo.toml b/rust-bindings/rust/Cargo.toml index e0ad1322..3eac8eb4 100644 --- a/rust-bindings/rust/Cargo.toml +++ b/rust-bindings/rust/Cargo.toml @@ -41,6 +41,7 @@ ostree-sys = { version = "0.3.0", path = "sys" } [dev-dependencies] maplit = "1.0.1" +openat = "0.1.17" tempfile = "3" [features] diff --git a/rust-bindings/rust/conf/ostree.toml b/rust-bindings/rust/conf/ostree.toml index 5d7e8331..235cfbe1 100644 --- a/rust-bindings/rust/conf/ostree.toml +++ b/rust-bindings/rust/conf/ostree.toml @@ -49,7 +49,6 @@ generate = [ #"OSTree.RepoPruneOptions", #"OSTree.RepoExportArchiveOptions", - #"OSTree.RepoCheckoutAtOptions", ] manual = [ @@ -63,6 +62,8 @@ manual = [ "GLib.KeyFile", "GLib.String", "GLib.Variant", + + "OSTree.RepoCheckoutAtOptions", ] [crate_name_overrides] @@ -94,6 +95,16 @@ status = "generate" name = "checkout_tree_at" ignore = true + [[object.function]] + # TODO: see which of these annotations I can upstream + name = "checkout_at" + [[object.function.parameter]] + name = "options" + const = true + [[object.function.parameter]] + name = "destination_path" + string_type = "filename" + [[object]] name = "OSTree.RepoFinderResult" status = "generate" diff --git a/rust-bindings/rust/src/auto/repo.rs b/rust-bindings/rust/src/auto/repo.rs index d7841c8e..7e942f8c 100644 --- a/rust-bindings/rust/src/auto/repo.rs +++ b/rust-bindings/rust/src/auto/repo.rs @@ -11,6 +11,8 @@ use MutableTree; use ObjectType; #[cfg(any(feature = "v2018_6", feature = "dox"))] use Remote; +#[cfg(any(feature = "v2016_8", feature = "dox"))] +use RepoCheckoutAtOptions; use RepoCheckoutMode; use RepoCheckoutOverwriteMode; use RepoCommitModifier; @@ -39,6 +41,7 @@ use glib_sys; use gobject_sys; use libc; use ostree_sys; +use std; use std::boxed::Box as Box_; use std::fmt; use std::mem; @@ -96,10 +99,14 @@ impl Repo { } } - //#[cfg(any(feature = "v2016_8", feature = "dox"))] - //pub fn checkout_at>(&self, options: /*Ignored*/Option<&mut RepoCheckoutAtOptions>, destination_dfd: i32, destination_path: &str, commit: &str, cancellable: Option<&P>) -> Result<(), Error> { - // unsafe { TODO: call ostree_sys:ostree_repo_checkout_at() } - //} + #[cfg(any(feature = "v2016_8", feature = "dox"))] + pub fn checkout_at, Q: IsA>(&self, options: Option<&RepoCheckoutAtOptions>, destination_dfd: i32, destination_path: P, commit: &str, cancellable: Option<&Q>) -> Result<(), Error> { + unsafe { + let mut error = ptr::null_mut(); + let _ = ostree_sys::ostree_repo_checkout_at(self.to_glib_none().0, mut_override(options.to_glib_none().0), destination_dfd, destination_path.as_ref().to_glib_none().0, commit.to_glib_none().0, cancellable.map(|p| p.as_ref()).to_glib_none().0, &mut error); + if error.is_null() { Ok(()) } else { Err(from_glib_full(error)) } + } + } pub fn checkout_gc>(&self, cancellable: Option<&P>) -> Result<(), Error> { unsafe { diff --git a/rust-bindings/rust/src/lib.rs b/rust-bindings/rust/src/lib.rs index d7653815..ea3130f6 100644 --- a/rust-bindings/rust/src/lib.rs +++ b/rust-bindings/rust/src/lib.rs @@ -34,10 +34,12 @@ pub use crate::auto::*; mod collection_ref; mod object_name; mod repo; +mod repo_checkout_at_options; #[cfg(any(feature = "v2018_6", feature = "dox"))] pub use crate::collection_ref::*; pub use crate::object_name::*; pub use crate::repo::*; +pub use crate::repo_checkout_at_options::*; // 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 new file mode 100644 index 00000000..0607af91 --- /dev/null +++ b/rust-bindings/rust/src/repo_checkout_at_options.rs @@ -0,0 +1,167 @@ +use glib::translate::{Stash, ToGlib, ToGlibPtr}; +use ostree_sys::OstreeRepoCheckoutAtOptions; +use std::os::raw::c_char; +use std::path::PathBuf; +use {RepoCheckoutMode, RepoCheckoutOverwriteMode}; +use {RepoDevInoCache, SePolicy}; + +#[derive(PartialEq, Eq, Hash, Debug, Clone)] +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, + pub force_copy: bool, + pub bareuseronly_dirs: bool, + pub force_copy_zerosized: bool, + pub subpath: Option, + pub devino_to_csum_cache: Option, + // pub filter: OstreeRepoCheckoutFilter, + // pub filter_user_data: gpointer, + 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, + force_copy: false, + bareuseronly_dirs: false, + force_copy_zerosized: false, + subpath: None, + devino_to_csum_cache: None, + sepolicy: None, + sepolicy_prefix: None, + } + } +} + +impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions { + type Storage = ( + Box, + Stash<'a, *const c_char, Option>, + Stash<'a, *const c_char, Option>, + ); + + fn to_glib_none(&'a self) -> Stash<*const OstreeRepoCheckoutAtOptions, Self> { + 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(); + options.force_copy = self.force_copy.to_glib(); + options.bareuseronly_dirs = self.bareuseronly_dirs.to_glib(); + options.force_copy_zerosized = self.force_copy_zerosized.to_glib(); + 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; + + /* + devino_to_csum_cache: None, + sepolicy: None, + */ + + Stash(options.as_ref(), (options, subpath, sepolicy_prefix)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gio::{File, NONE_CANCELLABLE}; + use glib_sys::{GFALSE, GTRUE}; + use ostree_sys::{ + OSTREE_REPO_CHECKOUT_MODE_NONE, OSTREE_REPO_CHECKOUT_MODE_USER, + OSTREE_REPO_CHECKOUT_OVERWRITE_NONE, OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL, + }; + 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); + assert_eq!((*ptr).force_copy, GFALSE); + assert_eq!((*ptr).bareuseronly_dirs, GFALSE); + 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]); + assert_eq!((*ptr).filter, None); + assert_eq!((*ptr).filter_user_data, ptr::null_mut()); + 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, + force_copy: true, + bareuseronly_dirs: true, + force_copy_zerosized: true, + subpath: Some("sub/path".into()), + devino_to_csum_cache: Some(RepoDevInoCache::new()), + sepolicy: Some(SePolicy::new(&File::new_for_path("a/b"), 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); + assert_eq!((*ptr).force_copy, GTRUE); + assert_eq!((*ptr).bareuseronly_dirs, GTRUE); + 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_ne!((*ptr).devino_to_csum_cache, ptr::null_mut()); + assert_eq!((*ptr).unused_ints, [0; 6]); + assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]); + assert_eq!((*ptr).filter, None); + assert_eq!((*ptr).filter_user_data, ptr::null_mut()); + assert_ne!((*ptr).sepolicy, ptr::null_mut()); + assert_eq!( + CStr::from_ptr((*ptr).sepolicy_prefix), + CString::new("prefix").unwrap().as_c_str() + ); + } + } +} diff --git a/rust-bindings/rust/tests/repo.rs b/rust-bindings/rust/tests/repo.rs index 7f8a7d11..02a094e4 100644 --- a/rust-bindings/rust/tests/repo.rs +++ b/rust-bindings/rust/tests/repo.rs @@ -1,5 +1,6 @@ extern crate gio; extern crate glib; +extern crate openat; extern crate ostree; extern crate tempfile; #[macro_use] @@ -11,7 +12,10 @@ use util::*; use gio::prelude::*; use gio::NONE_CANCELLABLE; use glib::prelude::*; -use ostree::{ObjectName, ObjectType}; +use ostree::{ + ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutMode, RepoCheckoutOverwriteMode, +}; +use std::os::unix::io::AsRawFd; #[test] fn should_commit_content_to_repo_and_list_refs_again() { @@ -91,11 +95,51 @@ fn should_checkout_tree() { ) .expect("checkout tree"); - let testfile_path = checkout_dir - .path() - .join("test-checkout") - .join("testdir") - .join("testfile"); - let testfile_contents = std::fs::read_to_string(testfile_path).expect("test file"); - assert_eq!("test\n", testfile_contents); + assert_test_file(checkout_dir.path()); +} + +#[test] +fn should_checkout_at_with_none_options() { + let test_repo = TestRepo::new(); + let checksum = test_repo.test_commit("test"); + let checkout_dir = tempfile::tempdir().expect("checkout dir"); + + let dirfd = openat::Dir::open(checkout_dir.path()).expect("openat"); + test_repo + .repo + .checkout_at( + None, + dirfd.as_raw_fd(), + "test-checkout", + &checksum, + NONE_CANCELLABLE, + ) + .expect("checkout at"); + + assert_test_file(checkout_dir.path()); +} + +#[test] +fn should_checkout_at_with_options() { + let test_repo = TestRepo::new(); + let checksum = test_repo.test_commit("test"); + let checkout_dir = tempfile::tempdir().expect("checkout dir"); + + let dirfd = openat::Dir::open(checkout_dir.path()).expect("openat"); + test_repo + .repo + .checkout_at( + Some(&RepoCheckoutAtOptions { + mode: RepoCheckoutMode::User, + overwrite_mode: RepoCheckoutOverwriteMode::UnionIdentical, + ..Default::default() + }), + dirfd.as_raw_fd(), + "test-checkout", + &checksum, + NONE_CANCELLABLE, + ) + .expect("checkout at"); + + assert_test_file(checkout_dir.path()); } diff --git a/rust-bindings/rust/tests/util/mod.rs b/rust-bindings/rust/tests/util/mod.rs index 8d92ea8a..32ed812c 100644 --- a/rust-bindings/rust/tests/util/mod.rs +++ b/rust-bindings/rust/tests/util/mod.rs @@ -64,3 +64,12 @@ pub fn commit(repo: &ostree::Repo, mtree: &ostree::MutableTree, ref_: &str) -> G .expect("commit transaction"); checksum } + +pub fn assert_test_file(checkout: &Path) { + let testfile_path = checkout + .join("test-checkout") + .join("testdir") + .join("testfile"); + let testfile_contents = std::fs::read_to_string(testfile_path).expect("test file"); + assert_eq!("test\n", testfile_contents); +}