lib: implement CheckoutOptions::filter (hackishly)

This commit is contained in:
Felix Krull 2019-05-31 21:56:44 +02:00 committed by Colin Walters
parent a521c838f5
commit 19fdf706d5
6 changed files with 154 additions and 12 deletions

View File

@ -24,6 +24,7 @@ generate = [
"OSTree.MutableTree",
"OSTree.ObjectType",
"OSTree.Remote",
"OSTree.RepoCheckoutFilterResult",
"OSTree.RepoCheckoutMode",
"OSTree.RepoCheckoutOverwriteMode",
"OSTree.RepoCommitModifier",

View File

@ -242,6 +242,53 @@ impl FromGlib<ostree_sys::OstreeObjectType> for ObjectType {
}
}
#[cfg(any(feature = "v2018_2", feature = "dox"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Copy)]
pub enum RepoCheckoutFilterResult {
Allow,
Skip,
#[doc(hidden)]
__Unknown(i32),
}
#[cfg(any(feature = "v2018_2", feature = "dox"))]
impl fmt::Display for RepoCheckoutFilterResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RepoCheckoutFilterResult::{}", match *self {
RepoCheckoutFilterResult::Allow => "Allow",
RepoCheckoutFilterResult::Skip => "Skip",
_ => "Unknown",
})
}
}
#[cfg(any(feature = "v2018_2", feature = "dox"))]
#[doc(hidden)]
impl ToGlib for RepoCheckoutFilterResult {
type GlibType = ostree_sys::OstreeRepoCheckoutFilterResult;
fn to_glib(&self) -> ostree_sys::OstreeRepoCheckoutFilterResult {
match *self {
RepoCheckoutFilterResult::Allow => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_ALLOW,
RepoCheckoutFilterResult::Skip => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_SKIP,
RepoCheckoutFilterResult::__Unknown(value) => value
}
}
}
#[cfg(any(feature = "v2018_2", feature = "dox"))]
#[doc(hidden)]
impl FromGlib<ostree_sys::OstreeRepoCheckoutFilterResult> for RepoCheckoutFilterResult {
fn from_glib(value: ostree_sys::OstreeRepoCheckoutFilterResult) -> Self {
match value {
0 => RepoCheckoutFilterResult::Allow,
1 => RepoCheckoutFilterResult::Skip,
value => RepoCheckoutFilterResult::__Unknown(value),
}
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Copy)]
pub enum RepoCheckoutMode {

View File

@ -71,6 +71,8 @@ pub use self::enums::DeploymentUnlockedState;
pub use self::enums::GpgSignatureAttr;
pub use self::enums::GpgSignatureFormatFlags;
pub use self::enums::ObjectType;
#[cfg(any(feature = "v2018_2", feature = "dox"))]
pub use self::enums::RepoCheckoutFilterResult;
pub use self::enums::RepoCheckoutMode;
pub use self::enums::RepoCheckoutOverwriteMode;
pub use self::enums::RepoMode;

View File

@ -34,11 +34,13 @@ pub use crate::auto::*;
mod collection_ref;
mod object_name;
mod repo;
#[cfg(any(feature = "v2018_2", feature = "dox"))]
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::*;
#[cfg(any(feature = "v2018_2", feature = "dox"))]
pub use crate::repo_checkout_at_options::*;
// tests

View File

@ -1,11 +1,15 @@
use glib::translate::{Stash, ToGlib, ToGlibPtr};
use glib::translate::{FromGlibPtrNone, Stash, ToGlib, ToGlibPtr};
use glib_sys::gpointer;
use libc::c_char;
use ostree_sys::OstreeRepoCheckoutAtOptions;
use std::path::PathBuf;
use ostree_sys::{OstreeRepo, OstreeRepoCheckoutAtOptions, OstreeRepoCheckoutFilterResult};
use std::path::{Path, PathBuf};
use {Repo, RepoCheckoutFilterResult};
use {RepoCheckoutMode, RepoCheckoutOverwriteMode};
use {RepoDevInoCache, SePolicy};
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub type RepoCheckoutFilter =
Option<Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>>;
pub struct RepoCheckoutAtOptions {
pub mode: RepoCheckoutMode,
pub overwrite_mode: RepoCheckoutOverwriteMode,
@ -18,9 +22,8 @@ pub struct RepoCheckoutAtOptions {
pub force_copy_zerosized: bool,
pub subpath: Option<PathBuf>,
pub devino_to_csum_cache: Option<RepoDevInoCache>,
// TODO: those thingamajigs
// pub filter: OstreeRepoCheckoutFilter,
// pub filter_user_data: gpointer,
// TODO: might be interesting to turn this into a type parameter
pub filter: RepoCheckoutFilter,
pub sepolicy: Option<SePolicy>,
pub sepolicy_prefix: Option<String>,
}
@ -39,6 +42,7 @@ impl Default for RepoCheckoutAtOptions {
force_copy_zerosized: false,
subpath: None,
devino_to_csum_cache: None,
filter: None,
sepolicy: None,
sepolicy_prefix: None,
}
@ -47,6 +51,21 @@ impl Default for RepoCheckoutAtOptions {
type StringStash<'a, T> = Stash<'a, *const c_char, Option<T>>;
unsafe extern "C" fn filter_trampoline(
repo: *mut OstreeRepo,
path: *const c_char,
stat: *mut libc::stat,
user_data: gpointer,
) -> OstreeRepoCheckoutFilterResult {
// TODO: handle unwinding
let closure =
user_data as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>;
let repo = FromGlibPtrNone::from_glib_none(repo);
let path: PathBuf = FromGlibPtrNone::from_glib_none(path);
let result = (*closure)(&repo, &path, &*stat);
result.to_glib()
}
impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions {
type Storage = (
Box<OstreeRepoCheckoutAtOptions>,
@ -75,6 +94,21 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
let sepolicy = self.sepolicy.to_glib_none();
options.sepolicy = sepolicy.0;
if let Some(filter) = &self.filter {
options.filter_user_data = filter
as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
as gpointer;
options.filter = Some(
filter_trampoline
as unsafe extern "C" fn(
*mut OstreeRepo,
*const c_char,
*mut libc::stat,
gpointer,
) -> OstreeRepoCheckoutFilterResult,
);
}
Stash(options.as_ref(), (options, subpath, sepolicy_prefix))
}
}
@ -83,7 +117,7 @@ impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOpt
mod tests {
use super::*;
use gio::{File, NONE_CANCELLABLE};
use glib_sys::{GFALSE, GTRUE};
use glib_sys::{gpointer, 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,
@ -132,6 +166,9 @@ mod tests {
force_copy_zerosized: true,
subpath: Some("sub/path".into()),
devino_to_csum_cache: Some(RepoDevInoCache::new()),
filter: Some(Box::new(|_repo, _path, _stat| {
RepoCheckoutFilterResult::Skip
})),
sepolicy: Some(SePolicy::new(&File::new_for_path("a/b"), NONE_CANCELLABLE).unwrap()),
sepolicy_prefix: Some("prefix".into()),
};
@ -161,8 +198,25 @@ mod tests {
);
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).filter,
Some(
filter_trampoline
as unsafe extern "C" fn(
*mut OstreeRepo,
*const c_char,
*mut libc::stat,
gpointer,
)
-> OstreeRepoCheckoutFilterResult
)
);
assert_eq!(
(*ptr).filter_user_data,
options.filter.as_ref().unwrap()
as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
as gpointer
);
assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0);
assert_eq!(
CStr::from_ptr((*ptr).sepolicy_prefix),

View File

@ -13,8 +13,8 @@ use gio::prelude::*;
use gio::NONE_CANCELLABLE;
use glib::prelude::*;
use ostree::{
ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutMode, RepoCheckoutOverwriteMode,
RepoDevInoCache,
ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutFilterResult, RepoCheckoutMode,
RepoCheckoutOverwriteMode, RepoDevInoCache,
};
use std::os::unix::io::AsRawFd;
@ -161,6 +161,9 @@ fn should_checkout_at_with_options() {
force_copy: true,
force_copy_zerosized: true,
devino_to_csum_cache: Some(RepoDevInoCache::new()),
filter: Some(Box::new(|_repo, _path, _stat| {
RepoCheckoutFilterResult::Allow
})),
..Default::default()
}),
dirfd.as_raw_fd(),
@ -172,3 +175,36 @@ fn should_checkout_at_with_options() {
assert_test_file(checkout_dir.path());
}
#[test]
#[cfg(feature = "v2016_8")]
fn should_checkout_at_with_filter() {
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 {
filter: Some(Box::new(|_repo, path, _stat| {
if let Some("testfile") = path.file_name().map(|s| s.to_str().unwrap()) {
RepoCheckoutFilterResult::Skip
} else {
RepoCheckoutFilterResult::Allow
}
})),
..Default::default()
}),
dirfd.as_raw_fd(),
"test-checkout",
&checksum,
NONE_CANCELLABLE,
)
.expect("checkout at");
let testdir = checkout_dir.path().join("test-checkout").join("testdir");
assert!(std::fs::read_dir(&testdir).is_ok());
assert!(std::fs::File::open(&testdir.join("testfile")).is_err());
}