From 4c0f9e0949029cf76914861cf900db2d2c2583e8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 3 Mar 2022 16:30:43 -0500 Subject: [PATCH 01/35] configure: post-release version bump --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 3bd735ed..95e22438 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ AC_PREREQ([2.63]) dnl To perform a release, follow the instructions in `docs/CONTRIBUTING.md`. m4_define([year_version], [2022]) -m4_define([release_version], [2]) +m4_define([release_version], [3]) m4_define([package_version], [year_version.release_version]) AC_INIT([libostree], [package_version], [walters@verbum.org]) -is_release_build=yes +is_release_build=no AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([buildutil]) AC_CONFIG_AUX_DIR([build-aux]) From ca0bb7595445870e6defab3d33f6c75eba60a8db Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Fri, 4 Mar 2022 13:56:38 +0000 Subject: [PATCH 02/35] apidoc: add missing page includes This fixes some missing sections in API reference, adding all the relevant includes. --- apidoc/ostree-docs.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apidoc/ostree-docs.xml b/apidoc/ostree-docs.xml index 1ad0de37..88bc8903 100644 --- a/apidoc/ostree-docs.xml +++ b/apidoc/ostree-docs.xml @@ -25,9 +25,16 @@ + + + + + + + API Index From 084f8913f02340831e91c4e39f6037af6ceb63e4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 7 Mar 2022 21:06:48 +0000 Subject: [PATCH 03/35] s390x-se-luks-gencpio: Fix shebang syntax An indented `#!` is technically meaningless, although many shells will run text files with the shell if asked to execute them. Signed-off-by: Simon McVittie --- src/libostree/s390x-se-luks-gencpio | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libostree/s390x-se-luks-gencpio b/src/libostree/s390x-se-luks-gencpio index f0ad24eb..729aff6f 100755 --- a/src/libostree/s390x-se-luks-gencpio +++ b/src/libostree/s390x-se-luks-gencpio @@ -1,5 +1,5 @@ - #!/usr/bin/bash - # This script creates new initramdisk with LUKS config within +#!/usr/bin/bash +# This script creates new initramdisk with LUKS config within set -euo pipefail old_initrd=$1 From a9a0110f440d10a4c9f51bf0f0ab9a7b3b79e2c5 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 7 Mar 2022 21:07:25 +0000 Subject: [PATCH 04/35] s390x-se-luks-gencpio: Use interoperable path for bash On OSs that do not consistently merge /usr/bin with /bin, the path to bash has traditionally been /bin/bash. Signed-off-by: Simon McVittie --- src/libostree/s390x-se-luks-gencpio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libostree/s390x-se-luks-gencpio b/src/libostree/s390x-se-luks-gencpio index 729aff6f..7c94a440 100755 --- a/src/libostree/s390x-se-luks-gencpio +++ b/src/libostree/s390x-se-luks-gencpio @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/bin/bash # This script creates new initramdisk with LUKS config within set -euo pipefail From 92ca2c7794afd06c1f3a602b53ac5e51ec34bfd4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 7 Mar 2022 19:52:25 +0000 Subject: [PATCH 05/35] test-prune: Read to the end of cut(1) output If we use head(1) to take only the first two lines, then cut(1) and earlier pipeline entries are killed by SIGPIPE (if they have not already terminated), and that's flagged as an error under `set -o pipefail`. Use an equivalent sed command to take exactly the second line, but without SIGPIPE. Signed-off-by: Simon McVittie Gbp-Pq: Name test-prune-Read-to-the-end-of-cut-1-output.patch --- tests/test-prune.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-prune.sh b/tests/test-prune.sh index 20904f31..bbb77a23 100755 --- a/tests/test-prune.sh +++ b/tests/test-prune.sh @@ -350,7 +350,7 @@ tap_ok --commit-only and --delete-commit # Test --delete-commit when it creates orphaned commits reinitialize_commit_only_test_repo # get the current HEAD's parent on dev branch -COMMIT_TO_DELETE=$(${CMD_PREFIX} ostree --repo=repo log dev | grep ^commit | cut -f 2 -d' ' | head -n 2 | tail -n 1) +COMMIT_TO_DELETE=$(${CMD_PREFIX} ostree --repo=repo log dev | grep ^commit | cut -f 2 -d' ' | sed -ne '2p') ${CMD_PREFIX} ostree --repo=repo prune --commit-only --refs-only --delete-commit=$COMMIT_TO_DELETE # we deleted a commit that orphaned another, so we lose two commits assert_repo_has_n_commits repo 4 From 8cc8e687683f363f070b900c8ab7bc913d5c83b7 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 8 Mar 2022 15:36:27 +0000 Subject: [PATCH 06/35] ostree-repo-pull: Take correct out path on error Like every other error return path in this function, jump to the `out` label on error here. Returning directly will cause leaks. Spotted by reading the code, not actually necessarily encountered in the wild. Signed-off-by: Philip Withnall --- src/libostree/ostree-repo-pull.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index e8918cf6..1d61faaf 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -4020,7 +4020,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, &pull_data->signapi_commit_verifiers, &pull_data->signapi_summary_verifiers, error)) - return FALSE; + goto out; } pull_data->phase = OSTREE_PULL_PHASE_FETCHING_REFS; From faa8ed547b0933aedcf9a27285c45787ddd87ed6 Mon Sep 17 00:00:00 2001 From: Joe Talbott Date: Wed, 9 Mar 2022 08:58:12 -0500 Subject: [PATCH 07/35] Add Fedora Kinoite link to index.md also. --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 5d925d6f..db729be3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,10 +47,11 @@ their [eos-updater](https://github.com/endlessm/eos-updater) and [deb-ostree-builder](https://github.com/dbnicholson/deb-ostree-builder) projects. -Fedora derivatives use rpm-ostree (noted below); there are 3 variants using OSTree: +Fedora derivatives use rpm-ostree (noted below); there are 4 variants using OSTree: - [Fedora CoreOS](https://getfedora.org/en/coreos/) - [Fedora Silverblue](https://silverblue.fedoraproject.org/) + - [Fedora Kinoite](https://kinoite.fedoraproject.org/) - [Fedora IoT](https://iot.fedoraproject.org/) Red Hat Enterprise Linux CoreOS is a derivative of Fedora CoreOS, used in [OpenShift 4](https://try.openshift.com/). From 039d86c63be7becbfba3b86c4bf8859055de4848 Mon Sep 17 00:00:00 2001 From: Damiano Donati Date: Fri, 11 Mar 2022 11:12:43 +0000 Subject: [PATCH 08/35] README.md: update ostree-rs language binding link According to the description on https://gitlab.com/fkrull/ostree-rs/ the repository is now moved to https://github.com/ostreedev/ostree-rs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fa17f94..0896b244 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ write higher level manual bindings on top; this is more common for statically compiled languages. Here's a list of such bindings: - [ostree-go](https://github.com/ostreedev/ostree-go/) - - [ostree-rs](https://gitlab.com/fkrull/ostree-rs/) + - [ostree-rs](https://github.com/ostreedev/ostree-rs/) ## Building From 0d020a7145da488d4939975860569a5d8841aaab Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 10 Mar 2022 16:46:53 -0500 Subject: [PATCH 09/35] tmpfiles: Create `/run/ostree` This is referenced by https://github.com/ostreedev/ostree-rs-ext/blob/9645cee4f29786ba51ae9d62a52eeef9230146fd/lib/src/globals.rs#L16 specifically used for the (container image) pull secret in `/run/ostree/auth.json`. Let's pre-create the directory so users don't have to. Motivated by https://github.com/openshift/machine-config-operator/pull/3007#discussion_r824172564 --- src/boot/ostree-tmpfiles.conf | 2 ++ tests/inst/src/sysroot.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/boot/ostree-tmpfiles.conf b/src/boot/ostree-tmpfiles.conf index 4cbba0bd..69c2d3f3 100644 --- a/src/boot/ostree-tmpfiles.conf +++ b/src/boot/ostree-tmpfiles.conf @@ -13,5 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +# ostree runtime configuration +d /run/ostree 0755 root root - # https://github.com/ostreedev/ostree/issues/393 R! /var/tmp/ostree-unlock-ovl.* diff --git a/tests/inst/src/sysroot.rs b/tests/inst/src/sysroot.rs index 301ef8b3..b10dbcd4 100644 --- a/tests/inst/src/sysroot.rs +++ b/tests/inst/src/sysroot.rs @@ -1,5 +1,8 @@ //! Tests that mostly use the API and access the booted sysroot read-only. +use std::os::unix::prelude::PermissionsExt; +use std::path::Path; + use anyhow::Result; use ostree_ext::prelude::*; use ostree_ext::{gio, ostree}; @@ -45,3 +48,13 @@ fn test_immutable_bit() -> Result<()> { cmd_has_output(sh_inline::bash_command!("lsattr -d /").unwrap(), "-i-")?; Ok(()) } + +#[itest] +fn test_tmpfiles() -> Result<()> { + if skip_non_ostree_host() { + return Ok(()); + } + let metadata = Path::new("/run/ostree").metadata()?; + assert_eq!(metadata.permissions().mode() & !nix::libc::S_IFMT, 0o755); + Ok(()) +} From b6d1119f912059b4851e1c37941eb176f8b5b192 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 27 Jan 2022 16:37:30 -0500 Subject: [PATCH 10/35] tests: Stop using inventory crate I was reading this thread https://internals.rust-lang.org/t/from-life-before-main-to-common-life-in-main/16006/30 and that reminded me about this code, which it turns out actually doesn't compile with my default local cargo config: ``` $ cat ~/.cargo/config [target.x86_64-unknown-linux-gnu] rustflags = ["-Ctarget-cpu=native", "-C", "link-arg=-fuse-ld=lld"] [profile.release] incremental = true $ cargo b ... error: linking with `cc` failed: exit status: 1 | = note: "cc" "-m64" "/var/srv/walters/src/github/ostreedev/ostree/target/debug/deps/ostree_test-4ca8e730f9dc6ffc.10325uqlhkyr5uol.rcgu.o" "/var/srv/walte" = note: ld.lld: error: undefined symbol: __start_linkme_NONDESTRUCTIVE_TESTS >>> referenced by 22nn09lfsklfqvyy >>> /var/srv/walters/src/github/ostreedev/ostree/target/debug/deps/ostree_test-4ca8e730f9dc6ffc.22nn09lfsklfqvyy.rcgu.o:(ostree_tes) ``` For now let's just go back to having a static list of functions. We don't have *too* many of those. --- tests/inst/Cargo.toml | 4 -- tests/inst/itest-macro/Cargo.toml | 14 ----- tests/inst/itest-macro/src/itest-macro.rs | 71 ----------------------- tests/inst/src/destructive.rs | 3 +- tests/inst/src/insttestmain.rs | 44 +++++++++----- tests/inst/src/repobin.rs | 15 ++--- tests/inst/src/sysroot.rs | 9 +-- tests/inst/src/test.rs | 16 +---- 8 files changed, 40 insertions(+), 136 deletions(-) delete mode 100644 tests/inst/itest-macro/Cargo.toml delete mode 100644 tests/inst/itest-macro/src/itest-macro.rs diff --git a/tests/inst/Cargo.toml b/tests/inst/Cargo.toml index 146744b8..31303f72 100644 --- a/tests/inst/Cargo.toml +++ b/tests/inst/Cargo.toml @@ -29,7 +29,6 @@ futures-util = "0.3.1" base64 = "0.12.0" procspawn = "0.8" rand = "0.7.3" -linkme = "0.2" strum = "0.18.0" strum_macros = "0.18.0" openat = "0.1.19" @@ -40,6 +39,3 @@ rpmostree-client = { git = "https://github.com/coreos/rpm-ostree", tag = "v2021. # This one I might publish to crates.io, not sure yet with-procspawn-tempdir = { git = "https://github.com/cgwalters/with-procspawn-tempdir" } - -# Internal crate for the test macro -itest-macro = { path = "itest-macro" } diff --git a/tests/inst/itest-macro/Cargo.toml b/tests/inst/itest-macro/Cargo.toml deleted file mode 100644 index 54494d29..00000000 --- a/tests/inst/itest-macro/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "itest-macro" -version = "0.1.0" -edition = "2018" - -[lib] -proc-macro = true -path = "src/itest-macro.rs" - -[dependencies] -quote = "1.0.3" -proc-macro2 = "1.0.10" -syn = { version = "1.0.3", features = ["full"] } -anyhow = "1.0" diff --git a/tests/inst/itest-macro/src/itest-macro.rs b/tests/inst/itest-macro/src/itest-macro.rs deleted file mode 100644 index 34d35a1a..00000000 --- a/tests/inst/itest-macro/src/itest-macro.rs +++ /dev/null @@ -1,71 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::quote; - -/// Wraps function using `procspawn` to allocate a new temporary directory, -/// make it the process' working directory, and run the function. -#[proc_macro_attribute] -pub fn itest(attrs: TokenStream, input: TokenStream) -> TokenStream { - let attrs = syn::parse_macro_input!(attrs as syn::AttributeArgs); - if attrs.len() > 1 { - return syn::Error::new_spanned(&attrs[1], "itest takes 0 or 1 attributes") - .to_compile_error() - .into(); - } - let destructive = match attrs.get(0) { - Some(syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue))) => { - if let Some(name) = namevalue.path.get_ident().map(|i| i.to_string()) { - if name == "destructive" { - match &namevalue.lit { - syn::Lit::Bool(v) => v.value, - _ => { - return syn::Error::new_spanned( - &attrs[1], - format!("destructive must be bool {}", name), - ) - .to_compile_error() - .into(); - } - } - } else { - return syn::Error::new_spanned( - &attrs[1], - format!("Unknown argument {}", name), - ) - .to_compile_error() - .into(); - } - } else { - false - } - } - Some(v) => { - return syn::Error::new_spanned(&v, "Unexpected argument") - .to_compile_error() - .into() - } - None => false, - }; - let func = syn::parse_macro_input!(input as syn::ItemFn); - let fident = func.sig.ident.clone(); - let varident = quote::format_ident!("ITEST_{}", fident); - let fidentstrbuf = format!(r#"{}"#, fident); - let fidentstr = syn::LitStr::new(&fidentstrbuf, Span::call_site()); - let testident = if destructive { - quote::format_ident!("{}", "DESTRUCTIVE_TESTS") - } else { - quote::format_ident!("{}", "NONDESTRUCTIVE_TESTS") - }; - let output = quote! { - #[linkme::distributed_slice(#testident)] - #[allow(non_upper_case_globals)] - static #varident : Test = Test { - name: #fidentstr, - f: #fident, - }; - #func - }; - output.into() -} diff --git a/tests/inst/src/destructive.rs b/tests/inst/src/destructive.rs index 2e2bd374..6900d391 100644 --- a/tests/inst/src/destructive.rs +++ b/tests/inst/src/destructive.rs @@ -537,8 +537,7 @@ fn impl_transaction_test>( } } -#[itest(destructive = true)] -fn transactionality() -> Result<()> { +pub(crate) fn itest_transactionality() -> Result<()> { testinit()?; let mark = get_reboot_mark()?; let cancellable = Some(gio::Cancellable::new()); diff --git a/tests/inst/src/insttestmain.rs b/tests/inst/src/insttestmain.rs index 0101363e..9d6d06b0 100644 --- a/tests/inst/src/insttestmain.rs +++ b/tests/inst/src/insttestmain.rs @@ -10,6 +10,25 @@ mod treegen; // Written by Ignition const DESTRUCTIVE_TEST_STAMP: &str = "/etc/ostree-destructive-test-ok"; +macro_rules! test { + ($f: path) => { + (stringify!($f), $f) + }; +} + +type StaticTest = (&'static str, fn() -> Result<()>); + +const TESTS: &[StaticTest] = &[ + test!(sysroot::itest_sysroot_ro), + test!(sysroot::itest_immutable_bit), + test!(sysroot::itest_tmpfiles), + test!(repobin::itest_basic), + test!(repobin::itest_nofifo), + test!(repobin::itest_extensions), + test!(repobin::itest_pull_basicauth), +]; +const DESTRUCTIVE_TESTS: &[StaticTest] = &[test!(destructive::itest_transactionality)]; + #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] #[allow(clippy::enum_variant_names)] @@ -30,18 +49,18 @@ enum NonDestructiveOpts { Args(Vec), } -fn libtest_from_test(t: &'static test::Test) -> test::TestImpl { +fn libtest_from_test(t: &StaticTest) -> test::TestImpl { libtest_mimic::Test { - name: t.name.into(), + name: t.0.into(), kind: "".into(), is_ignored: false, is_bench: false, - data: t, + data: t.1, } } fn run_test(test: &test::TestImpl) -> libtest_mimic::Outcome { - if let Err(e) = (test.data.f)() { + if let Err(e) = (test.data)() { libtest_mimic::Outcome::Failed { msg: Some(e.to_string()), } @@ -72,8 +91,8 @@ fn main() -> Result<()> { match opt { Opt::ListDestructive => { - for t in test::DESTRUCTIVE_TESTS.iter() { - println!("{}", t.name); + for t in DESTRUCTIVE_TESTS { + println!("{}", t.0); } Ok(()) } @@ -81,10 +100,7 @@ fn main() -> Result<()> { // FIXME add method to parse subargs let NonDestructiveOpts::Args(iter) = subopt; let libtestargs = libtest_mimic::Arguments::from_iter(iter); - let tests: Vec<_> = test::NONDESTRUCTIVE_TESTS - .iter() - .map(libtest_from_test) - .collect(); + let tests: Vec<_> = TESTS.iter().map(libtest_from_test).collect(); libtest_mimic::run_tests(&libtestargs, tests, run_test).exit(); } Opt::RunDestructive { name } => { @@ -98,10 +114,10 @@ fn main() -> Result<()> { bail!("An ostree-based host is required") } - for t in test::DESTRUCTIVE_TESTS.iter() { - if t.name == name { - (t.f)()?; - println!("ok destructive test: {}", t.name); + for (tname, f) in DESTRUCTIVE_TESTS { + if *tname == name.as_str() { + (f)()?; + println!("ok destructive test: {}", tname); return Ok(()); } } diff --git a/tests/inst/src/repobin.rs b/tests/inst/src/repobin.rs index 2180e81f..582b0290 100644 --- a/tests/inst/src/repobin.rs +++ b/tests/inst/src/repobin.rs @@ -8,15 +8,13 @@ use anyhow::{Context, Result}; use sh_inline::{bash, bash_command}; use with_procspawn_tempdir::with_procspawn_tempdir; -#[itest] -fn test_basic() -> Result<()> { +pub(crate) fn itest_basic() -> Result<()> { bash!(r"ostree --help >/dev/null")?; Ok(()) } -#[itest] #[with_procspawn_tempdir] -fn test_nofifo() -> Result<()> { +pub(crate) fn itest_nofifo() -> Result<()> { assert!(std::path::Path::new(".procspawn-tmpdir").exists()); bash!( r"ostree --repo=repo init --mode=archive @@ -34,9 +32,8 @@ fn test_nofifo() -> Result<()> { Ok(()) } -#[itest] #[with_procspawn_tempdir] -fn test_mtime() -> Result<()> { +pub(crate) fn itest_mtime() -> Result<()> { bash!( r"ostree --repo=repo init --mode=archive mkdir tmproot @@ -50,17 +47,15 @@ fn test_mtime() -> Result<()> { Ok(()) } -#[itest] #[with_procspawn_tempdir] -fn test_extensions() -> Result<()> { +pub(crate) fn itest_extensions() -> Result<()> { bash!(r"ostree --repo=repo init --mode=bare")?; assert!(Path::new("repo/extensions").exists()); Ok(()) } -#[itest] #[with_procspawn_tempdir] -fn test_pull_basicauth() -> Result<()> { +pub(crate) fn itest_pull_basicauth() -> Result<()> { let opts = TestHttpServerOpts { basicauth: true, ..Default::default() diff --git a/tests/inst/src/sysroot.rs b/tests/inst/src/sysroot.rs index b10dbcd4..818b4eb1 100644 --- a/tests/inst/src/sysroot.rs +++ b/tests/inst/src/sysroot.rs @@ -13,8 +13,7 @@ fn skip_non_ostree_host() -> bool { !std::path::Path::new("/run/ostree-booted").exists() } -#[itest] -fn test_sysroot_ro() -> Result<()> { +pub(crate) fn itest_sysroot_ro() -> Result<()> { // TODO add a skipped identifier if skip_non_ostree_host() { return Ok(()); @@ -39,8 +38,7 @@ fn test_sysroot_ro() -> Result<()> { Ok(()) } -#[itest] -fn test_immutable_bit() -> Result<()> { +pub(crate) fn itest_immutable_bit() -> Result<()> { if skip_non_ostree_host() { return Ok(()); } @@ -49,8 +47,7 @@ fn test_immutable_bit() -> Result<()> { Ok(()) } -#[itest] -fn test_tmpfiles() -> Result<()> { +pub(crate) fn itest_tmpfiles() -> Result<()> { if skip_non_ostree_host() { return Ok(()); } diff --git a/tests/inst/src/test.rs b/tests/inst/src/test.rs index 81592f7a..6f7aa258 100644 --- a/tests/inst/src/test.rs +++ b/tests/inst/src/test.rs @@ -6,10 +6,8 @@ use std::process::Command; use std::time; use anyhow::{bail, Context, Result}; -use linkme::distributed_slice; use rand::Rng; -pub use itest_macro::itest; pub use with_procspawn_tempdir::with_procspawn_tempdir; // HTTP Server deps @@ -20,19 +18,7 @@ use hyper_staticfile::Static; use tokio::runtime::Runtime; pub(crate) type TestFn = fn() -> Result<()>; - -#[derive(Debug)] -pub(crate) struct Test { - pub(crate) name: &'static str, - pub(crate) f: TestFn, -} - -pub(crate) type TestImpl = libtest_mimic::Test<&'static Test>; - -#[distributed_slice] -pub(crate) static NONDESTRUCTIVE_TESTS: [Test] = [..]; -#[distributed_slice] -pub(crate) static DESTRUCTIVE_TESTS: [Test] = [..]; +pub(crate) type TestImpl = libtest_mimic::Test; /// Run command and assert that its stderr contains pat pub(crate) fn cmd_fails_with>(mut c: C, pat: &str) -> Result<()> { From 34d1bcc68ab9cda211c569daa4c7e35f85a7e3dc Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 11 Mar 2022 15:32:27 -0500 Subject: [PATCH 11/35] Update to nix 0.23 Part of general crate updates. --- tests/inst/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/inst/Cargo.toml b/tests/inst/Cargo.toml index 31303f72..96c72e26 100644 --- a/tests/inst/Cargo.toml +++ b/tests/inst/Cargo.toml @@ -33,7 +33,7 @@ strum = "0.18.0" strum_macros = "0.18.0" openat = "0.1.19" openat-ext = "0.1.4" -nix = "0.20.0" +nix = "0.23.0" # See discussion in https://github.com/coreos/rpm-ostree/pull/2569#issuecomment-780569188 rpmostree-client = { git = "https://github.com/coreos/rpm-ostree", tag = "v2021.3" } From fba7efb6da609e0f3b384f7ea24d2ec2ef27db72 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 11 Mar 2022 15:33:02 -0500 Subject: [PATCH 12/35] Update to ostree-ext 0.6 Part of general crate updates. --- tests/inst/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/inst/Cargo.toml b/tests/inst/Cargo.toml index 96c72e26..884d314f 100644 --- a/tests/inst/Cargo.toml +++ b/tests/inst/Cargo.toml @@ -17,7 +17,7 @@ serde_json = "1.0" sh-inline = "0.1.0" anyhow = "1.0" tempfile = "3.1.0" -ostree-ext = { version = "0.3.0" } +ostree-ext = { version = "0.6.0" } libtest-mimic = "0.3.0" twoway = "0.2.1" hyper = { version = "0.14", features = ["runtime", "http1", "http2", "tcp", "server"] } From 75ae283f23e77dd8c849bb596d9d47a24af7d135 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 11 Mar 2022 15:34:59 -0500 Subject: [PATCH 13/35] Update to rand 0.8 Part of general crate updates. --- tests/inst/Cargo.toml | 2 +- tests/inst/src/destructive.rs | 2 +- tests/inst/src/test.rs | 2 +- tests/inst/src/treegen.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/inst/Cargo.toml b/tests/inst/Cargo.toml index 884d314f..3735c1d5 100644 --- a/tests/inst/Cargo.toml +++ b/tests/inst/Cargo.toml @@ -28,7 +28,7 @@ tokio = { version = "1.4.0", features = ["full"] } futures-util = "0.3.1" base64 = "0.12.0" procspawn = "0.8" -rand = "0.7.3" +rand = "0.8" strum = "0.18.0" strum_macros = "0.18.0" openat = "0.1.19" diff --git a/tests/inst/src/destructive.rs b/tests/inst/src/destructive.rs index 6900d391..98e1c687 100644 --- a/tests/inst/src/destructive.rs +++ b/tests/inst/src/destructive.rs @@ -450,7 +450,7 @@ fn impl_transaction_test>( let ms = std::cmp::min(cycle_time_ms.saturating_mul(20), 24 * 60 * 60 * 1000); time::Duration::from_millis(ms) } else { - time::Duration::from_millis(rng.gen_range(0, cycle_time_ms)) + time::Duration::from_millis(rng.gen_range(0..cycle_time_ms)) }; println!( "force-reboot-time={:?} cycle={:?} status:{:?}", diff --git a/tests/inst/src/test.rs b/tests/inst/src/test.rs index 6f7aa258..c18dbd23 100644 --- a/tests/inst/src/test.rs +++ b/tests/inst/src/test.rs @@ -91,7 +91,7 @@ pub(crate) async fn http_server>( ) -> Result> { if let Some(random_delay) = opts.random_delay { let slices = 100u32; - let n: u32 = rand::thread_rng().gen_range(0, slices); + let n: u32 = rand::thread_rng().gen_range(0..slices); std::thread::sleep((random_delay / slices) * n); } if opts.basicauth { diff --git a/tests/inst/src/treegen.rs b/tests/inst/src/treegen.rs index 975db472..2ec4e849 100644 --- a/tests/inst/src/treegen.rs +++ b/tests/inst/src/treegen.rs @@ -62,9 +62,9 @@ pub(crate) fn mutate_one_executable_to( let extra = rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) .take(10) - .collect::(); + .collect::>(); destf - .write_all(extra.as_bytes()) + .write_all(&extra) .context("Failed to append extra data")?; Ok(()) } From aca9e8e6a73c79554f320b85aafb4cb01e91ac5a Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Thu, 17 Feb 2022 20:12:18 +0100 Subject: [PATCH 14/35] glib: bump glib requirement to 2.66 and port to GUri This removes the old SoupURI copypasta from previous generation of libsoup and opens up a path for a simple libsoup3 port. --- Makefile-libostree.am | 9 - Makefile.am | 2 +- configure.ac | 2 +- src/libostree/ostree-chain-input-stream.c | 11 +- src/libostree/ostree-checksum-input-stream.c | 12 +- src/libostree/ostree-core.c | 38 +- src/libostree/ostree-fetcher-curl.c | 35 +- src/libostree/ostree-fetcher-soup.c | 13 +- src/libostree/ostree-fetcher-uri.c | 86 +- src/libostree/ostree-fetcher-util.c | 3 +- src/libostree/ostree-gpg-verifier.c | 3 +- .../ostree-libarchive-input-stream.c | 12 +- src/libostree/ostree-lzma-compressor.c | 3 +- src/libostree/ostree-repo-commit.c | 78 +- src/libostree/ostree-repo-file.c | 21 +- src/libostree/ostree-repo-finder-avahi.c | 16 +- src/libostree/ostree-repo-pull.c | 75 +- .../ostree-repo-static-delta-processing.c | 37 +- src/libostree/ostree-repo.c | 13 +- src/libostree/ostree-soup-form.c | 140 -- src/libostree/ostree-soup-uri.c | 1438 ----------------- src/libostree/ostree-soup-uri.h | 147 -- src/libostree/ostree-sysroot.c | 5 +- src/libotutil/ot-checksum-instream.c | 9 +- src/libotutil/ot-gpg-utils.c | 3 +- src/ostree/ot-builtin-fsck.c | 3 +- src/ostree/ot-builtin-prune.c | 3 +- tests/repo-finder-mount.c | 3 +- tests/test-mock-gio.c | 3 +- 29 files changed, 266 insertions(+), 1957 deletions(-) delete mode 100644 src/libostree/ostree-soup-form.c delete mode 100644 src/libostree/ostree-soup-uri.c delete mode 100644 src/libostree/ostree-soup-uri.h diff --git a/Makefile-libostree.am b/Makefile-libostree.am index f125adb8..b58106aa 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -221,8 +221,6 @@ endif if USE_CURL libostree_1_la_SOURCES += src/libostree/ostree-fetcher-curl.c \ - src/libostree/ostree-soup-uri.h src/libostree/ostree-soup-uri.c \ - src/libostree/ostree-soup-form.c \ $(NULL) libostree_1_la_CFLAGS += $(OT_DEP_CURL_CFLAGS) libostree_1_la_LIBADD += $(OT_DEP_CURL_LIBS) @@ -231,13 +229,6 @@ if USE_LIBSOUP libostree_1_la_SOURCES += src/libostree/ostree-fetcher-soup.c libostree_1_la_CFLAGS += $(OT_INTERNAL_SOUP_CFLAGS) libostree_1_la_LIBADD += $(OT_INTERNAL_SOUP_LIBS) -else -if USE_AVAHI -libostree_1_la_SOURCES += src/libostree/ostree-soup-uri.h \ - src/libostree/ostree-soup-uri.c \ - src/libostree/ostree-soup-form.c \ - $(NULL) -endif endif endif diff --git a/Makefile.am b/Makefile.am index ce90ca45..6eb88388 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,7 +29,7 @@ AM_CPPFLAGS += -DDATADIR='"$(datadir)"' -DLIBEXECDIR='"$(libexecdir)"' \ -DOSTREE_COMPILATION \ -DG_LOG_DOMAIN=\"OSTree\" \ -DOSTREE_GITREV='"$(OSTREE_GITREV)"' \ - -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44 '-DGLIB_VERSION_MAX_ALLOWED=G_ENCODE_VERSION(2,50)' \ + -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_66 '-DGLIB_VERSION_MAX_ALLOWED=G_ENCODE_VERSION(2,70)' \ -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_40 '-DSOUP_VERSION_MAX_ALLOWED=G_ENCODE_VERSION(2,48)' # For strict aliasing, see https://bugzilla.gnome.org/show_bug.cgi?id=791622 AM_CFLAGS += -std=gnu99 -fno-strict-aliasing $(WARN_CFLAGS) diff --git a/configure.ac b/configure.ac index 95e22438..8ca2f451 100644 --- a/configure.ac +++ b/configure.ac @@ -113,7 +113,7 @@ AM_PATH_GLIB_2_0(,,AC_MSG_ERROR([GLib not found])) dnl When bumping the gio-unix-2.0 dependency (or glib-2.0 in general), dnl remember to bump GLIB_VERSION_MIN_REQUIRED and dnl GLIB_VERSION_MAX_ALLOWED in Makefile.am -GIO_DEPENDENCY="gio-unix-2.0 >= 2.44.0" +GIO_DEPENDENCY="gio-unix-2.0 >= 2.66.0" PKG_CHECK_MODULES(OT_DEP_GIO_UNIX, $GIO_DEPENDENCY) dnl 5.1.0 is an arbitrary version here diff --git a/src/libostree/ostree-chain-input-stream.c b/src/libostree/ostree-chain-input-stream.c index 879d10c7..5ff4392a 100644 --- a/src/libostree/ostree-chain-input-stream.c +++ b/src/libostree/ostree-chain-input-stream.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -26,13 +27,13 @@ enum { PROP_STREAMS }; -G_DEFINE_TYPE (OstreeChainInputStream, ostree_chain_input_stream, G_TYPE_INPUT_STREAM) - struct _OstreeChainInputStreamPrivate { GPtrArray *streams; guint index; }; +G_DEFINE_TYPE_WITH_PRIVATE (OstreeChainInputStream, ostree_chain_input_stream, G_TYPE_INPUT_STREAM) + static void ostree_chain_input_stream_set_property (GObject *object, guint prop_id, const GValue *value, @@ -56,8 +57,6 @@ ostree_chain_input_stream_class_init (OstreeChainInputStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (OstreeChainInputStreamPrivate)); gobject_class->get_property = ostree_chain_input_stream_get_property; gobject_class->set_property = ostree_chain_input_stream_set_property; @@ -137,9 +136,7 @@ ostree_chain_input_stream_finalize (GObject *object) static void ostree_chain_input_stream_init (OstreeChainInputStream *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - OSTREE_TYPE_CHAIN_INPUT_STREAM, - OstreeChainInputStreamPrivate); + self->priv = ostree_chain_input_stream_get_instance_private (self); } diff --git a/src/libostree/ostree-checksum-input-stream.c b/src/libostree/ostree-checksum-input-stream.c index 8cef10d3..7cdf2048 100644 --- a/src/libostree/ostree-checksum-input-stream.c +++ b/src/libostree/ostree-checksum-input-stream.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -26,12 +27,12 @@ enum { PROP_CHECKSUM }; -G_DEFINE_TYPE (OstreeChecksumInputStream, ostree_checksum_input_stream, G_TYPE_FILTER_INPUT_STREAM) - struct _OstreeChecksumInputStreamPrivate { GChecksum *checksum; }; +G_DEFINE_TYPE_WITH_PRIVATE (OstreeChecksumInputStream, ostree_checksum_input_stream, G_TYPE_FILTER_INPUT_STREAM) + static void ostree_checksum_input_stream_set_property (GObject *object, guint prop_id, const GValue *value, @@ -51,8 +52,6 @@ ostree_checksum_input_stream_class_init (OstreeChecksumInputStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (OstreeChecksumInputStreamPrivate)); gobject_class->get_property = ostree_checksum_input_stream_get_property; gobject_class->set_property = ostree_checksum_input_stream_set_property; @@ -118,10 +117,7 @@ ostree_checksum_input_stream_get_property (GObject *object, static void ostree_checksum_input_stream_init (OstreeChecksumInputStream *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - OSTREE_TYPE_CHECKSUM_INPUT_STREAM, - OstreeChecksumInputStreamPrivate); - + self->priv = ostree_checksum_input_stream_get_instance_private (self); } OstreeChecksumInputStream * diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index f0d0e698..794f0e11 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -1031,19 +1032,22 @@ typedef struct { } ChecksumFileAsyncData; static void -checksum_file_async_thread (GSimpleAsyncResult *res, +checksum_file_async_thread (GTask *task, GObject *object, + gpointer datap, GCancellable *cancellable) { GError *error = NULL; - ChecksumFileAsyncData *data; + ChecksumFileAsyncData *data = datap; guchar *csum = NULL; - data = g_simple_async_result_get_op_res_gpointer (res); if (!ostree_checksum_file (data->f, data->objtype, &csum, cancellable, &error)) - g_simple_async_result_take_error (res, error); + g_task_return_error (task, error); else - data->csum = csum; + { + data->csum = csum; + g_task_return_pointer (task, data, NULL); + } } static void @@ -1076,18 +1080,18 @@ ostree_checksum_file_async (GFile *f, GAsyncReadyCallback callback, gpointer user_data) { - GSimpleAsyncResult *res; + g_autoptr(GTask) task = NULL; ChecksumFileAsyncData *data; data = g_new0 (ChecksumFileAsyncData, 1); data->f = g_object_ref (f); data->objtype = objtype; - res = g_simple_async_result_new (G_OBJECT (f), callback, user_data, ostree_checksum_file_async); - g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)checksum_file_async_data_free); - - g_simple_async_result_run_in_thread (res, checksum_file_async_thread, io_priority, cancellable); - g_object_unref (res); + task = g_task_new (G_OBJECT (f), cancellable, callback, user_data); + g_task_set_task_data (task, data, (GDestroyNotify)checksum_file_async_data_free); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, ostree_checksum_file_async); + g_task_run_in_thread (task, (GTaskThreadFunc)checksum_file_async_thread); } /** @@ -1106,15 +1110,19 @@ ostree_checksum_file_async_finish (GFile *f, guchar **out_csum, GError **error) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); ChecksumFileAsyncData *data; - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == ostree_checksum_file_async); + g_return_val_if_fail (G_IS_FILE (f), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_task_is_valid (result, f), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (result, ostree_checksum_file_async), FALSE); - if (g_simple_async_result_propagate_error (simple, error)) + data = g_task_propagate_pointer (G_TASK (result), error); + + if (data == NULL) return FALSE; - data = g_simple_async_result_get_op_res_gpointer (simple); /* Transfer ownership */ *out_csum = data->csum; data->csum = NULL; diff --git a/src/libostree/ostree-fetcher-curl.c b/src/libostree/ostree-fetcher-curl.c index 35769f8a..c63369fd 100644 --- a/src/libostree/ostree-fetcher-curl.c +++ b/src/libostree/ostree-fetcher-curl.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2016 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -50,8 +51,6 @@ #include "ostree-repo-private.h" #include "otutil.h" -#include "ostree-soup-uri.h" - typedef struct FetcherRequest FetcherRequest; typedef struct SockInfo SockInfo; @@ -183,13 +182,13 @@ _ostree_fetcher_finalize (GObject *object) g_free (self->cookie_jar_path); g_free (self->proxy); g_assert_cmpint (g_hash_table_size (self->outstanding_requests), ==, 0); - g_clear_pointer (&self->extra_headers, (GDestroyNotify)curl_slist_free_all); + g_clear_pointer (&self->extra_headers, curl_slist_free_all); g_hash_table_unref (self->outstanding_requests); g_hash_table_unref (self->sockets); - g_clear_pointer (&self->timer_event, (GDestroyNotify)destroy_and_unref_source); + g_clear_pointer (&self->timer_event, destroy_and_unref_source); if (self->mainctx) g_main_context_unref (self->mainctx); - g_clear_pointer (&self->custom_user_agent, (GDestroyNotify)g_free); + g_clear_pointer (&self->custom_user_agent, g_free); G_OBJECT_CLASS (_ostree_fetcher_parent_class)->finalize (object); } @@ -266,11 +265,11 @@ destroy_and_unref_source (GSource *source) } static char * -request_get_uri (FetcherRequest *req, SoupURI *baseuri) +request_get_uri (FetcherRequest *req, GUri *baseuri) { if (!req->filename) - return soup_uri_to_string (baseuri, FALSE); - { g_autofree char *uristr = soup_uri_to_string (baseuri, FALSE); + return g_uri_to_string_partial (baseuri, G_URI_HIDE_PASSWORD); + { g_autofree char *uristr = g_uri_to_string_partial (baseuri, G_URI_HIDE_PASSWORD); return g_build_filename (uristr, req->filename, NULL); } } @@ -424,7 +423,7 @@ static gboolean timer_cb (gpointer data) { OstreeFetcher *fetcher = data; - g_clear_pointer (&fetcher->timer_event, (GDestroyNotify)destroy_and_unref_source); + g_clear_pointer (&fetcher->timer_event, destroy_and_unref_source); (void)curl_multi_socket_action (fetcher->multi, CURL_SOCKET_TIMEOUT, 0, &fetcher->curl_running); check_multi_info (fetcher); @@ -437,7 +436,7 @@ update_timeout_cb (CURLM *multi, long timeout_ms, void *userp) { OstreeFetcher *fetcher = userp; - g_clear_pointer (&fetcher->timer_event, (GDestroyNotify)destroy_and_unref_source); + g_clear_pointer (&fetcher->timer_event, destroy_and_unref_source); if (timeout_ms != -1) { @@ -479,7 +478,7 @@ sock_unref (SockInfo *f) return; if (--f->refcount != 0) return; - g_clear_pointer (&f->ch, (GDestroyNotify)destroy_and_unref_source); + g_clear_pointer (&f->ch, destroy_and_unref_source); g_free (f); } @@ -492,7 +491,7 @@ setsock (SockInfo*f, curl_socket_t s, int act, OstreeFetcher *fetcher) f->sockfd = s; f->action = act; - g_clear_pointer (&f->ch, (GDestroyNotify)destroy_and_unref_source); + g_clear_pointer (&f->ch, destroy_and_unref_source); /* TODO - investigate new g_source_modify_unix_fd() so changing the poll * flags involves less allocation. */ @@ -648,7 +647,7 @@ request_unref (FetcherRequest *req) g_string_free (req->output_buf, TRUE); g_free (req->if_none_match); g_free (req->out_etag); - g_clear_pointer (&req->req_headers, (GDestroyNotify)curl_slist_free_all); + g_clear_pointer (&req->req_headers, curl_slist_free_all); curl_easy_cleanup (req->easy); g_free (req); @@ -705,7 +704,7 @@ _ostree_fetcher_set_extra_headers (OstreeFetcher *self, const char *key; const char *value; - g_clear_pointer (&self->extra_headers, (GDestroyNotify)curl_slist_free_all); + g_clear_pointer (&self->extra_headers, curl_slist_free_all); g_variant_iter_init (&viter, extra_headers); while (g_variant_iter_loop (&viter, "(&s&s)", &key, &value)) @@ -719,7 +718,7 @@ void _ostree_fetcher_set_extra_user_agent (OstreeFetcher *self, const char *extra_user_agent) { - g_clear_pointer (&self->custom_user_agent, (GDestroyNotify)g_free); + g_clear_pointer (&self->custom_user_agent, g_free); if (extra_user_agent) { self->custom_user_agent = @@ -763,7 +762,7 @@ initiate_next_curl_request (FetcherRequest *req, g_assert_cmpint (req->idx, <, req->mirrorlist->len); - SoupURI *baseuri = req->mirrorlist->pdata[req->idx]; + GUri *baseuri = req->mirrorlist->pdata[req->idx]; { g_autofree char *uri = request_get_uri (req, baseuri); curl_easy_setopt (req->easy, CURLOPT_URL, uri); } @@ -842,9 +841,9 @@ initiate_next_curl_request (FetcherRequest *req, curl_easy_setopt (req->easy, CURLOPT_ACCEPT_ENCODING, ""); /* If we have e.g. basic auth in the URL string, let's honor that */ - const char *username = soup_uri_get_user (baseuri); + const char *username = g_uri_get_user (baseuri); curl_easy_setopt (req->easy, CURLOPT_USERNAME, username); - const char *password = soup_uri_get_password (baseuri); + const char *password = g_uri_get_password (baseuri); curl_easy_setopt (req->easy, CURLOPT_PASSWORD, password); /* We should only speak HTTP; TODO: only enable file if specified */ diff --git a/src/libostree/ostree-fetcher-soup.c b/src/libostree/ostree-fetcher-soup.c index be87f81e..ec1d8e09 100644 --- a/src/libostree/ostree-fetcher-soup.c +++ b/src/libostree/ostree-fetcher-soup.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -153,7 +154,7 @@ thread_closure_unref (ThreadClosure *thread_closure) g_clear_pointer (&thread_closure->main_context, g_main_context_unref); - g_clear_pointer (&thread_closure->extra_headers, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&thread_closure->extra_headers, g_variant_unref); g_clear_pointer (&thread_closure->output_stream_set, g_hash_table_unref); g_mutex_clear (&thread_closure->output_stream_set_lock); @@ -329,7 +330,7 @@ session_thread_set_headers_cb (ThreadClosure *thread_closure, { GVariant *headers = data; - g_clear_pointer (&thread_closure->extra_headers, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&thread_closure->extra_headers, g_variant_unref); thread_closure->extra_headers = g_variant_ref (headers); } @@ -431,11 +432,15 @@ create_pending_soup_request (OstreeFetcherPendingURI *pending, next_mirror = g_ptr_array_index (pending->mirrorlist, pending->mirrorlist_idx); if (pending->filename) uri = _ostree_fetcher_uri_new_subpath (next_mirror, pending->filename); + if (!uri) + uri = (OstreeFetcherURI*)g_uri_ref ((GUri*)next_mirror); g_clear_object (&pending->request); - pending->request = soup_session_request_uri (pending->thread_closure->session, - (SoupURI*)(uri ? uri : next_mirror), error); + { + g_autofree gchar *uri_str = g_uri_to_string ((GUri*)uri); + pending->request = soup_session_request (pending->thread_closure->session, uri_str, error); + } /* Add caching headers. */ if (SOUP_IS_REQUEST_HTTP (pending->request) && pending->if_none_match != NULL) diff --git a/src/libostree/ostree-fetcher-uri.c b/src/libostree/ostree-fetcher-uri.c index 13bcb8f2..d5ed576d 100644 --- a/src/libostree/ostree-fetcher-uri.c +++ b/src/libostree/ostree-fetcher-uri.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011,2017 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -21,15 +22,7 @@ #include "config.h" - -#ifdef HAVE_LIBCURL -#include "ostree-soup-uri.h" -#else -#define LIBSOUP_USE_UNSTABLE_REQUEST_API -#include -#include -#include -#endif +#include #include "ostree-fetcher.h" @@ -39,21 +32,52 @@ void _ostree_fetcher_uri_free (OstreeFetcherURI *uri) { if (uri) - soup_uri_free ((SoupURI*)uri); + g_uri_unref ((GUri*)uri); } OstreeFetcherURI * _ostree_fetcher_uri_parse (const char *str, GError **error) { - SoupURI *soupuri = soup_uri_new (str); - if (soupuri == NULL) + GUri *uri = NULL; +#if GLIB_CHECK_VERSION(2, 68, 0) + uri = g_uri_parse (str, G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_ENCODED | G_URI_FLAGS_SCHEME_NORMALIZE, error); +#else + /* perform manual scheme normalization for older glib */ + uri = g_uri_parse (str, G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_ENCODED, error); + if (uri) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to parse uri: %s", str); - return NULL; + GUri *nuri = NULL; + switch (g_uri_get_port (uri)) + { + case 21: + if (!strcmp (g_uri_get_scheme (uri), "ftp")) + break; + return (OstreeFetcherURI*)uri; + case 80: + if (!strcmp (g_uri_get_scheme (uri), "http")) + break; + return (OstreeFetcherURI*)uri; + case 443: + if (!strcmp (g_uri_get_scheme (uri), "https")) + break; + return (OstreeFetcherURI*)uri; + default: + return (OstreeFetcherURI*)uri; + } + nuri = g_uri_build_with_user (g_uri_get_flags (uri), "http", + g_uri_get_user (uri), + g_uri_get_password (uri), + NULL, + g_uri_get_host (uri), -1, + g_uri_get_path (uri), + g_uri_get_query (uri), + g_uri_get_fragment (uri)); + g_uri_unref (uri); + uri = nuri; } - return (OstreeFetcherURI*)soupuri; +#endif + return (OstreeFetcherURI*)uri; } static OstreeFetcherURI * @@ -61,21 +85,31 @@ _ostree_fetcher_uri_new_path_internal (OstreeFetcherURI *uri, gboolean extend, const char *path) { - SoupURI *newuri = soup_uri_copy ((SoupURI*)uri); + GUri *guri = (GUri*)uri; + const char *opath = g_uri_get_path (guri); + g_autofree char *newpath = NULL; if (path) { if (extend) { - const char *origpath = soup_uri_get_path ((SoupURI*)uri); - g_autofree char *newpath = g_build_filename (origpath, path, NULL); - soup_uri_set_path (newuri, newpath); + newpath = g_build_filename (opath, path, NULL); + opath = newpath; } else { - soup_uri_set_path (newuri, path); + opath = path; } } - return (OstreeFetcherURI*)newuri; + return (OstreeFetcherURI*)g_uri_build_with_user (g_uri_get_flags (guri), + g_uri_get_scheme (guri), + g_uri_get_user (guri), + g_uri_get_password (guri), + NULL, + g_uri_get_host (guri), + g_uri_get_port (guri), + opath, + g_uri_get_query (guri), + g_uri_get_fragment (guri)); } OstreeFetcherURI * @@ -101,19 +135,19 @@ _ostree_fetcher_uri_clone (OstreeFetcherURI *uri) char * _ostree_fetcher_uri_get_scheme (OstreeFetcherURI *uri) { - return g_strdup (soup_uri_get_scheme ((SoupURI*)uri)); + return g_strdup (g_uri_get_scheme ((GUri*)uri)); } char * _ostree_fetcher_uri_get_path (OstreeFetcherURI *uri) { - return g_strdup (soup_uri_get_path ((SoupURI*)uri)); + return g_strdup (g_uri_get_path ((GUri*)uri)); } char * _ostree_fetcher_uri_to_string (OstreeFetcherURI *uri) { - return soup_uri_to_string ((SoupURI*)uri, FALSE); + return g_uri_to_string_partial ((GUri*)uri, G_URI_HIDE_PASSWORD); } @@ -124,7 +158,7 @@ _ostree_fetcher_uri_to_string (OstreeFetcherURI *uri) gboolean _ostree_fetcher_uri_validate (OstreeFetcherURI *uri, GError **error) { - const char *scheme = soup_uri_get_scheme ((SoupURI*)uri); + const char *scheme = g_uri_get_scheme ((GUri*)uri); // TODO only allow file if explicitly requested by a higher level if (!(g_str_equal (scheme, "http") || g_str_equal (scheme, "https") || g_str_equal (scheme, "file"))) { diff --git a/src/libostree/ostree-fetcher-util.c b/src/libostree/ostree-fetcher-util.c index 7aeb035a..450a3abe 100644 --- a/src/libostree/ostree-fetcher-util.c +++ b/src/libostree/ostree-fetcher-util.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -124,7 +125,7 @@ _ostree_fetcher_mirrored_request_to_membuf_once (OstreeFetcher *fe out: if (mainctx) g_main_context_pop_thread_default (mainctx); - g_clear_pointer (&data.result_buf, (GDestroyNotify)g_bytes_unref); + g_clear_pointer (&data.result_buf, g_bytes_unref); g_clear_pointer (&data.result_etag, g_free); return ret; } diff --git a/src/libostree/ostree-gpg-verifier.c b/src/libostree/ostree-gpg-verifier.c index 16b87c6f..1c8d9464 100644 --- a/src/libostree/ostree-gpg-verifier.c +++ b/src/libostree/ostree-gpg-verifier.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 Colin Walters * Copyright (C) 2013 Sjoerd Simons + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -53,7 +54,7 @@ ostree_gpg_verifier_finalize (GObject *object) g_list_free_full (self->keyrings, g_object_unref); if (self->key_ascii_files) g_ptr_array_unref (self->key_ascii_files); - g_clear_pointer (&self->keyring_data, (GDestroyNotify)g_ptr_array_unref); + g_clear_pointer (&self->keyring_data, g_ptr_array_unref); G_OBJECT_CLASS (_ostree_gpg_verifier_parent_class)->finalize (object); } diff --git a/src/libostree/ostree-libarchive-input-stream.c b/src/libostree/ostree-libarchive-input-stream.c index f80bd77c..3c313c1d 100644 --- a/src/libostree/ostree-libarchive-input-stream.c +++ b/src/libostree/ostree-libarchive-input-stream.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -29,12 +30,12 @@ enum { PROP_ARCHIVE }; -G_DEFINE_TYPE (OstreeLibarchiveInputStream, _ostree_libarchive_input_stream, G_TYPE_INPUT_STREAM) - struct _OstreeLibarchiveInputStreamPrivate { struct archive *archive; }; +G_DEFINE_TYPE_WITH_PRIVATE (OstreeLibarchiveInputStream, _ostree_libarchive_input_stream, G_TYPE_INPUT_STREAM) + static void ostree_libarchive_input_stream_set_property (GObject *object, guint prop_id, const GValue *value, @@ -63,8 +64,6 @@ _ostree_libarchive_input_stream_class_init (OstreeLibarchiveInputStreamClass *kl { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (OstreeLibarchiveInputStreamPrivate)); gobject_class->get_property = ostree_libarchive_input_stream_get_property; gobject_class->set_property = ostree_libarchive_input_stream_set_property; @@ -132,10 +131,7 @@ ostree_libarchive_input_stream_get_property (GObject *object, static void _ostree_libarchive_input_stream_init (OstreeLibarchiveInputStream *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, - OstreeLibarchiveInputStreamPrivate); - + self->priv = _ostree_libarchive_input_stream_get_instance_private (self); } GInputStream * diff --git a/src/libostree/ostree-lzma-compressor.c b/src/libostree/ostree-lzma-compressor.c index 3b201418..c60e6b71 100644 --- a/src/libostree/ostree-lzma-compressor.c +++ b/src/libostree/ostree-lzma-compressor.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -66,7 +67,7 @@ _ostree_lzma_compressor_finalize (GObject *object) OstreeLzmaCompressor *self = OSTREE_LZMA_COMPRESSOR (object); lzma_end (&self->lstream); - g_clear_pointer (&self->params, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&self->params, g_variant_unref); G_OBJECT_CLASS (_ostree_lzma_compressor_parent_class)->finalize (object); } diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 5b16be5b..d688e9b7 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011,2013 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -640,8 +641,8 @@ _ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite) return; glnx_tmpfile_clear (&real->tmpf); ot_checksum_clear (&real->checksum); - g_clear_pointer (&real->expected_checksum, (GDestroyNotify)g_free); - g_clear_pointer (&real->xattrs, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&real->expected_checksum, g_free); + g_clear_pointer (&real->xattrs, g_variant_unref); real->initialized = FALSE; } @@ -2584,8 +2585,6 @@ typedef struct { char *expected_checksum; GVariant *object; GCancellable *cancellable; - GSimpleAsyncResult *result; - guchar *result_csum; } WriteMetadataAsyncData; @@ -2603,19 +2602,21 @@ write_metadata_async_data_free (gpointer user_data) } static void -write_metadata_thread (GSimpleAsyncResult *res, +write_metadata_thread (GTask *task, GObject *object, + gpointer datap, GCancellable *cancellable) { GError *error = NULL; - WriteMetadataAsyncData *data; + WriteMetadataAsyncData *data = datap; - data = g_simple_async_result_get_op_res_gpointer (res); if (!ostree_repo_write_metadata (data->repo, data->objtype, data->expected_checksum, data->object, &data->result_csum, cancellable, &error)) - g_simple_async_result_take_error (res, error); + g_task_return_error (task, error); + else + g_task_return_pointer (task, data, NULL); } /** @@ -2640,6 +2641,7 @@ ostree_repo_write_metadata_async (OstreeRepo *self, GAsyncReadyCallback callback, gpointer user_data) { + g_autoptr(GTask) task = NULL; WriteMetadataAsyncData *asyncdata; asyncdata = g_new0 (WriteMetadataAsyncData, 1); @@ -2649,14 +2651,10 @@ ostree_repo_write_metadata_async (OstreeRepo *self, asyncdata->object = g_variant_ref (object); asyncdata->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - asyncdata->result = g_simple_async_result_new ((GObject*) self, - callback, user_data, - ostree_repo_write_metadata_async); - - g_simple_async_result_set_op_res_gpointer (asyncdata->result, asyncdata, - write_metadata_async_data_free); - g_simple_async_result_run_in_thread (asyncdata->result, write_metadata_thread, G_PRIORITY_DEFAULT, cancellable); - g_object_unref (asyncdata->result); + task = g_task_new (G_OBJECT (self), cancellable, callback, user_data); + g_task_set_task_data (task, asyncdata, write_metadata_async_data_free); + g_task_set_source_tag (task, ostree_repo_write_metadata_async); + g_task_run_in_thread (task, (GTaskThreadFunc)write_metadata_thread); } /** @@ -2674,15 +2672,18 @@ ostree_repo_write_metadata_finish (OstreeRepo *self, guchar **out_csum, GError **error) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); WriteMetadataAsyncData *data; - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == ostree_repo_write_metadata_async); + g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (result, ostree_repo_write_metadata_async), FALSE); - if (g_simple_async_result_propagate_error (simple, error)) + data = g_task_propagate_pointer (G_TASK (result), error); + if (data == NULL) return FALSE; - data = g_simple_async_result_get_op_res_gpointer (simple); /* Transfer ownership */ *out_csum = data->result_csum; data->result_csum = NULL; @@ -2920,7 +2921,6 @@ typedef struct { GInputStream *object; guint64 file_object_length; GCancellable *cancellable; - GSimpleAsyncResult *result; guchar *result_csum; } WriteContentAsyncData; @@ -2939,19 +2939,21 @@ write_content_async_data_free (gpointer user_data) } static void -write_content_thread (GSimpleAsyncResult *res, +write_content_thread (GTask *task, GObject *object, + gpointer datap, GCancellable *cancellable) { GError *error = NULL; - WriteContentAsyncData *data; + WriteContentAsyncData *data = datap; - data = g_simple_async_result_get_op_res_gpointer (res); if (!ostree_repo_write_content (data->repo, data->expected_checksum, data->object, data->file_object_length, &data->result_csum, cancellable, &error)) - g_simple_async_result_take_error (res, error); + g_task_return_error (task, error); + else + g_task_return_pointer (task, data, NULL); } /** @@ -2976,6 +2978,7 @@ ostree_repo_write_content_async (OstreeRepo *self, GAsyncReadyCallback callback, gpointer user_data) { + g_autoptr(GTask) task = NULL; WriteContentAsyncData *asyncdata; asyncdata = g_new0 (WriteContentAsyncData, 1); @@ -2985,14 +2988,10 @@ ostree_repo_write_content_async (OstreeRepo *self, asyncdata->file_object_length = length; asyncdata->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - asyncdata->result = g_simple_async_result_new ((GObject*) self, - callback, user_data, - ostree_repo_write_content_async); - - g_simple_async_result_set_op_res_gpointer (asyncdata->result, asyncdata, - write_content_async_data_free); - g_simple_async_result_run_in_thread (asyncdata->result, write_content_thread, G_PRIORITY_DEFAULT, cancellable); - g_object_unref (asyncdata->result); + task = g_task_new (G_OBJECT (self), cancellable, callback, user_data); + g_task_set_task_data (task, asyncdata, (GDestroyNotify)write_content_async_data_free); + g_task_set_source_tag (task, ostree_repo_write_content_async); + g_task_run_in_thread (task, (GTaskThreadFunc)write_content_thread); } /** @@ -3010,15 +3009,18 @@ ostree_repo_write_content_finish (OstreeRepo *self, guchar **out_csum, GError **error) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); WriteContentAsyncData *data; - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == ostree_repo_write_content_async); + g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (result, ostree_repo_write_content_async), FALSE); - if (g_simple_async_result_propagate_error (simple, error)) + data = g_task_propagate_pointer (G_TASK (result), error); + if (data == NULL) return FALSE; - data = g_simple_async_result_get_op_res_gpointer (simple); ot_transfer_out_value (out_csum, &data->result_csum); return TRUE; } @@ -4336,7 +4338,7 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier) if (modifier->xattr_destroy) modifier->xattr_destroy (modifier->xattr_user_data); - g_clear_pointer (&modifier->devino_cache, (GDestroyNotify)g_hash_table_unref); + g_clear_pointer (&modifier->devino_cache, g_hash_table_unref); g_clear_object (&modifier->sepolicy); diff --git a/src/libostree/ostree-repo-file.c b/src/libostree/ostree-repo-file.c index 082588a5..3d396ced 100644 --- a/src/libostree/ostree-repo-file.c +++ b/src/libostree/ostree-repo-file.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -55,8 +56,8 @@ ostree_repo_file_finalize (GObject *object) self = OSTREE_REPO_FILE (object); - g_clear_pointer (&self->tree_contents, (GDestroyNotify) g_variant_unref); - g_clear_pointer (&self->tree_metadata, (GDestroyNotify) g_variant_unref); + g_clear_pointer (&self->tree_contents, g_variant_unref); + g_clear_pointer (&self->tree_metadata, g_variant_unref); g_free (self->cached_file_checksum); g_free (self->tree_contents_checksum); g_free (self->tree_metadata_checksum); @@ -236,7 +237,7 @@ do_resolve_nonroot (OstreeRepoFile *self, files_variant = g_variant_get_child_value (self->parent->tree_contents, 0); self->index = g_variant_n_children (files_variant) + i; - g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); + g_clear_pointer (&files_variant, g_variant_unref); g_variant_get_child (container, i, "(&s@ay@ay)", &name, &contents_csum_v, &metadata_csum_v); @@ -337,7 +338,7 @@ ostree_repo_file_tree_set_metadata (OstreeRepoFile *self, const char *checksum, GVariant *metadata) { - g_clear_pointer (&self->tree_metadata, (GDestroyNotify) g_variant_unref); + g_clear_pointer (&self->tree_metadata, g_variant_unref); self->tree_metadata = g_variant_ref (metadata); g_free (self->tree_metadata_checksum); self->tree_metadata_checksum = g_strdup (checksum); @@ -414,8 +415,8 @@ ostree_repo_file_get_checksum (OstreeRepoFile *self) g_variant_get_child (files_variant, n, "(@s@ay)", NULL, &csum_bytes); } - g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); - g_clear_pointer (&dirs_variant, (GDestroyNotify) g_variant_unref); + g_clear_pointer (&files_variant, g_variant_unref); + g_clear_pointer (&dirs_variant, g_variant_unref); self->cached_file_checksum = ostree_checksum_from_bytes_v (csum_bytes); @@ -775,9 +776,9 @@ ostree_repo_file_tree_find_child (OstreeRepoFile *self, *out_container = ret_container; ret_container = NULL; } - g_clear_pointer (&ret_container, (GDestroyNotify) g_variant_unref); - g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); - g_clear_pointer (&dirs_variant, (GDestroyNotify) g_variant_unref); + g_clear_pointer (&ret_container, g_variant_unref); + g_clear_pointer (&files_variant, g_variant_unref); + g_clear_pointer (&dirs_variant, g_variant_unref); return i; } @@ -969,7 +970,7 @@ ostree_repo_file_read (GFile *file, return g_file_read (dest, cancellable, error); } - return g_steal_pointer (&ret_stream); + return (GFileInputStream *)g_steal_pointer (&ret_stream); } static void diff --git a/src/libostree/ostree-repo-finder-avahi.c b/src/libostree/ostree-repo-finder-avahi.c index 76153b01..7555df6e 100644 --- a/src/libostree/ostree-repo-finder-avahi.c +++ b/src/libostree/ostree-repo-finder-avahi.c @@ -1,6 +1,7 @@ /* * Copyright © 2016 Kinvolk GmbH * Copyright © 2017 Endless Mobile, Inc. + * Copyright © 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -53,7 +54,6 @@ #include "ostree-repo-private.h" #include "ostree-repo.h" #include "ostree-repo-finder-avahi-private.h" -#include "ostree-soup-uri.h" #include "otutil.h" #endif /* HAVE_AVAHI */ @@ -490,7 +490,7 @@ fill_refs_and_checksums_from_summary (GVariant *summary, return FALSE; } - g_clear_pointer (&ref_map, (GDestroyNotify) g_variant_iter_free); + g_clear_pointer (&ref_map, g_variant_iter_free); /* Repeat for the other collections listed in the summary. */ if (g_variant_dict_lookup (&additional_metadata, OSTREE_SUMMARY_COLLECTION_MAP, "a{sa(s(taya{sv}))}", &collection_map)) @@ -705,7 +705,7 @@ ostree_avahi_service_build_repo_finder_result (OstreeAvahiService g_autoptr(GVariant) repo_index = NULL; g_autofree gchar *repo_path = NULL; g_autoptr(GPtrArray) possible_refs = NULL; /* (element-type OstreeCollectionRef) */ - SoupURI *_uri = NULL; + GUri *_uri = NULL; g_autofree gchar *uri = NULL; g_autoptr(GError) error = NULL; gsize i; @@ -772,13 +772,9 @@ ostree_avahi_service_build_repo_finder_result (OstreeAvahiService repo_to_refs = g_hash_table_new_full (uri_and_keyring_hash, uri_and_keyring_equal, (GDestroyNotify) uri_and_keyring_free, (GDestroyNotify) g_hash_table_unref); - _uri = soup_uri_new (NULL); - soup_uri_set_scheme (_uri, "http"); - soup_uri_set_host (_uri, service->address); - soup_uri_set_port (_uri, service->port); - soup_uri_set_path (_uri, repo_path); - uri = soup_uri_to_string (_uri, FALSE); - soup_uri_free (_uri); + _uri = g_uri_build (G_URI_FLAGS_ENCODED, "http", NULL, service->address, service->port, repo_path, NULL, NULL); + uri = g_uri_to_string (_uri); + g_uri_unref (_uri); for (i = 0; i < possible_refs->len; i++) { diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 1d61faaf..4819a40a 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011,2012,2013 Colin Walters * Copyright © 2017 Endless Mobile, Inc. + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -435,7 +436,7 @@ idle_worker (gpointer user_data) scan_data = g_queue_pop_head (&pull_data->scan_object_queue); if (!scan_data) { - g_clear_pointer (&pull_data->idle_src, (GDestroyNotify) g_source_destroy); + g_clear_pointer (&pull_data->idle_src, g_source_destroy); return G_SOURCE_REMOVE; } @@ -4476,7 +4477,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, pull_data->remote_name); summary_from_cache = FALSE; - g_clear_pointer (&bytes_summary, (GDestroyNotify)g_bytes_unref); + g_clear_pointer (&bytes_summary, g_bytes_unref); g_clear_pointer (&summary_etag, g_free); summary_last_modified = 0; if (!_ostree_fetcher_mirrored_request_to_membuf (pull_data->fetcher, @@ -4544,7 +4545,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, pull_data->remote_name); summary_from_cache = FALSE; - g_clear_pointer (&bytes_summary, (GDestroyNotify)g_bytes_unref); + g_clear_pointer (&bytes_summary, g_bytes_unref); g_clear_pointer (&summary_etag, g_free); summary_last_modified = 0; if (!_ostree_fetcher_mirrored_request_to_membuf (pull_data->fetcher, @@ -5153,46 +5154,46 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_source_destroy (update_timeout); g_strfreev (configured_branches); g_clear_object (&pull_data->fetcher); - g_clear_pointer (&pull_data->extra_headers, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&pull_data->extra_headers, g_variant_unref); g_clear_object (&pull_data->cancellable); - g_clear_pointer (&pull_data->localcache_repos, (GDestroyNotify)g_ptr_array_unref); + g_clear_pointer (&pull_data->localcache_repos, g_ptr_array_unref); g_clear_object (&pull_data->remote_repo_local); g_free (pull_data->remote_refspec_name); g_free (pull_data->remote_name); g_free (pull_data->append_user_agent); - g_clear_pointer (&pull_data->signapi_commit_verifiers, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&pull_data->signapi_summary_verifiers, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&pull_data->meta_mirrorlist, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&pull_data->content_mirrorlist, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&pull_data->summary_data, (GDestroyNotify) g_bytes_unref); + g_clear_pointer (&pull_data->signapi_commit_verifiers, g_ptr_array_unref); + g_clear_pointer (&pull_data->signapi_summary_verifiers, g_ptr_array_unref); + g_clear_pointer (&pull_data->meta_mirrorlist, g_ptr_array_unref); + g_clear_pointer (&pull_data->content_mirrorlist, g_ptr_array_unref); + g_clear_pointer (&pull_data->summary_data, g_bytes_unref); g_clear_pointer (&pull_data->summary_etag, g_free); - g_clear_pointer (&pull_data->summary_data_sig, (GDestroyNotify) g_bytes_unref); + g_clear_pointer (&pull_data->summary_data_sig, g_bytes_unref); g_clear_pointer (&pull_data->summary_sig_etag, g_free); - g_clear_pointer (&pull_data->summary, (GDestroyNotify) g_variant_unref); - g_clear_pointer (&pull_data->static_delta_superblocks, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&pull_data->commit_to_depth, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->expected_commit_sizes, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->scanned_metadata, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->fetched_detached_metadata, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->summary_deltas_checksums, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->ref_original_commits, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&pull_data->summary, g_variant_unref); + g_clear_pointer (&pull_data->static_delta_superblocks, g_ptr_array_unref); + g_clear_pointer (&pull_data->commit_to_depth, g_hash_table_unref); + g_clear_pointer (&pull_data->expected_commit_sizes, g_hash_table_unref); + g_clear_pointer (&pull_data->scanned_metadata, g_hash_table_unref); + g_clear_pointer (&pull_data->fetched_detached_metadata, g_hash_table_unref); + g_clear_pointer (&pull_data->summary_deltas_checksums, g_hash_table_unref); + g_clear_pointer (&pull_data->ref_original_commits, g_hash_table_unref); g_free (pull_data->timestamp_check_from_rev); - g_clear_pointer (&pull_data->verified_commits, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->signapi_verified_commits, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->ref_keyring_map, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->requested_content, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->requested_fallback_content, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->requested_metadata, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->pending_fetch_content, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->pending_fetch_metadata, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->pending_fetch_delta_indexes, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->pending_fetch_delta_superblocks, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&pull_data->pending_fetch_deltaparts, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&pull_data->verified_commits, g_hash_table_unref); + g_clear_pointer (&pull_data->signapi_verified_commits, g_hash_table_unref); + g_clear_pointer (&pull_data->ref_keyring_map, g_hash_table_unref); + g_clear_pointer (&pull_data->requested_content, g_hash_table_unref); + g_clear_pointer (&pull_data->requested_fallback_content, g_hash_table_unref); + g_clear_pointer (&pull_data->requested_metadata, g_hash_table_unref); + g_clear_pointer (&pull_data->pending_fetch_content, g_hash_table_unref); + g_clear_pointer (&pull_data->pending_fetch_metadata, g_hash_table_unref); + g_clear_pointer (&pull_data->pending_fetch_delta_indexes, g_hash_table_unref); + g_clear_pointer (&pull_data->pending_fetch_delta_superblocks, g_hash_table_unref); + g_clear_pointer (&pull_data->pending_fetch_deltaparts, g_hash_table_unref); g_queue_foreach (&pull_data->scan_object_queue, (GFunc) scan_object_queue_data_free, NULL); g_queue_clear (&pull_data->scan_object_queue); - g_clear_pointer (&pull_data->idle_src, (GDestroyNotify) g_source_destroy); - g_clear_pointer (&pull_data->dirs, (GDestroyNotify) g_ptr_array_unref); - g_clear_pointer (&remote_config, (GDestroyNotify) g_key_file_unref); + g_clear_pointer (&pull_data->idle_src, g_source_destroy); + g_clear_pointer (&pull_data->dirs, g_ptr_array_unref); + g_clear_pointer (&remote_config, g_key_file_unref); return ret; } @@ -5838,7 +5839,7 @@ find_remotes_cb (GObject *obj, { g_debug ("%s: Failed to download summary for result ‘%s’. Ignoring. %s", G_STRFUNC, result->remote->name, error->message); - g_clear_pointer (&g_ptr_array_index (results, i), (GDestroyNotify) ostree_repo_finder_result_free); + g_clear_pointer (&g_ptr_array_index (results, i), ostree_repo_finder_result_free); g_clear_error (&error); continue; } @@ -5847,7 +5848,7 @@ find_remotes_cb (GObject *obj, g_debug ("%s: Failed to download summary for result ‘%s’. Ignoring. %s", G_STRFUNC, result->remote->name, "No summary file exists on server"); - g_clear_pointer (&g_ptr_array_index (results, i), (GDestroyNotify) ostree_repo_finder_result_free); + g_clear_pointer (&g_ptr_array_index (results, i), ostree_repo_finder_result_free); continue; } @@ -5869,7 +5870,7 @@ find_remotes_cb (GObject *obj, if (!find_remotes_process_refs (self, refs, result, i, summary_collection_id, summary_refs, commit_metadatas, refs_and_remotes_table)) { - g_clear_pointer (&g_ptr_array_index (results, i), (GDestroyNotify) ostree_repo_finder_result_free); + g_clear_pointer (&g_ptr_array_index (results, i), ostree_repo_finder_result_free); continue; } } @@ -5890,7 +5891,7 @@ find_remotes_cb (GObject *obj, if (!find_remotes_process_refs (self, refs, result, i, summary_collection_id, summary_refs, commit_metadatas, refs_and_remotes_table)) { - g_clear_pointer (&g_ptr_array_index (results, i), (GDestroyNotify) ostree_repo_finder_result_free); + g_clear_pointer (&g_ptr_array_index (results, i), ostree_repo_finder_result_free); invalid_result = TRUE; break; } diff --git a/src/libostree/ostree-repo-static-delta-processing.c b/src/libostree/ostree-repo-static-delta-processing.c index 6e093121..47e6c064 100644 --- a/src/libostree/ostree-repo-static-delta-processing.c +++ b/src/libostree/ostree-repo-static-delta-processing.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013,2014 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -285,7 +286,6 @@ typedef struct { GVariant *header; GVariant *part; GCancellable *cancellable; - GSimpleAsyncResult *result; } StaticDeltaPartExecuteAsyncData; static void @@ -301,20 +301,22 @@ static_delta_part_execute_async_data_free (gpointer user_data) } static void -static_delta_part_execute_thread (GSimpleAsyncResult *res, +static_delta_part_execute_thread (GTask *task, GObject *object, + gpointer datap, GCancellable *cancellable) { GError *error = NULL; - StaticDeltaPartExecuteAsyncData *data; + StaticDeltaPartExecuteAsyncData *data = datap; - data = g_simple_async_result_get_op_res_gpointer (res); if (!_ostree_static_delta_part_execute (data->repo, data->header, data->part, FALSE, NULL, cancellable, &error)) - g_simple_async_result_take_error (res, error); + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); } void @@ -325,6 +327,7 @@ _ostree_static_delta_part_execute_async (OstreeRepo *repo, GAsyncReadyCallback callback, gpointer user_data) { + g_autoptr(GTask) task = NULL; StaticDeltaPartExecuteAsyncData *asyncdata; asyncdata = g_new0 (StaticDeltaPartExecuteAsyncData, 1); @@ -333,14 +336,10 @@ _ostree_static_delta_part_execute_async (OstreeRepo *repo, asyncdata->part = g_variant_ref (part); asyncdata->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - asyncdata->result = g_simple_async_result_new ((GObject*) repo, - callback, user_data, - _ostree_static_delta_part_execute_async); - - g_simple_async_result_set_op_res_gpointer (asyncdata->result, asyncdata, - static_delta_part_execute_async_data_free); - g_simple_async_result_run_in_thread (asyncdata->result, static_delta_part_execute_thread, G_PRIORITY_DEFAULT, cancellable); - g_object_unref (asyncdata->result); + task = g_task_new (G_OBJECT (repo), cancellable, callback, user_data); + g_task_set_task_data (task, asyncdata, (GDestroyNotify)static_delta_part_execute_async_data_free); + g_task_set_source_tag (task, _ostree_static_delta_part_execute_async); + g_task_run_in_thread (task, (GTaskThreadFunc)static_delta_part_execute_thread); } gboolean @@ -348,13 +347,13 @@ _ostree_static_delta_part_execute_finish (OstreeRepo *repo, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_task_is_valid (result, repo), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (result, _ostree_static_delta_part_execute_async), FALSE); - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _ostree_static_delta_part_execute_async); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - return TRUE; + return g_task_propagate_boolean (G_TASK (result), error); } static gboolean diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index a27591b3..54438b78 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 Colin Walters * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -1208,8 +1209,8 @@ ostree_repo_finalize (GObject *object) g_clear_pointer (&self->txn.refs, g_hash_table_destroy); g_clear_pointer (&self->txn.collection_refs, g_hash_table_destroy); g_clear_error (&self->writable_error); - g_clear_pointer (&self->object_sizes, (GDestroyNotify) g_hash_table_unref); - g_clear_pointer (&self->dirmeta_cache, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&self->object_sizes, g_hash_table_unref); + g_clear_pointer (&self->dirmeta_cache, g_hash_table_unref); g_mutex_clear (&self->cache_lock); g_mutex_clear (&self->txn_lock); g_free (self->collection_id); @@ -2306,7 +2307,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self, goto out; } - g_clear_pointer (&data_buffer, (GDestroyNotify) gpgme_data_release); + g_clear_pointer (&data_buffer, gpgme_data_release); } /* Retrieve all keys or specific keys from the source GPGME context. @@ -3179,7 +3180,7 @@ reload_core_config (OstreeRepo *self, gboolean is_archive; gsize len; - g_clear_pointer (&self->config, (GDestroyNotify)g_key_file_unref); + g_clear_pointer (&self->config, g_key_file_unref); self->config = g_key_file_new (); contents = glnx_file_get_contents_utf8_at (self->repo_dir_fd, "config", &len, @@ -6274,7 +6275,7 @@ ostree_repo_regenerate_summary (OstreeRepo *self, g_autoptr(GHashTable) collection_map = NULL; /* (element-type utf8 GHashTable) */ g_hash_table_iter_init (&iter, collection_refs); collection_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify) g_hash_table_unref); + (GDestroyNotify)g_hash_table_unref); const OstreeCollectionRef *c_ref; const char *checksum; @@ -6594,7 +6595,7 @@ _ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state) g_mutex_lock (lock); repo->dirmeta_cache_refcount--; if (repo->dirmeta_cache_refcount == 0) - g_clear_pointer (&repo->dirmeta_cache, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&repo->dirmeta_cache, g_hash_table_unref); g_mutex_unlock (lock); g_object_unref (repo); } diff --git a/src/libostree/ostree-soup-form.c b/src/libostree/ostree-soup-form.c deleted file mode 100644 index dfaffb96..00000000 --- a/src/libostree/ostree-soup-form.c +++ /dev/null @@ -1,140 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* soup-form.c : utility functions for HTML forms */ - -/* - * Copyright 2008 Red Hat, Inc. - */ - -/* This one is stripped down to only have soup_form_encode_hash() - * and soup_form_encode_valist() which are the only bits that soup-uri.c - * calls. - */ - -#include - -#include - -#include "ostree-soup-uri.h" - -/** - * SECTION:soup-form - * @short_description: HTML form handling - * @see_also: #SoupMultipart - * - * libsoup contains several help methods for processing HTML forms as - * defined by the - * HTML 4.01 specification. - **/ - -/** - * SOUP_FORM_MIME_TYPE_URLENCODED: - * - * A macro containing the value - * "application/x-www-form-urlencoded"; the default - * MIME type for POSTing HTML form data. - * - * Since: 2.26 - **/ - -/** - * SOUP_FORM_MIME_TYPE_MULTIPART: - * - * A macro containing the value - * "multipart/form-data"; the MIME type used for - * posting form data that contains files to be uploaded. - * - * Since: 2.26 - **/ - -#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) -#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2])) - -static void -append_form_encoded (GString *str, const char *in) -{ - const unsigned char *s = (const unsigned char *)in; - - while (*s) { - if (*s == ' ') { - g_string_append_c (str, '+'); - s++; - } else if (!g_ascii_isalnum (*s) && (*s != '-') && (*s != '_') - && (*s != '.')) - g_string_append_printf (str, "%%%02X", (int)*s++); - else - g_string_append_c (str, *s++); - } -} - -static void -encode_pair (GString *str, const char *name, const char *value) -{ - g_return_if_fail (name != NULL); - g_return_if_fail (value != NULL); - - if (str->len) - g_string_append_c (str, '&'); - append_form_encoded (str, name); - g_string_append_c (str, '='); - append_form_encoded (str, value); -} - -/** - * soup_form_encode_hash: - * @form_data_set: (element-type utf8 utf8) (transfer none): a hash table containing - * name/value pairs (as strings) - * - * Encodes @form_data_set into a value of type - * "application/x-www-form-urlencoded", as defined in the HTML 4.01 - * spec. - * - * Note that the HTML spec states that "The control names/values are - * listed in the order they appear in the document." Since this method - * takes a hash table, it cannot enforce that; if you care about the - * ordering of the form fields, use soup_form_encode_datalist(). - * - * Return value: the encoded form - **/ -char * -soup_form_encode_hash (GHashTable *form_data_set) -{ - GString *str = g_string_new (NULL); - GHashTableIter iter; - gpointer name, value; - - g_hash_table_iter_init (&iter, form_data_set); - while (g_hash_table_iter_next (&iter, &name, &value)) - encode_pair (str, name, value); - return g_string_free (str, FALSE); -} - -/** - * soup_form_encode_valist: - * @first_field: name of the first form field - * @args: pointer to additional values, as in soup_form_encode() - * - * See soup_form_encode(). This is mostly an internal method, used by - * various other methods such as soup_uri_set_query_from_fields() and - * soup_form_request_new(). - * - * Return value: the encoded form - **/ -char * -soup_form_encode_valist (const char *first_field, va_list args) -{ - GString *str = g_string_new (NULL); - const char *name, *value; - - name = first_field; - value = va_arg (args, const char *); - while (name && value) { - encode_pair (str, name, value); - - name = va_arg (args, const char *); - if (name) - value = va_arg (args, const char *); - } - - return g_string_free (str, FALSE); -} diff --git a/src/libostree/ostree-soup-uri.c b/src/libostree/ostree-soup-uri.c deleted file mode 100644 index bfc5dd1a..00000000 --- a/src/libostree/ostree-soup-uri.c +++ /dev/null @@ -1,1438 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* soup-uri.c : utility functions to parse URLs */ - -/* - * Copyright 1999-2003 Ximian, Inc. - */ - -#include "config.h" - -#include -#include - -#include "ostree-soup-uri.h" - -/* OSTREECHANGE: definitions from soup-misc-private.h */ -char *soup_uri_decoded_copy (const char *str, int length, int *decoded_length); -char *soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query, - gboolean force_port); - -/* OSTREECHANGE: import soup-misc's char helpers */ -#define SOUP_CHAR_URI_PERCENT_ENCODED 0x01 -#define SOUP_CHAR_URI_GEN_DELIMS 0x02 -#define SOUP_CHAR_URI_SUB_DELIMS 0x04 -#define SOUP_CHAR_HTTP_SEPARATOR 0x08 -#define SOUP_CHAR_HTTP_CTL 0x10 - -/* 00 URI_UNRESERVED - * 01 URI_PCT_ENCODED - * 02 URI_GEN_DELIMS - * 04 URI_SUB_DELIMS - * 08 HTTP_SEPARATOR - * 10 HTTP_CTL - */ -const char soup_char_attributes[] = { - /* 0x00 - 0x07 */ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - /* 0x08 - 0x0f */ - 0x11, 0x19, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - /* 0x10 - 0x17 */ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - /* 0x18 - 0x1f */ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - /* !"#$%&' */ - 0x09, 0x04, 0x09, 0x02, 0x04, 0x01, 0x04, 0x04, - /* ()*+,-./ */ - 0x0c, 0x0c, 0x04, 0x04, 0x0c, 0x00, 0x00, 0x0a, - /* 01234567 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 89:;<=>? */ - 0x00, 0x00, 0x0a, 0x0c, 0x09, 0x0a, 0x09, 0x0a, - /* @ABCDEFG */ - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* HIJKLMNO */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* PQRSTUVW */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* XYZ[\]^_ */ - 0x00, 0x00, 0x00, 0x0a, 0x09, 0x0a, 0x01, 0x00, - /* `abcdefg */ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* hijklmno */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* pqrstuvw */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* xyz{|}~ */ - 0x00, 0x00, 0x00, 0x09, 0x01, 0x09, 0x00, 0x11, - /* 0x80 - 0xFF */ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 -}; - -#define soup_char_is_uri_percent_encoded(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_PERCENT_ENCODED) -#define soup_char_is_uri_gen_delims(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_GEN_DELIMS) -#define soup_char_is_uri_sub_delims(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_SUB_DELIMS) -#define soup_char_is_uri_unreserved(ch) (!(soup_char_attributes[(guchar)ch] & (SOUP_CHAR_URI_PERCENT_ENCODED | SOUP_CHAR_URI_GEN_DELIMS | SOUP_CHAR_URI_SUB_DELIMS))) -#define soup_char_is_token(ch) (!(soup_char_attributes[(guchar)ch] & (SOUP_CHAR_HTTP_SEPARATOR | SOUP_CHAR_HTTP_CTL))) - -/** - * soup_str_case_hash: - * @key: ASCII string to hash - * - * Hashes @key in a case-insensitive manner. - * - * Return value: the hash code. - **/ -static guint -soup_str_case_hash (gconstpointer key) -{ - const char *p = key; - guint h = g_ascii_toupper(*p); - - if (h) - for (p += 1; *p != '\0'; p++) - h = (h << 5) - h + g_ascii_toupper(*p); - - return h; -} - -/** - * SECTION:soup-uri - * @short_description: URIs - * - * A #SoupURI represents a (parsed) URI. - * - * Many applications will not need to use #SoupURI directly at all; on - * the client side, soup_message_new() takes a stringified URI, and on - * the server side, the path and query components are provided for you - * in the server callback. - **/ - -/** - * SoupURI: - * @scheme: the URI scheme (eg, "http") - * @user: a username, or %NULL - * @password: a password, or %NULL - * @host: the hostname or IP address - * @port: the port number on @host - * @path: the path on @host - * @query: a query for @path, or %NULL - * @fragment: a fragment identifier within @path, or %NULL - * - * A #SoupURI represents a (parsed) URI. #SoupURI supports RFC 3986 - * (URI Generic Syntax), and can parse any valid URI. However, libsoup - * only uses "http" and "https" URIs internally; You can use - * SOUP_URI_VALID_FOR_HTTP() to test if a #SoupURI is a valid HTTP - * URI. - * - * @scheme will always be set in any URI. It is an interned string and - * is always all lowercase. (If you parse a URI with a non-lowercase - * scheme, it will be converted to lowercase.) The macros - * %SOUP_URI_SCHEME_HTTP and %SOUP_URI_SCHEME_HTTPS provide the - * interned values for "http" and "https" and can be compared against - * URI @scheme values. - * - * @user and @password are parsed as defined in the older URI specs - * (ie, separated by a colon; RFC 3986 only talks about a single - * "userinfo" field). Note that @password is not included in the - * output of soup_uri_to_string(). libsoup does not normally use these - * fields; authentication is handled via #SoupSession signals. - * - * @host contains the hostname, and @port the port specified in the - * URI. If the URI doesn't contain a hostname, @host will be %NULL, - * and if it doesn't specify a port, @port may be 0. However, for - * "http" and "https" URIs, @host is guaranteed to be non-%NULL - * (trying to parse an http URI with no @host will return %NULL), and - * @port will always be non-0 (because libsoup knows the default value - * to use when it is not specified in the URI). - * - * @path is always non-%NULL. For http/https URIs, @path will never be - * an empty string either; if the input URI has no path, the parsed - * #SoupURI will have a @path of "/". - * - * @query and @fragment are optional for all URI types. - * soup_form_decode() may be useful for parsing @query. - * - * Note that @path, @query, and @fragment may contain - * %-encoded characters. soup_uri_new() calls - * soup_uri_normalize() on them, but not soup_uri_decode(). This is - * necessary to ensure that soup_uri_to_string() will generate a URI - * that has exactly the same meaning as the original. (In theory, - * #SoupURI should leave @user, @password, and @host partially-encoded - * as well, but this would be more annoying than useful.) - **/ - -/** - * SOUP_URI_IS_VALID: - * @uri: a #SoupURI - * - * Tests whether @uri is a valid #SoupURI; that is, that it is non-%NULL - * and its @scheme and @path members are also non-%NULL. - * - * This macro does not check whether http and https URIs have a non-%NULL - * @host member. - * - * Return value: %TRUE if @uri is valid for use. - * - * Since: 2.38 - **/ - -/** - * SOUP_URI_VALID_FOR_HTTP: - * @uri: a #SoupURI - * - * Tests if @uri is a valid #SoupURI for HTTP communication; that is, if - * it can be used to construct a #SoupMessage. - * - * Return value: %TRUE if @uri is a valid "http" or "https" URI. - * - * Since: 2.24 - **/ - -/** - * SOUP_URI_SCHEME_HTTP: - * - * "http" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - */ -/** - * SOUP_URI_SCHEME_HTTPS: - * - * "https" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - */ -/** - * SOUP_URI_SCHEME_FTP: - * - * "ftp" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - * - * Since: 2.30 - */ -/** - * SOUP_URI_SCHEME_FILE: - * - * "file" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - * - * Since: 2.30 - */ -/** - * SOUP_URI_SCHEME_DATA: - * - * "data" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - * - * Since: 2.30 - */ -/** - * SOUP_URI_SCHEME_RESOURCE: - * - * "data" as an interned string; you can compare this directly to a - * #SoupURI's scheme field using - * ==. - * - * Since: 2.42 - */ -/** - * SOUP_URI_SCHEME_WS: - * - * "ws" (WebSocket) as an interned string; you can compare this - * directly to a #SoupURI's scheme field using - * ==. - * - * Since: 2.50 - */ -/** - * SOUP_URI_SCHEME_WSS: - * - * "wss" (WebSocket over TLS) as an interned string; you can compare - * this directly to a #SoupURI's scheme field using - * ==. - * - * Since: 2.50 - */ - -struct _SoupURI { - const char *scheme; - - char *user; - char *password; - - char *host; - guint port; - - char *path; - char *query; - - char *fragment; -}; - -static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars); -static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra); - -gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS; -gpointer _SOUP_URI_SCHEME_WS, _SOUP_URI_SCHEME_WSS; -gpointer _SOUP_URI_SCHEME_FTP; -gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE; - -static inline const char * -soup_uri_parse_scheme (const char *scheme, int len) -{ - if (len == 4 && !g_ascii_strncasecmp (scheme, "http", len)) { - return SOUP_URI_SCHEME_HTTP; - } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) { - return SOUP_URI_SCHEME_HTTPS; - } else if (len == 8 && !g_ascii_strncasecmp (scheme, "resource", len)) { - return SOUP_URI_SCHEME_RESOURCE; - } else if (len == 2 && !g_ascii_strncasecmp (scheme, "ws", len)) { - return SOUP_URI_SCHEME_WS; - } else if (len == 3 && !g_ascii_strncasecmp (scheme, "wss", len)) { - return SOUP_URI_SCHEME_WSS; - } else { - char *lower_scheme; - - lower_scheme = g_ascii_strdown (scheme, len); - scheme = g_intern_static_string (lower_scheme); - if (scheme != (const char *)lower_scheme) - g_free (lower_scheme); - return scheme; - } -} - -static inline guint -soup_scheme_default_port (const char *scheme) -{ - if (scheme == SOUP_URI_SCHEME_HTTP || scheme == SOUP_URI_SCHEME_WS) - return 80; - else if (scheme == SOUP_URI_SCHEME_HTTPS || scheme == SOUP_URI_SCHEME_WSS) - return 443; - else if (scheme == SOUP_URI_SCHEME_FTP) - return 21; - else - return 0; -} - -/** - * soup_uri_new_with_base: - * @base: a base URI - * @uri_string: the URI - * - * Parses @uri_string relative to @base. - * - * Return value: a parsed #SoupURI. - **/ -SoupURI * -soup_uri_new_with_base (SoupURI *base, const char *uri_string) -{ - SoupURI *uri, fixed_base; - const char *end, *hash, *colon, *at, *path, *question; - const char *c, *hostend; - gboolean remove_dot_segments = TRUE; - int len; - - g_return_val_if_fail (uri_string != NULL, NULL); - - /* Allow a %NULL path in @base, for compatibility */ - if (base && base->scheme && !base->path) { - g_warn_if_fail (SOUP_URI_IS_VALID (base)); - - memcpy (&fixed_base, base, sizeof (SoupURI)); - fixed_base.path = ""; - base = &fixed_base; - } - - g_return_val_if_fail (base == NULL || SOUP_URI_IS_VALID (base), NULL); - - /* First some cleanup steps (which are supposed to all be no-ops, - * but...). Skip initial whitespace, strip out internal tabs and - * line breaks, and ignore trailing whitespace. - */ - while (g_ascii_isspace (*uri_string)) - uri_string++; - - len = strcspn (uri_string, "\t\n\r"); - if (uri_string[len]) { - char *clean = g_malloc (strlen (uri_string) + 1), *d; - const char *s; - - for (s = uri_string, d = clean; *s; s++) { - if (*s != '\t' && *s != '\n' && *s != '\r') - *d++ = *s; - } - *d = '\0'; - - uri = soup_uri_new_with_base (base, clean); - g_free (clean); - return uri; - } - end = uri_string + len; - while (end > uri_string && g_ascii_isspace (end[-1])) - end--; - - uri = g_slice_new0 (SoupURI); - - /* Find fragment. */ - hash = strchr (uri_string, '#'); - if (hash) { - uri->fragment = uri_normalized_copy (hash + 1, end - hash + 1, - NULL); - end = hash; - } - - /* Find scheme */ - c = uri_string; - while (c < end && (g_ascii_isalpha (*c) || - (c > uri_string && (g_ascii_isdigit (*c) || - *c == '.' || - *c == '+' || - *c == '-')))) - c++; - - if (c > uri_string && *c == ':') { - uri->scheme = soup_uri_parse_scheme (uri_string, c - uri_string); - uri_string = c + 1; - } - - if (uri_string == end && !base && !uri->fragment) { - uri->path = g_strdup (""); - return uri; - } - - /* Check for authority */ - if (strncmp (uri_string, "//", 2) == 0) { - uri_string += 2; - - path = uri_string + strcspn (uri_string, "/?#"); - if (path > end) - path = end; - at = strchr (uri_string, '@'); - if (at && at < path) { - colon = strchr (uri_string, ':'); - if (colon && colon < at) { - uri->password = soup_uri_decoded_copy (colon + 1, - at - colon - 1, NULL); - } else { - uri->password = NULL; - colon = at; - } - - uri->user = soup_uri_decoded_copy (uri_string, - colon - uri_string, NULL); - uri_string = at + 1; - } else - uri->user = uri->password = NULL; - - /* Find host and port. */ - if (*uri_string == '[') { - const char *pct; - - uri_string++; - hostend = strchr (uri_string, ']'); - if (!hostend || hostend > path) { - soup_uri_free (uri); - return NULL; - } - if (*(hostend + 1) == ':') - colon = hostend + 1; - else - colon = NULL; - - pct = memchr (uri_string, '%', hostend - uri_string); - if (!pct || (pct[1] == '2' && pct[2] == '5')) { - uri->host = soup_uri_decoded_copy (uri_string, - hostend - uri_string, NULL); - } else - uri->host = g_strndup (uri_string, hostend - uri_string); - } else { - colon = memchr (uri_string, ':', path - uri_string); - hostend = colon ? colon : path; - uri->host = soup_uri_decoded_copy (uri_string, - hostend - uri_string, NULL); - } - - if (colon && colon != path - 1) { - char *portend; - uri->port = strtoul (colon + 1, &portend, 10); - if (portend != (char *)path) { - soup_uri_free (uri); - return NULL; - } - } - - uri_string = path; - } - - /* Find query */ - question = memchr (uri_string, '?', end - uri_string); - if (question) { - uri->query = uri_normalized_copy (question + 1, - end - (question + 1), - NULL); - end = question; - } - - if (end != uri_string) { - uri->path = uri_normalized_copy (uri_string, end - uri_string, - NULL); - } - - /* Apply base URI. This is spelled out in RFC 3986. */ - if (base && !uri->scheme && uri->host) - uri->scheme = base->scheme; - else if (base && !uri->scheme) { - uri->scheme = base->scheme; - uri->user = g_strdup (base->user); - uri->password = g_strdup (base->password); - uri->host = g_strdup (base->host); - uri->port = base->port; - - if (!uri->path) { - uri->path = g_strdup (base->path); - if (!uri->query) - uri->query = g_strdup (base->query); - remove_dot_segments = FALSE; - } else if (*uri->path != '/') { - char *newpath, *last; - - last = strrchr (base->path, '/'); - if (last) { - newpath = g_strdup_printf ("%.*s%s", - (int)(last + 1 - base->path), - base->path, - uri->path); - } else - newpath = g_strdup_printf ("/%s", uri->path); - - g_free (uri->path); - uri->path = newpath; - } - } - - if (remove_dot_segments && uri->path && *uri->path) { - char *p, *q; - - /* Remove "./" where "." is a complete segment. */ - for (p = uri->path + 1; *p; ) { - if (*(p - 1) == '/' && - *p == '.' && *(p + 1) == '/') - memmove (p, p + 2, strlen (p + 2) + 1); - else - p++; - } - /* Remove "." at end. */ - if (p > uri->path + 2 && - *(p - 1) == '.' && *(p - 2) == '/') - *(p - 1) = '\0'; - - /* Remove "/../" where != ".." */ - for (p = uri->path + 1; *p; ) { - if (!strncmp (p, "../", 3)) { - p += 3; - continue; - } - q = strchr (p + 1, '/'); - if (!q) - break; - if (strncmp (q, "/../", 4) != 0) { - p = q + 1; - continue; - } - memmove (p, q + 4, strlen (q + 4) + 1); - p = uri->path + 1; - } - /* Remove "/.." at end where != ".." */ - q = strrchr (uri->path, '/'); - if (q && !strcmp (q, "/..")) { - p = q - 1; - while (p > uri->path && *p != '/') - p--; - if (strncmp (p, "/../", 4) != 0) - *(p + 1) = 0; - } - - /* Remove extraneous initial "/.."s */ - while (!strncmp (uri->path, "/../", 4)) - memmove (uri->path, uri->path + 3, strlen (uri->path) - 2); - if (!strcmp (uri->path, "/..")) - uri->path[1] = '\0'; - } - - /* HTTP-specific stuff */ - if (uri->scheme == SOUP_URI_SCHEME_HTTP || - uri->scheme == SOUP_URI_SCHEME_HTTPS) { - if (!uri->path) - uri->path = g_strdup ("/"); - if (!SOUP_URI_VALID_FOR_HTTP (uri)) { - soup_uri_free (uri); - return NULL; - } - } - - if (uri->scheme == SOUP_URI_SCHEME_FTP) { - if (!uri->host) { - soup_uri_free (uri); - return NULL; - } - } - - if (!uri->port) - uri->port = soup_scheme_default_port (uri->scheme); - if (!uri->path) - uri->path = g_strdup (""); - - return uri; -} - -/** - * soup_uri_new: - * @uri_string: (allow-none): a URI - * - * Parses an absolute URI. - * - * You can also pass %NULL for @uri_string if you want to get back an - * "empty" #SoupURI that you can fill in by hand. (You will need to - * call at least soup_uri_set_scheme() and soup_uri_set_path(), since - * those fields are required.) - * - * Return value: (nullable): a #SoupURI, or %NULL if the given string - * was found to be invalid. - **/ -SoupURI * -soup_uri_new (const char *uri_string) -{ - SoupURI *uri; - - if (!uri_string) - return g_slice_new0 (SoupURI); - - uri = soup_uri_new_with_base (NULL, uri_string); - if (!uri) - return NULL; - if (!SOUP_URI_IS_VALID (uri)) { - soup_uri_free (uri); - return NULL; - } - - return uri; -} - - -char * -soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query, - gboolean force_port) -{ - GString *str; - char *return_result; - - g_return_val_if_fail (uri != NULL, NULL); - g_warn_if_fail (SOUP_URI_IS_VALID (uri)); - - str = g_string_sized_new (40); - - if (uri->scheme && !just_path_and_query) - g_string_append_printf (str, "%s:", uri->scheme); - if (uri->host && !just_path_and_query) { - g_string_append (str, "//"); - if (uri->user) { - append_uri_encoded (str, uri->user, ":;@?/"); - g_string_append_c (str, '@'); - } - if (strchr (uri->host, ':')) { - const char *pct; - - g_string_append_c (str, '['); - pct = strchr (uri->host, '%'); - if (pct) { - g_string_append_printf (str, "%.*s%%25%s", - (int) (pct - uri->host), - uri->host, pct + 1); - } else - g_string_append (str, uri->host); - g_string_append_c (str, ']'); - } else - append_uri_encoded (str, uri->host, ":/"); - if (uri->port && (force_port || uri->port != soup_scheme_default_port (uri->scheme))) - g_string_append_printf (str, ":%u", uri->port); - if (!uri->path && (uri->query || uri->fragment)) - g_string_append_c (str, '/'); - else if ((!uri->path || !*uri->path) && - (uri->scheme == SOUP_URI_SCHEME_HTTP || - uri->scheme == SOUP_URI_SCHEME_HTTPS)) - g_string_append_c (str, '/'); - } - - if (uri->path && *uri->path) - g_string_append (str, uri->path); - else if (just_path_and_query) - g_string_append_c (str, '/'); - - if (uri->query) { - g_string_append_c (str, '?'); - g_string_append (str, uri->query); - } - if (uri->fragment && !just_path_and_query) { - g_string_append_c (str, '#'); - g_string_append (str, uri->fragment); - } - - return_result = str->str; - g_string_free (str, FALSE); - - return return_result; -} - -/** - * soup_uri_to_string: - * @uri: a #SoupURI - * @just_path_and_query: if %TRUE, output just the path and query portions - * - * Returns a string representing @uri. - * - * If @just_path_and_query is %TRUE, this concatenates the path and query - * together. That is, it constructs the string that would be needed in - * the Request-Line of an HTTP request for @uri. - * - * Note that the output will never contain a password, even if @uri - * does. - * - * Return value: a string representing @uri, which the caller must free. - **/ -char * -soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query) -{ - return soup_uri_to_string_internal (uri, just_path_and_query, FALSE); -} - -/** - * soup_uri_copy: - * @uri: a #SoupURI - * - * Copies @uri - * - * Return value: a copy of @uri, which must be freed with soup_uri_free() - **/ -SoupURI * -soup_uri_copy (SoupURI *uri) -{ - SoupURI *dup; - - g_return_val_if_fail (uri != NULL, NULL); - g_warn_if_fail (SOUP_URI_IS_VALID (uri)); - - dup = g_slice_new0 (SoupURI); - dup->scheme = uri->scheme; - dup->user = g_strdup (uri->user); - dup->password = g_strdup (uri->password); - dup->host = g_strdup (uri->host); - dup->port = uri->port; - dup->path = g_strdup (uri->path); - dup->query = g_strdup (uri->query); - dup->fragment = g_strdup (uri->fragment); - - return dup; -} - -static inline gboolean -parts_equal (const char *one, const char *two, gboolean insensitive) -{ - if (!one && !two) - return TRUE; - if (!one || !two) - return FALSE; - return insensitive ? !g_ascii_strcasecmp (one, two) : !strcmp (one, two); -} - -/** - * soup_uri_equal: - * @uri1: a #SoupURI - * @uri2: another #SoupURI - * - * Tests whether or not @uri1 and @uri2 are equal in all parts - * - * Return value: %TRUE or %FALSE - **/ -gboolean -soup_uri_equal (SoupURI *uri1, SoupURI *uri2) -{ - g_return_val_if_fail (uri1 != NULL, FALSE); - g_return_val_if_fail (uri2 != NULL, FALSE); - g_warn_if_fail (SOUP_URI_IS_VALID (uri1)); - g_warn_if_fail (SOUP_URI_IS_VALID (uri2)); - - if (uri1->scheme != uri2->scheme || - uri1->port != uri2->port || - !parts_equal (uri1->user, uri2->user, FALSE) || - !parts_equal (uri1->password, uri2->password, FALSE) || - !parts_equal (uri1->host, uri2->host, TRUE) || - !parts_equal (uri1->path, uri2->path, FALSE) || - !parts_equal (uri1->query, uri2->query, FALSE) || - !parts_equal (uri1->fragment, uri2->fragment, FALSE)) - return FALSE; - - return TRUE; -} - -/** - * soup_uri_free: - * @uri: a #SoupURI - * - * Frees @uri. - **/ -void -soup_uri_free (SoupURI *uri) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->user); - g_free (uri->password); - g_free (uri->host); - g_free (uri->path); - g_free (uri->query); - g_free (uri->fragment); - - g_slice_free (SoupURI, uri); -} - -static void -append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars) -{ - const unsigned char *s = (const unsigned char *)in; - - while (*s) { - if (soup_char_is_uri_percent_encoded (*s) || - soup_char_is_uri_gen_delims (*s) || - (extra_enc_chars && strchr (extra_enc_chars, *s))) - g_string_append_printf (str, "%%%02X", (int)*s++); - else - g_string_append_c (str, *s++); - } -} - -/** - * soup_uri_encode: - * @part: a URI part - * @escape_extra: (allow-none): additional reserved characters to - * escape (or %NULL) - * - * This %-encodes the given URI part and returns the escaped - * version in allocated memory, which the caller must free when it is - * done. - * - * Return value: the encoded URI part - **/ -char * -soup_uri_encode (const char *part, const char *escape_extra) -{ - GString *str; - char *encoded; - - g_return_val_if_fail (part != NULL, NULL); - - str = g_string_new (NULL); - append_uri_encoded (str, part, escape_extra); - encoded = str->str; - g_string_free (str, FALSE); - - return encoded; -} - -#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) -#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2])) - -char * -soup_uri_decoded_copy (const char *part, int length, int *decoded_length) -{ - unsigned char *s, *d; - char *decoded; - - g_return_val_if_fail (part != NULL, NULL); - - decoded = g_strndup (part, length); - s = d = (unsigned char *)decoded; - do { - if (*s == '%') { - if (!g_ascii_isxdigit (s[1]) || - !g_ascii_isxdigit (s[2])) { - *d++ = *s; - continue; - } - *d++ = HEXCHAR (s); - s += 2; - } else - *d++ = *s; - } while (*s++); - - if (decoded_length) - *decoded_length = d - (unsigned char *)decoded - 1; - - return decoded; -} - -/** - * soup_uri_decode: - * @part: a URI part - * - * Fully %-decodes @part. - * - * In the past, this would return %NULL if @part contained invalid - * percent-encoding, but now it just ignores the problem (as - * soup_uri_new() already did). - * - * Return value: the decoded URI part. - */ -char * -soup_uri_decode (const char *part) -{ - g_return_val_if_fail (part != NULL, NULL); - - return soup_uri_decoded_copy (part, strlen (part), NULL); -} - -static char * -uri_normalized_copy (const char *part, int length, - const char *unescape_extra) -{ - unsigned char *s, *d, c; - char *normalized = g_strndup (part, length); - gboolean need_fixup = FALSE; - - if (!unescape_extra) - unescape_extra = ""; - - s = d = (unsigned char *)normalized; - while (*s) { - if (*s == '%') { - if (!g_ascii_isxdigit (s[1]) || - !g_ascii_isxdigit (s[2])) { - *d++ = *s++; - continue; - } - - c = HEXCHAR (s); - if (soup_char_is_uri_unreserved (c) || - (c && strchr (unescape_extra, c))) { - *d++ = c; - s += 3; - } else { - /* We leave it unchanged. We used to uppercase percent-encoded - * triplets but we do not do it any more as RFC3986 Section 6.2.2.1 - * says that they only SHOULD be case normalized. - */ - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - } else { - if (!g_ascii_isgraph (*s) && - !strchr (unescape_extra, *s)) - need_fixup = TRUE; - *d++ = *s++; - } - } - *d = '\0'; - - if (need_fixup) { - GString *fixed; - - fixed = g_string_new (NULL); - s = (guchar *)normalized; - while (*s) { - if (g_ascii_isgraph (*s) || - strchr (unescape_extra, *s)) - g_string_append_c (fixed, *s); - else - g_string_append_printf (fixed, "%%%02X", (int)*s); - s++; - } - g_free (normalized); - normalized = g_string_free (fixed, FALSE); - } - - return normalized; -} - -/** - * soup_uri_normalize: - * @part: a URI part - * @unescape_extra: (allow-none): reserved characters to unescape (or %NULL) - * - * %-decodes any "unreserved" characters (or characters in - * @unescape_extra) in @part, and %-encodes any non-ASCII - * characters, spaces, and non-printing characters in @part. - * - * "Unreserved" characters are those that are not allowed to be used - * for punctuation according to the URI spec. For example, letters are - * unreserved, so soup_uri_normalize() will turn - * http://example.com/foo/b%61r into - * http://example.com/foo/bar, which is guaranteed - * to mean the same thing. However, "/" is "reserved", so - * http://example.com/foo%2Fbar would not - * be changed, because it might mean something different to the - * server. - * - * In the past, this would return %NULL if @part contained invalid - * percent-encoding, but now it just ignores the problem (as - * soup_uri_new() already did). - * - * Return value: the normalized URI part - */ -char * -soup_uri_normalize (const char *part, const char *unescape_extra) -{ - g_return_val_if_fail (part != NULL, NULL); - - return uri_normalized_copy (part, strlen (part), unescape_extra); -} - - -/** - * soup_uri_uses_default_port: - * @uri: a #SoupURI - * - * Tests if @uri uses the default port for its scheme. (Eg, 80 for - * http.) (This only works for http, https and ftp; libsoup does not know - * the default ports of other protocols.) - * - * Return value: %TRUE or %FALSE - **/ -gboolean -soup_uri_uses_default_port (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, FALSE); - g_warn_if_fail (SOUP_URI_IS_VALID (uri)); - - return uri->port == soup_scheme_default_port (uri->scheme); -} - -/** - * soup_uri_get_scheme: - * @uri: a #SoupURI - * - * Gets @uri's scheme. - * - * Return value: @uri's scheme. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_scheme (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->scheme; -} - -/** - * soup_uri_set_scheme: - * @uri: a #SoupURI - * @scheme: the URI scheme - * - * Sets @uri's scheme to @scheme. This will also set @uri's port to - * the default port for @scheme, if known. - **/ -void -soup_uri_set_scheme (SoupURI *uri, const char *scheme) -{ - g_return_if_fail (uri != NULL); - g_return_if_fail (scheme != NULL); - - uri->scheme = soup_uri_parse_scheme (scheme, strlen (scheme)); - uri->port = soup_scheme_default_port (uri->scheme); -} - -/** - * soup_uri_get_user: - * @uri: a #SoupURI - * - * Gets @uri's user. - * - * Return value: @uri's user. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_user (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->user; -} - -/** - * soup_uri_set_user: - * @uri: a #SoupURI - * @user: (allow-none): the username, or %NULL - * - * Sets @uri's user to @user. - **/ -void -soup_uri_set_user (SoupURI *uri, const char *user) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->user); - uri->user = g_strdup (user); -} - -/** - * soup_uri_get_password: - * @uri: a #SoupURI - * - * Gets @uri's password. - * - * Return value: @uri's password. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_password (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->password; -} - -/** - * soup_uri_set_password: - * @uri: a #SoupURI - * @password: (allow-none): the password, or %NULL - * - * Sets @uri's password to @password. - **/ -void -soup_uri_set_password (SoupURI *uri, const char *password) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->password); - uri->password = g_strdup (password); -} - -/** - * soup_uri_get_host: - * @uri: a #SoupURI - * - * Gets @uri's host. - * - * Return value: @uri's host. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_host (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->host; -} - -/** - * soup_uri_set_host: - * @uri: a #SoupURI - * @host: (allow-none): the hostname or IP address, or %NULL - * - * Sets @uri's host to @host. - * - * If @host is an IPv6 IP address, it should not include the brackets - * required by the URI syntax; they will be added automatically when - * converting @uri to a string. - * - * http and https URIs should not have a %NULL @host. - **/ -void -soup_uri_set_host (SoupURI *uri, const char *host) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->host); - uri->host = g_strdup (host); -} - -/** - * soup_uri_get_port: - * @uri: a #SoupURI - * - * Gets @uri's port. - * - * Return value: @uri's port. - * - * Since: 2.32 - **/ -guint -soup_uri_get_port (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, 0); - - return uri->port; -} - -/** - * soup_uri_set_port: - * @uri: a #SoupURI - * @port: the port, or 0 - * - * Sets @uri's port to @port. If @port is 0, @uri will not have an - * explicitly-specified port. - **/ -void -soup_uri_set_port (SoupURI *uri, guint port) -{ - g_return_if_fail (uri != NULL); - - uri->port = port; -} - -/** - * soup_uri_get_path: - * @uri: a #SoupURI - * - * Gets @uri's path. - * - * Return value: @uri's path. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_path (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->path; -} - -/** - * soup_uri_set_path: - * @uri: a #SoupURI - * @path: the non-%NULL path - * - * Sets @uri's path to @path. - **/ -void -soup_uri_set_path (SoupURI *uri, const char *path) -{ - g_return_if_fail (uri != NULL); - - /* We allow a NULL path for compatibility, but warn about it. */ - if (!path) { - g_warn_if_fail (path != NULL); - path = ""; - } - - g_free (uri->path); - uri->path = g_strdup (path); -} - -/** - * soup_uri_get_query: - * @uri: a #SoupURI - * - * Gets @uri's query. - * - * Return value: @uri's query. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_query (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->query; -} - -/** - * soup_uri_set_query: - * @uri: a #SoupURI - * @query: (allow-none): the query - * - * Sets @uri's query to @query. - **/ -void -soup_uri_set_query (SoupURI *uri, const char *query) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->query); - uri->query = g_strdup (query); -} - -/** - * soup_uri_set_query_from_form: - * @uri: a #SoupURI - * @form: (element-type utf8 utf8) (transfer none): a #GHashTable containing HTML form - * information - * - * Sets @uri's query to the result of encoding @form according to the - * HTML form rules. See soup_form_encode_hash() for more information. - **/ -void -soup_uri_set_query_from_form (SoupURI *uri, GHashTable *form) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->query); - uri->query = soup_form_encode_hash (form); -} - -/** - * soup_uri_set_query_from_fields: - * @uri: a #SoupURI - * @first_field: name of the first form field to encode into query - * @...: value of @first_field, followed by additional field names - * and values, terminated by %NULL. - * - * Sets @uri's query to the result of encoding the given form fields - * and values according to the * HTML form rules. See - * soup_form_encode() for more information. - **/ -void -soup_uri_set_query_from_fields (SoupURI *uri, - const char *first_field, - ...) -{ - va_list args; - - g_return_if_fail (uri != NULL); - - g_free (uri->query); - va_start (args, first_field); - uri->query = soup_form_encode_valist (first_field, args); - va_end (args); -} - -/** - * soup_uri_get_fragment: - * @uri: a #SoupURI - * - * Gets @uri's fragment. - * - * Return value: @uri's fragment. - * - * Since: 2.32 - **/ -const char * -soup_uri_get_fragment (SoupURI *uri) -{ - g_return_val_if_fail (uri != NULL, NULL); - - return uri->fragment; -} - -/** - * soup_uri_set_fragment: - * @uri: a #SoupURI - * @fragment: (allow-none): the fragment - * - * Sets @uri's fragment to @fragment. - **/ -void -soup_uri_set_fragment (SoupURI *uri, const char *fragment) -{ - g_return_if_fail (uri != NULL); - - g_free (uri->fragment); - uri->fragment = g_strdup (fragment); -} - -/** - * soup_uri_copy_host: - * @uri: a #SoupURI - * - * Makes a copy of @uri, considering only the protocol, host, and port - * - * Return value: the new #SoupURI - * - * Since: 2.28 - **/ -SoupURI * -soup_uri_copy_host (SoupURI *uri) -{ - SoupURI *dup; - - g_return_val_if_fail (uri != NULL, NULL); - g_warn_if_fail (SOUP_URI_IS_VALID (uri)); - - dup = soup_uri_new (NULL); - dup->scheme = uri->scheme; - dup->host = g_strdup (uri->host); - dup->port = uri->port; - dup->path = g_strdup (""); - - return dup; -} - -/** - * soup_uri_host_hash: - * @key: (type Soup.URI): a #SoupURI with a non-%NULL @host member - * - * Hashes @key, considering only the scheme, host, and port. - * - * Return value: a hash - * - * Since: 2.28 - **/ -guint -soup_uri_host_hash (gconstpointer key) -{ - const SoupURI *uri = key; - - g_return_val_if_fail (uri != NULL && uri->host != NULL, 0); - g_warn_if_fail (SOUP_URI_IS_VALID (uri)); - - return GPOINTER_TO_UINT (uri->scheme) + uri->port + - soup_str_case_hash (uri->host); -} - -/** - * soup_uri_host_equal: - * @v1: (type Soup.URI): a #SoupURI with a non-%NULL @host member - * @v2: (type Soup.URI): a #SoupURI with a non-%NULL @host member - * - * Compares @v1 and @v2, considering only the scheme, host, and port. - * - * Return value: whether or not the URIs are equal in scheme, host, - * and port. - * - * Since: 2.28 - **/ -gboolean -soup_uri_host_equal (gconstpointer v1, gconstpointer v2) -{ - const SoupURI *one = v1; - const SoupURI *two = v2; - - g_return_val_if_fail (one != NULL && two != NULL, one == two); - g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host); - g_warn_if_fail (SOUP_URI_IS_VALID (one)); - g_warn_if_fail (SOUP_URI_IS_VALID (two)); - - if (one->scheme != two->scheme) - return FALSE; - if (one->port != two->port) - return FALSE; - - return g_ascii_strcasecmp (one->host, two->host) == 0; -} - -/* OSTREECHANGE: drop boxed type definition */ -/* G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free) */ diff --git a/src/libostree/ostree-soup-uri.h b/src/libostree/ostree-soup-uri.h deleted file mode 100644 index 650b7efc..00000000 --- a/src/libostree/ostree-soup-uri.h +++ /dev/null @@ -1,147 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* - * Copyright 1999-2002 Ximian, Inc. - */ - -/* NOTE - taken from the libsoup codebase for use by the ostree curl backend - * (yes, ironically enough). - * - * Please watch for future changes in libsoup. - */ - - -#ifndef SOUP_URI_H -#define SOUP_URI_H 1 - -/* OSTREECHANGE: make struct private - * Only include gio, and skip available definitions. - */ -#include -#define SOUP_AVAILABLE_IN_2_4 -#define SOUP_AVAILABLE_IN_2_28 -#define SOUP_AVAILABLE_IN_2_32 - -G_BEGIN_DECLS - -/* OSTREECHANGE: make struct private */ -typedef struct _SoupURI SoupURI; - -/* OSTREECHANGE: import soup-misc's interning */ -#define SOUP_VAR extern -#define _SOUP_ATOMIC_INTERN_STRING(variable, value) ((const char *)(g_atomic_pointer_get (&(variable)) ? (variable) : (g_atomic_pointer_set (&(variable), (gpointer)g_intern_static_string (value)), (variable)))) -#define SOUP_URI_SCHEME_HTTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http") -#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https") -#define SOUP_URI_SCHEME_FTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp") -#define SOUP_URI_SCHEME_FILE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file") -#define SOUP_URI_SCHEME_DATA _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data") -#define SOUP_URI_SCHEME_RESOURCE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_RESOURCE, "resource") -#define SOUP_URI_SCHEME_WS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_WS, "ws") -#define SOUP_URI_SCHEME_WSS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_WSS, "wss") - -/* OSTREECHANGE: import soup-form bits */ -SOUP_AVAILABLE_IN_2_4 -char *soup_form_encode_hash (GHashTable *form_data_set); -SOUP_AVAILABLE_IN_2_4 -char *soup_form_encode_valist (const char *first_field, - va_list args); - -SOUP_VAR gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS; -SOUP_VAR gpointer _SOUP_URI_SCHEME_FTP; -SOUP_VAR gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE; -SOUP_VAR gpointer _SOUP_URI_SCHEME_WS, _SOUP_URI_SCHEME_WSS; - -SOUP_AVAILABLE_IN_2_4 -SoupURI *soup_uri_new_with_base (SoupURI *base, - const char *uri_string); -SOUP_AVAILABLE_IN_2_4 -SoupURI *soup_uri_new (const char *uri_string); - -SOUP_AVAILABLE_IN_2_4 -char *soup_uri_to_string (SoupURI *uri, - gboolean just_path_and_query); - -SOUP_AVAILABLE_IN_2_4 -SoupURI *soup_uri_copy (SoupURI *uri); - -SOUP_AVAILABLE_IN_2_4 -gboolean soup_uri_equal (SoupURI *uri1, - SoupURI *uri2); - -SOUP_AVAILABLE_IN_2_4 -void soup_uri_free (SoupURI *uri); - -SOUP_AVAILABLE_IN_2_4 -char *soup_uri_encode (const char *part, - const char *escape_extra); -SOUP_AVAILABLE_IN_2_4 -char *soup_uri_decode (const char *part); -SOUP_AVAILABLE_IN_2_4 -char *soup_uri_normalize (const char *part, - const char *unescape_extra); - -SOUP_AVAILABLE_IN_2_4 -gboolean soup_uri_uses_default_port (SoupURI *uri); - -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_scheme (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_scheme (SoupURI *uri, - const char *scheme); -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_user (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_user (SoupURI *uri, - const char *user); -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_password (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_password (SoupURI *uri, - const char *password); -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_host (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_host (SoupURI *uri, - const char *host); -SOUP_AVAILABLE_IN_2_32 -guint soup_uri_get_port (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_port (SoupURI *uri, - guint port); -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_path (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_path (SoupURI *uri, - const char *path); -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_query (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_query (SoupURI *uri, - const char *query); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_query_from_form (SoupURI *uri, - GHashTable *form); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_query_from_fields (SoupURI *uri, - const char *first_field, - ...) G_GNUC_NULL_TERMINATED; -SOUP_AVAILABLE_IN_2_32 -const char *soup_uri_get_fragment (SoupURI *uri); -SOUP_AVAILABLE_IN_2_4 -void soup_uri_set_fragment (SoupURI *uri, - const char *fragment); - -SOUP_AVAILABLE_IN_2_28 -SoupURI *soup_uri_copy_host (SoupURI *uri); -SOUP_AVAILABLE_IN_2_28 -guint soup_uri_host_hash (gconstpointer key); -SOUP_AVAILABLE_IN_2_28 -gboolean soup_uri_host_equal (gconstpointer v1, - gconstpointer v2); - -#define SOUP_URI_IS_VALID(uri) ((uri) && (uri)->scheme && (uri)->path) -#define SOUP_URI_VALID_FOR_HTTP(uri) ((uri) && ((uri)->scheme == SOUP_URI_SCHEME_HTTP || (uri)->scheme == SOUP_URI_SCHEME_HTTPS) && (uri)->host && (uri)->path) - -G_END_DECLS - -#endif /*SOUP_URI_H*/ diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 266a2975..0e0521da 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -82,7 +83,7 @@ ostree_sysroot_finalize (GObject *object) g_clear_pointer (&self->deployments, g_ptr_array_unref); g_clear_object (&self->booted_deployment); g_clear_object (&self->staged_deployment); - g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&self->staged_deployment_data, g_variant_unref); glnx_release_lock_file (&self->lock); @@ -1016,7 +1017,7 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self, g_assert (self->booted_deployment); g_clear_object (&self->staged_deployment); - g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&self->staged_deployment_data, g_variant_unref); /* Read the staged state from disk */ glnx_autofd int fd = -1; diff --git a/src/libotutil/ot-checksum-instream.c b/src/libotutil/ot-checksum-instream.c index 6555eb8a..e89d68e4 100644 --- a/src/libotutil/ot-checksum-instream.c +++ b/src/libotutil/ot-checksum-instream.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -22,12 +23,12 @@ #include "ot-checksum-instream.h" #include "ot-checksum-utils.h" -G_DEFINE_TYPE (OtChecksumInstream, ot_checksum_instream, G_TYPE_FILTER_INPUT_STREAM) - struct _OtChecksumInstreamPrivate { OtChecksum checksum; }; +G_DEFINE_TYPE_WITH_PRIVATE (OtChecksumInstream, ot_checksum_instream, G_TYPE_FILTER_INPUT_STREAM) + static gssize ot_checksum_instream_read (GInputStream *stream, void *buffer, gsize count, @@ -50,8 +51,6 @@ ot_checksum_instream_class_init (OtChecksumInstreamClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - g_type_class_add_private (klass, sizeof (OtChecksumInstreamPrivate)); - object_class->finalize = ot_checksum_instream_finalize; stream_class->read_fn = ot_checksum_instream_read; } @@ -59,7 +58,7 @@ ot_checksum_instream_class_init (OtChecksumInstreamClass *klass) static void ot_checksum_instream_init (OtChecksumInstream *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, OT_TYPE_CHECKSUM_INSTREAM, OtChecksumInstreamPrivate); + self->priv = ot_checksum_instream_get_instance_private (self); } OtChecksumInstream * diff --git a/src/libotutil/ot-gpg-utils.c b/src/libotutil/ot-gpg-utils.c index 63d6e503..fd65e120 100644 --- a/src/libotutil/ot-gpg-utils.c +++ b/src/libotutil/ot-gpg-utils.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -129,7 +130,7 @@ ot_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx, goto out; /* Sneaky cast from GFileOutputStream to GOutputStream. */ - *out_pubring_stream = g_steal_pointer (&pubring_stream); + *out_pubring_stream = (GOutputStream *)g_steal_pointer (&pubring_stream); } if (out_tmp_home_dir != NULL) diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c index 8e3266a1..042b157d 100644 --- a/src/ostree/ot-builtin-fsck.c +++ b/src/ostree/ot-builtin-fsck.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -419,7 +420,7 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation, } } - g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref); + g_clear_pointer (&objects, g_hash_table_unref); if (!opt_quiet) g_print ("Verifying content integrity of %u commit objects...\n", diff --git a/src/ostree/ot-builtin-prune.c b/src/ostree/ot-builtin-prune.c index b2dd407a..73dfe5e5 100644 --- a/src/ostree/ot-builtin-prune.c +++ b/src/ostree/ot-builtin-prune.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Colin Walters + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -133,7 +134,7 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum, g_free (next_checksum); next_checksum = ostree_commit_get_parent (commit); if (next_checksum) - g_clear_pointer (&commit, (GDestroyNotify)g_variant_unref); + g_clear_pointer (&commit, g_variant_unref); else break; /* No parent, we're done */ } diff --git a/tests/repo-finder-mount.c b/tests/repo-finder-mount.c index 184148cc..66c4ecd7 100644 --- a/tests/repo-finder-mount.c +++ b/tests/repo-finder-mount.c @@ -1,5 +1,6 @@ /* * Copyright © 2017 Endless Mobile, Inc. + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -47,7 +48,7 @@ result_cb (GObject *source_object, static void collection_ref_free0 (OstreeCollectionRef *ref) { - g_clear_pointer (&ref, (GDestroyNotify) ostree_collection_ref_free); + g_clear_pointer (&ref, ostree_collection_ref_free); } int diff --git a/tests/test-mock-gio.c b/tests/test-mock-gio.c index bf7f5fcb..719dc6cd 100644 --- a/tests/test-mock-gio.c +++ b/tests/test-mock-gio.c @@ -1,5 +1,6 @@ /* * Copyright © 2017 Endless Mobile, Inc. + * Copyright (C) 2022 Igalia S.L. * * SPDX-License-Identifier: LGPL-2.0+ * @@ -136,7 +137,7 @@ ostree_mock_volume_monitor_new (GList *mounts, monitor->mounts = g_list_copy_deep (mounts, (GCopyFunc) g_object_ref, NULL); monitor->volumes = g_list_copy_deep (volumes, (GCopyFunc) g_object_ref, NULL); - return g_steal_pointer (&monitor); + return (GVolumeMonitor *)g_steal_pointer (&monitor); } /* Mock volume class. This returns a static set of data to the caller, which it From 089b8210989411a5d6602beec1636e17204850a3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 14 Mar 2022 09:21:15 -0400 Subject: [PATCH 15/35] ci: Disable Ubuntu LTS It doesn't have a new enough glib. --- .github/workflows/tests.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76b3967b..13e33535 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -94,11 +94,12 @@ jobs: # the devel tag, which is the unreleased version. # # https://hub.docker.com/_/ubuntu - - name: Ubuntu Latest LTS - image: ubuntu:latest - pre-checkout-setup: | - apt-get update - apt-get install -y git + # For now, this is disabled because its glib version is too old. + # - name: Ubuntu Latest LTS + # image: ubuntu:latest + # pre-checkout-setup: | + # apt-get update + # apt-get install -y git - name: Ubuntu Latest Release image: ubuntu:rolling From e6f92f76fad0bae435088f649797fc02cd93b458 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 14 Mar 2022 09:27:07 -0400 Subject: [PATCH 16/35] ci: Update docs workflow to use fcos-buildroot This way we can also use `./ci/installdeps.sh` which avoids yet another list of dependencies. --- .github/workflows/docs.yml | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 74f5e9d6..fe120fde 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,6 +16,7 @@ jobs: docs: name: Build documentation runs-on: ubuntu-latest + container: registry.ci.openshift.org/coreos/fcos-buildroot:testing-devel steps: - name: Checkout repository uses: actions/checkout@v2 @@ -23,39 +24,7 @@ jobs: # This is taken from ci/travis-install.sh but should probably be # refactored. - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - attr \ - bison \ - cpio \ - debhelper \ - dh-autoreconf \ - dh-systemd \ - docbook-xml \ - docbook-xsl \ - e2fslibs-dev \ - elfutils \ - fuse \ - gjs \ - gnome-desktop-testing \ - gobject-introspection \ - gtk-doc-tools \ - libarchive-dev \ - libattr1-dev \ - libcap-dev \ - libfuse-dev \ - libgirepository1.0-dev \ - libglib2.0-dev \ - libgpgme11-dev \ - liblzma-dev \ - libmount-dev \ - libselinux1-dev \ - libsoup2.4-dev \ - libcurl4-openssl-dev \ - procps \ - zlib1g-dev \ - python3-yaml + run: ./ci/installdeps.sh - name: Build API docs run: | From afdc84b97bb6571a3ff6a4e6bc2c320efb8afaf9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 11 Mar 2022 15:37:15 -0500 Subject: [PATCH 17/35] Update to sh-inline 0.2 Syncing up with the latest. --- tests/inst/Cargo.toml | 2 +- tests/inst/src/destructive.rs | 24 ++++++++++++------------ tests/inst/src/repobin.rs | 12 ++++++------ tests/inst/src/treegen.rs | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/inst/Cargo.toml b/tests/inst/Cargo.toml index 3735c1d5..70e56dc5 100644 --- a/tests/inst/Cargo.toml +++ b/tests/inst/Cargo.toml @@ -14,7 +14,7 @@ structopt = "0.3" serde = "1.0.111" serde_derive = "1.0.111" serde_json = "1.0" -sh-inline = "0.1.0" +sh-inline = "0.2.0" anyhow = "1.0" tempfile = "3.1.0" ostree-ext = { version = "0.6.0" } diff --git a/tests/inst/src/destructive.rs b/tests/inst/src/destructive.rs index 98e1c687..352ee6d5 100644 --- a/tests/inst/src/destructive.rs +++ b/tests/inst/src/destructive.rs @@ -152,7 +152,7 @@ fn generate_update(commit: &str) -> Result<()> { // but traversing all the objects is expensive. So here we only prune 1/5 of the time. if rand::thread_rng().gen_ratio(1, 5) { bash!( - "ostree --repo={srvrepo} prune --refs-only --depth=1", + "ostree --repo=${srvrepo} prune --refs-only --depth=1", srvrepo = SRVREPO )?; } @@ -166,10 +166,10 @@ fn generate_update(commit: &str) -> Result<()> { fn generate_srv_repo(commit: &str) -> Result<()> { bash!( r#" - ostree --repo={srvrepo} init --mode=archive - ostree --repo={srvrepo} config set archive.zlib-level 1 - ostree --repo={srvrepo} pull-local /sysroot/ostree/repo {commit} - ostree --repo={srvrepo} refs --create={testref} {commit} + ostree --repo=${srvrepo} init --mode=archive + ostree --repo=${srvrepo} config set archive.zlib-level 1 + ostree --repo=${srvrepo} pull-local /sysroot/ostree/repo ${commit} + ostree --repo=${srvrepo} refs --create=${testref} ${commit} "#, srvrepo = SRVREPO, commit = commit, @@ -310,7 +310,7 @@ fn parse_and_validate_reboot_mark>( fn validate_pending_commit(pending_commit: &str, commitstates: &CommitStates) -> Result<()> { if pending_commit != commitstates.target { bash!("rpm-ostree status -v")?; - bash!("ostree show {pending_commit}", pending_commit)?; + bash!("ostree show ${pending_commit}", pending_commit)?; anyhow::bail!( "Expected target commit={} but pending={} ({:?})", commitstates.target, @@ -463,11 +463,11 @@ fn impl_transaction_test>( " systemctl stop rpm-ostreed systemctl stop ostree-finalize-staged - ostree reset testrepo:{testref} {booted_commit} + ostree reset testrepo:${testref} ${booted_commit} rpm-ostree cleanup -pbrm ", testref, - booted_commit, + booted_commit ) .with_context(|| { format!( @@ -571,7 +571,7 @@ pub(crate) fn itest_transactionality() -> Result<()> { let url = format!("http://{}", addr); bash!( "ostree remote delete --if-exists testrepo - ostree remote add --set=gpg-verify=false testrepo {url}", + ostree remote add --set=gpg-verify=false testrepo ${url}", url )?; @@ -589,9 +589,9 @@ pub(crate) fn itest_transactionality() -> Result<()> { let testref = TESTREF; bash!( " - ostree admin set-origin testrepo {url} {testref} - ostree refs --create testrepo:{testref} {commit} - ostree refs --create={origref} {commit} + ostree admin set-origin testrepo ${url} ${testref} + ostree refs --create testrepo:${testref} ${commit} + ostree refs --create=${origref} ${commit} ", url, origref, diff --git a/tests/inst/src/repobin.rs b/tests/inst/src/repobin.rs index 582b0290..4048b76a 100644 --- a/tests/inst/src/repobin.rs +++ b/tests/inst/src/repobin.rs @@ -72,14 +72,14 @@ pub(crate) fn itest_pull_basicauth() -> Result<()> { let osroot = Path::new("osroot"); crate::treegen::mkroot(&osroot)?; bash!( - r#"ostree --repo={serverrepo} init --mode=archive - ostree --repo={serverrepo} commit -b os --tree=dir={osroot} >/dev/null + r#"ostree --repo=${serverrepo} init --mode=archive + ostree --repo=${serverrepo} commit -b os --tree=dir=${osroot} >/dev/null mkdir client cd client ostree --repo=repo init --mode=archive - ostree --repo=repo remote add --set=gpg-verify=false origin-unauth {baseuri} - ostree --repo=repo remote add --set=gpg-verify=false origin-badauth {unauthuri} - ostree --repo=repo remote add --set=gpg-verify=false origin-goodauth {authuri} + ostree --repo=repo remote add --set=gpg-verify=false origin-unauth ${baseuri} + ostree --repo=repo remote add --set=gpg-verify=false origin-badauth ${unauthuri} + ostree --repo=repo remote add --set=gpg-verify=false origin-goodauth ${authuri} "#, osroot = osroot, serverrepo = serverrepo, @@ -90,7 +90,7 @@ pub(crate) fn itest_pull_basicauth() -> Result<()> { for rem in &["unauth", "badauth"] { cmd_fails_with( bash_command!( - r#"ostree --repo=client/repo pull origin-{rem} os >/dev/null"#, + r#"ostree --repo=client/repo pull origin-${rem} os >/dev/null"#, rem = *rem ) .unwrap(), diff --git a/tests/inst/src/treegen.rs b/tests/inst/src/treegen.rs index 2ec4e849..b6a3a704 100644 --- a/tests/inst/src/treegen.rs +++ b/tests/inst/src/treegen.rs @@ -140,7 +140,7 @@ pub(crate) fn update_os_tree>( } assert!(mutated > 0); println!("Mutated ELF files: {}", mutated); - bash!("ostree --repo={repo} commit --consume -b {ostref} --base={ostref} --tree=dir={tempdir} --owner-uid 0 --owner-gid 0 --selinux-policy-from-base --link-checkout-speedup --no-bindings --no-xattrs", + bash!("ostree --repo=${repo} commit --consume -b ${ostref} --base=${ostref} --tree=dir=${tempdir} --owner-uid 0 --owner-gid 0 --selinux-policy-from-base --link-checkout-speedup --no-bindings --no-xattrs", repo = repo_path, ostref = ostref, tempdir = tempdir.path()).context("Failed to commit updated content")?; From 1898019face400b84b504c199a5a3de27969103d Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sat, 26 Mar 2022 10:50:45 +0100 Subject: [PATCH 18/35] tests/test-cli-extensions: Fix with single-binary coreutils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On systems where `coreutils` are built with `--enable-single-binary=symlinks` like Nix, `/usr/bin/env` is symlinked to `/usr/bin/coreutils` and uses `argv[0]` to determine which program to run. Since the `test-cli-extensions.sh` created a new symlink named `ostree-env`, coreutils would be confused about the utility to choose, so running it would fail: ostree-env: unknown program ‘ostree-env’ Try 'ostree-env --help' for more information. Fixes: https://github.com/ostreedev/ostree/issues/2553 --- tests/test-cli-extensions.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test-cli-extensions.sh b/tests/test-cli-extensions.sh index e1916036..edce0c9d 100755 --- a/tests/test-cli-extensions.sh +++ b/tests/test-cli-extensions.sh @@ -15,7 +15,9 @@ echo '1..2' mkdir -p ./localbin ORIG_PATH="${PATH}" export PATH="./localbin/:${PATH}" -ln -s /usr/bin/env ./localbin/ostree-env +echo '#!/bin/sh' >> ./localbin/ostree-env +echo 'env "$@"' >> ./localbin/ostree-env +chmod +x ./localbin/ostree-env export A_CUSTOM_TEST_FLAG="myvalue" ${CMD_PREFIX} ostree env >out.txt assert_file_has_content out.txt "^A_CUSTOM_TEST_FLAG=myvalue" From edb4f3893474736156c654aa43bdbf3784991811 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Wed, 9 Mar 2022 15:27:11 +0100 Subject: [PATCH 19/35] deploy: Try to rebuild policy in new deployment if needed Whenever the user has SELinux enabled and has any local modules/modifications installed, it is necessary to rebuild the policy in the final deployment, otherwise ostree will leave the binary policy files unchanged from last deployment as it detects difference against the base content (in rpm-ostree case this is the RPM content). To avoid the situation where the policy binaries go stale once any local customization of the policy is made, try to rebuild the policy as part of sysroot_finalize_deployment(). Use the special --rebuild-if-modules-changed switch, which detects if the input module files have changed relative to last time the policy was built and skips the most time-consuming part of the rebuild process if modules are unchanged (thus making this a relatively cheap operation if the user hasn't made any modifications to the shipped policy). As suggested by Jonathan Lebon, this uses bubblewrap (via g_spawn_sync()) to perform the rebuild inside the deployment's filesystem tree, which also means that ostree will have a runtime dependency on bubblewrap. Partially addresses: https://github.com/coreos/fedora-coreos-tracker/issues/701 Signed-off-by: Ondrej Mosnacek --- ci/gh-install.sh | 1 + src/libostree/ostree-sysroot-deploy.c | 117 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/ci/gh-install.sh b/ci/gh-install.sh index f39331b1..c2928665 100755 --- a/ci/gh-install.sh +++ b/ci/gh-install.sh @@ -69,6 +69,7 @@ case "$ID" in automake bison build-essential + bubblewrap ca-certificates cpio debhelper diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index b7cc232f..3f301891 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -2830,6 +2830,118 @@ get_var_dfd (OstreeSysroot *self, return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error); } +#ifdef HAVE_SELINUX +static void +child_setup_fchdir (gpointer data) +{ + int fd = (int) (uintptr_t) data; + int rc __attribute__((unused)); + + rc = fchdir (fd); +} + +/* + * Derived from rpm-ostree's rust/src/bwrap.rs + */ +static gboolean +run_in_deployment (int deployment_dfd, + const gchar * const *child_argv, + gsize child_argc, + gint *exit_status, + gchar **stdout, + GError **error) +{ + static const gchar * const COMMON_ARGV[] = { + "/usr/bin/bwrap", + "--dev", "/dev", "--proc", "/proc", "--dir", "/run", "--dir", "/tmp", + "--chdir", "/", + "--die-with-parent", + "--unshare-pid", + "--unshare-uts", + "--unshare-ipc", + "--unshare-cgroup-try", + "--ro-bind", "/sys/block", "/sys/block", + "--ro-bind", "/sys/bus", "/sys/bus", + "--ro-bind", "/sys/class", "/sys/class", + "--ro-bind", "/sys/dev", "/sys/dev", + "--ro-bind", "/sys/devices", "/sys/devices", + "--bind", "usr", "/usr", + "--bind", "etc", "/etc", + "--bind", "var", "/var", + "--symlink", "/usr/lib", "/lib", + "--symlink", "/usr/lib32", "/lib32", + "--symlink", "/usr/lib64", "/lib64", + "--symlink", "/usr/bin", "/bin", + "--symlink", "/usr/sbin", "/sbin", + }; + static const gsize COMMON_ARGC = sizeof (COMMON_ARGV) / sizeof (*COMMON_ARGV); + + gsize i; + GPtrArray *args = g_ptr_array_sized_new (COMMON_ARGC + child_argc + 1); + g_autofree gchar **args_raw = NULL; + + for (i = 0; i < COMMON_ARGC; i++) + g_ptr_array_add (args, (gchar *) COMMON_ARGV[i]); + + for (i = 0; i < child_argc; i++) + g_ptr_array_add (args, (gchar *) child_argv[i]); + + g_ptr_array_add (args, NULL); + + args_raw = (gchar **) g_ptr_array_free (args, FALSE); + + return g_spawn_sync (NULL, args_raw, NULL, 0, &child_setup_fchdir, + (gpointer) (uintptr_t) deployment_dfd, + stdout, NULL, exit_status, error); +} + +/* + * Run semodule to check if the module content changed after merging /etc + * and rebuild the policy if needed. + */ +static gboolean +sysroot_finalize_selinux_policy (int deployment_dfd, GError **error) +{ + struct stat stbuf; + gint exit_status; + g_autofree gchar *stdout = NULL; + + if (!glnx_fstatat_allow_noent (deployment_dfd, "etc/selinux/config", &stbuf, + AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + + /* Skip the SELinux policy refresh if /etc/selinux/config doesn't exist. */ + if (errno != 0) + return TRUE; + + /* + * Skip the SELinux policy refresh if the --rebuild-if-modules-changed + * flag is not supported by semodule. + */ + static const gchar * const SEMODULE_HELP_ARGV[] = { + "semodule", "--help" + }; + static const gsize SEMODULE_HELP_ARGC = sizeof (SEMODULE_HELP_ARGV) / sizeof (*SEMODULE_HELP_ARGV); + if (!run_in_deployment (deployment_dfd, SEMODULE_HELP_ARGV, + SEMODULE_HELP_ARGC, &exit_status, &stdout, error)) + return FALSE; + if (!g_spawn_check_exit_status (exit_status, error)) + return FALSE; + if (!strstr(stdout, "--rebuild-if-modules-changed")) + return TRUE; + + static const gchar * const SEMODULE_REBUILD_ARGV[] = { + "semodule", "-N", "--rebuild-if-modules-changed" + }; + static const gsize SEMODULE_REBUILD_ARGC = sizeof (SEMODULE_REBUILD_ARGV) / sizeof (*SEMODULE_REBUILD_ARGV); + + if (!run_in_deployment (deployment_dfd, SEMODULE_REBUILD_ARGV, + SEMODULE_REBUILD_ARGC, &exit_status, NULL, error)) + return FALSE; + return g_spawn_check_exit_status (exit_status, error); +} +#endif /* HAVE_SELINUX */ + static gboolean sysroot_finalize_deployment (OstreeSysroot *self, OstreeDeployment *deployment, @@ -2866,6 +2978,11 @@ sysroot_finalize_deployment (OstreeSysroot *self, return FALSE; } +#ifdef HAVE_SELINUX + if (!sysroot_finalize_selinux_policy(deployment_dfd, error)) + return FALSE; +#endif /* HAVE_SELINUX */ + const char *osdeploypath = glnx_strjoina ("ostree/deploy/", ostree_deployment_get_osname (deployment)); glnx_autofd int os_deploy_dfd = -1; if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error)) From c58a4fe661d9d3bf2c515aa5605b1e094c0a62ca Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 28 Mar 2022 17:46:59 -0400 Subject: [PATCH 20/35] deploy: Be a bit more verbose about SELinux bits Let's log when we don't find the expected CLI argument which will help debug things. --- src/libostree/ostree-sysroot-deploy.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 3f301891..5219e2a4 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -2926,9 +2926,12 @@ sysroot_finalize_selinux_policy (int deployment_dfd, GError **error) SEMODULE_HELP_ARGC, &exit_status, &stdout, error)) return FALSE; if (!g_spawn_check_exit_status (exit_status, error)) - return FALSE; + return glnx_prefix_error (error, "failed to run semodule"); if (!strstr(stdout, "--rebuild-if-modules-changed")) - return TRUE; + { + ot_journal_print (LOG_INFO, "semodule does not have --rebuild-if-modules-changed"); + return TRUE; + } static const gchar * const SEMODULE_REBUILD_ARGV[] = { "semodule", "-N", "--rebuild-if-modules-changed" From 2346d5f4d581ca11617e0f215a40cf146754cb22 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 4 Apr 2022 10:25:35 -0400 Subject: [PATCH 21/35] libarchive: Handle `archive_entry_symlink()` returning NULL The `archive_entry_symlink()` API can definitely return `NULL`, reading through the libarchive sources. I hit this in the wild when using old ostree-ext to try to unpack a chunked archive. I didn't try to characterize this more, and sorry no unit test right now. --- src/libostree/ostree-repo-libarchive.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libostree/ostree-repo-libarchive.c b/src/libostree/ostree-repo-libarchive.c index 679aa44d..631c6d4b 100644 --- a/src/libostree/ostree-repo-libarchive.c +++ b/src/libostree/ostree-repo-libarchive.c @@ -146,8 +146,12 @@ file_info_from_archive_entry (struct archive_entry *entry) g_autoptr(GFileInfo) info = _ostree_stbuf_to_gfileinfo (&stbuf); if (S_ISLNK (stbuf.st_mode)) - g_file_info_set_attribute_byte_string (info, "standard::symlink-target", - archive_entry_symlink (entry)); + { + const char *target = archive_entry_symlink (entry); + if (target != NULL) + g_file_info_set_attribute_byte_string (info, "standard::symlink-target", + target); + } return g_steal_pointer (&info); } From f396873d3687c892204767267e3860a15f134238 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 8 Apr 2022 18:01:31 -0600 Subject: [PATCH 22/35] man: Remove unnecessary nbsp's from ostree man page I really don't know what these were for. In both man and HTML output with a recent version of the Docbook XSL, the command synopses are already indented. I don't see any reason why they'd need to be manually indented more. --- man/ostree.xml | 66 +++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/man/ostree.xml b/man/ostree.xml index c06c6121..39f78845 100644 --- a/man/ostree.xml +++ b/man/ostree.xml @@ -151,7 +151,7 @@ License along with this library. If not, see . ostree-admin-cleanup1 -  Delete untagged + Delete untagged deployments and repository objects. @@ -160,7 +160,7 @@ License along with this library. If not, see . ostree-admin-config-diff1 -  See changes to + See changes to /etc as compared to the current default (from /usr/etc). @@ -171,7 +171,7 @@ License along with this library. If not, see . ostree-admin-deploy1 -  Takes a particular + Takes a particular commit or revision, and sets it up for the next boot. @@ -181,7 +181,7 @@ License along with this library. If not, see . ostree-admin-init-fs1 -  Initialize a root filesystem + Initialize a root filesystem in a specified path. @@ -190,7 +190,7 @@ License along with this library. If not, see . ostree-admin-instutil1 -  Utility functions intended primarily for operating system installation programs + Utility functions intended primarily for operating system installation programs @@ -198,7 +198,7 @@ License along with this library. If not, see . ostree-admin-os-init1 -  Initialize the + Initialize the deployment location for an operating system with a specified name. @@ -208,7 +208,7 @@ License along with this library. If not, see . ostree-admin-status1 -  Show and list the deployments. + Show and list the deployments. @@ -216,7 +216,7 @@ License along with this library. If not, see . ostree-admin-switch1 -  Choose a different ref + Choose a different ref to track from the same remote as the current tree. @@ -226,7 +226,7 @@ License along with this library. If not, see . ostree-admin-undeploy1 -  Remove the previously + Remove the previously INDEX deployed tree from the bootloader configuration. @@ -237,7 +237,7 @@ License along with this library. If not, see . ostree-admin-upgrade1 -  Download the latest version for the + Download the latest version for the current ref, and deploy it. @@ -253,7 +253,7 @@ License along with this library. If not, see . ostree-cat1 -  Concatenate contents of files + Concatenate contents of files @@ -261,7 +261,7 @@ License along with this library. If not, see . ostree-checkout1 -  Check out a commit into a filesystem tree. + Check out a commit into a filesystem tree. @@ -269,7 +269,7 @@ License along with this library. If not, see . ostree-checksum1 -  Gives checksum of any file. + Gives checksum of any file. @@ -277,7 +277,7 @@ License along with this library. If not, see . ostree-commit1 -  Given one or more + Given one or more trees, create a new commit using those contents. @@ -286,7 +286,7 @@ License along with this library. If not, see . ostree-config1 -  Change settings. + Change settings. @@ -294,7 +294,7 @@ License along with this library. If not, see . ostree-create-usb1 -  Put the given refs on an external drive for P2P distribution. + Put the given refs on an external drive for P2P distribution. @@ -302,7 +302,7 @@ License along with this library. If not, see . ostree-diff1 -  Concisely list + Concisely list differences between the given refs. @@ -311,7 +311,7 @@ License along with this library. If not, see . ostree-find-remotes1 -  Find remotes to serve the given refs. + Find remotes to serve the given refs. @@ -319,7 +319,7 @@ License along with this library. If not, see . ostree-fsck1 -  Check a repository for consistency. + Check a repository for consistency. @@ -327,7 +327,7 @@ License along with this library. If not, see . ostree-init1 -  Initialize a new repository. + Initialize a new repository. @@ -335,7 +335,7 @@ License along with this library. If not, see . ostree-log1 -  Show revision log. + Show revision log. @@ -343,7 +343,7 @@ License along with this library. If not, see . ostree-ls1 -  List the contents of a given commit. + List the contents of a given commit. @@ -351,7 +351,7 @@ License along with this library. If not, see . ostree-prune1 -  Search for unreachable objects. + Search for unreachable objects. @@ -359,7 +359,7 @@ License along with this library. If not, see . ostree-pull-local1 -  Copy data from source-repo. + Copy data from source-repo. @@ -367,7 +367,7 @@ License along with this library. If not, see . ostree-pull1 -  Download data from remote repo. If you have libsoup. + Download data from remote repo. If you have libsoup. @@ -375,7 +375,7 @@ License along with this library. If not, see . ostree-refs1 -  List refs. + List refs. @@ -383,7 +383,7 @@ License along with this library. If not, see . ostree-remote1 -  Manipulate remote archive configuration. + Manipulate remote archive configuration. @@ -391,7 +391,7 @@ License along with this library. If not, see . ostree-reset1 -  Reset a ref to a previous commit. + Reset a ref to a previous commit. @@ -399,7 +399,7 @@ License along with this library. If not, see . ostree-rev-parse1 -  Show the SHA256 corresponding to a given rev. + Show the SHA256 corresponding to a given rev. @@ -407,7 +407,7 @@ License along with this library. If not, see . ostree-show1 -  Given an OSTree SHA256 checksum, display its contents. + Given an OSTree SHA256 checksum, display its contents. @@ -415,7 +415,7 @@ License along with this library. If not, see . ostree-static-delta1 -  Manage static delta files. + Manage static delta files. @@ -423,7 +423,7 @@ License along with this library. If not, see . ostree-summary1 -  Regenerate the repository summary metadata. + Regenerate the repository summary metadata. @@ -431,7 +431,7 @@ License along with this library. If not, see . ostree-trivial-httpd1 -  Simple webserver. + Simple webserver. From 61deb167daa13bf5bfeff954e5ce8e98fd8b6a28 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 21 May 2021 11:57:54 -0600 Subject: [PATCH 23/35] man: Allow building HTML man pages Add a --enable-man-html configure option to build HTML man pages using a different stylesheet. The HTML pages aren't installed as I don't know what purpose they'd serve on an actual installation. --- Makefile-man.am | 25 ++++++++++++++++++++++--- configure.ac | 10 ++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Makefile-man.am b/Makefile-man.am index 78025fff..1779edc1 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -52,9 +52,23 @@ man5_files = ostree.repo.5 ostree.repo-config.5 man1_MANS = $(addprefix man/,$(man1_files)) man5_MANS = $(addprefix man/,$(man5_files)) +manhtml_files = \ + $(addprefix man/html/,$(man1_files:.1=.html)) \ + $(addprefix man/html/,$(man5_files:.5=.html)) \ + $(NULL) + +if ENABLE_MAN_HTML +noinst_DATA += $(manhtml_files) + +# Convenience target for building the just the HTML man pages +manhtml: $(manhtml_files) +.PHONY: manhtml +endif + EXTRA_DIST += $(man1_MANS:.1=.xml) $(man5_MANS:.5=.xml) -XSLT_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl +XSLT_MAN_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl +XSLT_HTML_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl XSLTPROC_FLAGS = \ --nonet \ @@ -67,14 +81,19 @@ XSLTPROC_FLAGS = \ XSLTPROC_MAN = $(XSLTPROC) $(XSLTPROC_FLAGS) %.1: %.xml - $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_STYLESHEET) $< + $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_MAN_STYLESHEET) $< %.5: %.xml - $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_STYLESHEET) $< + $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_MAN_STYLESHEET) $< + +man/html/%.html: man/%.xml + @mkdir -p man/html + $(AM_V_GEN) $(XSLTPROC_MAN) --output $@ $(XSLT_HTML_STYLESHEET) $< CLEANFILES += \ $(man1_MANS) \ $(man5_MANS) \ + $(manhtml_files) \ $(NULL) endif diff --git a/configure.ac b/configure.ac index 8ca2f451..693261ca 100644 --- a/configure.ac +++ b/configure.ac @@ -292,6 +292,16 @@ AS_IF([test "$enable_man" != no], [ ]) AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) +AC_ARG_ENABLE([man-html], + [AS_HELP_STRING([--enable-man-html], + [generate man HTML pages [default=no]])],, + enable_man_html=no) + +AS_IF([test "$enable_man_html" = yes && test "$enable_man" = no], [ + AC_MSG_ERROR([--enable-man is required for --enable-man-html]) +]) +AM_CONDITIONAL(ENABLE_MAN_HTML, test "$enable_man_html" = yes) + AC_ARG_WITH(libarchive, AS_HELP_STRING([--without-libarchive], [Do not use libarchive]), :, with_libarchive=maybe) From d5061dcf82f64c2cf3ba559760a37808c57d9031 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 8 Apr 2022 15:30:23 -0600 Subject: [PATCH 24/35] man: Use custom XSL stylesheet for HTML output This allows using a template stolen from systemd to turn references into links. --- Makefile-man.am | 3 ++- man/html.xsl | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 man/html.xsl diff --git a/Makefile-man.am b/Makefile-man.am index 1779edc1..3f941bed 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -68,7 +68,8 @@ endif EXTRA_DIST += $(man1_MANS:.1=.xml) $(man5_MANS:.5=.xml) XSLT_MAN_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl -XSLT_HTML_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl +XSLT_HTML_STYLESHEET = man/html.xsl +EXTRA_DIST += $(XSLT_HTML_STYLESHEET) XSLTPROC_FLAGS = \ --nonet \ diff --git a/man/html.xsl b/man/html.xsl new file mode 100644 index 00000000..17a425d4 --- /dev/null +++ b/man/html.xsl @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + .html + + + + + + From 8032e89d33f7d0505a9953546ccf567444166603 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 8 Apr 2022 16:51:59 -0600 Subject: [PATCH 25/35] man: Create an HTML index This provides something that can serve at the root of the HTML man pages tree. --- Makefile-man.am | 5 +- man/index.xml | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 man/index.xml diff --git a/Makefile-man.am b/Makefile-man.am index 3f941bed..5c7f2413 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -19,6 +19,8 @@ if ENABLE_MAN +# If you add a new man page here, add a reference to it in index.xml and +# ostree.xml. man1_files = ostree.1 ostree-admin-cleanup.1 \ ostree-admin-config-diff.1 ostree-admin-deploy.1 \ ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-os-init.1 \ @@ -53,6 +55,7 @@ man1_MANS = $(addprefix man/,$(man1_files)) man5_MANS = $(addprefix man/,$(man5_files)) manhtml_files = \ + man/html/index.html \ $(addprefix man/html/,$(man1_files:.1=.html)) \ $(addprefix man/html/,$(man5_files:.5=.html)) \ $(NULL) @@ -65,7 +68,7 @@ manhtml: $(manhtml_files) .PHONY: manhtml endif -EXTRA_DIST += $(man1_MANS:.1=.xml) $(man5_MANS:.5=.xml) +EXTRA_DIST += man/index.xml $(man1_MANS:.1=.xml) $(man5_MANS:.5=.xml) XSLT_MAN_STYLESHEET = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl XSLT_HTML_STYLESHEET = man/html.xsl diff --git a/man/index.xml b/man/index.xml new file mode 100644 index 00000000..e20ae871 --- /dev/null +++ b/man/index.xml @@ -0,0 +1,202 @@ + + + + + + + OSTree Manual + + + ostree1 + + + + ostree-admin-cleanup1 + + + + ostree-admin-config-diff1 + + + + ostree-admin-deploy1 + + + + ostree-admin-init-fs1 + + + + ostree-admin-instutil1 + + + + ostree-admin-os-init1 + + + + ostree-admin-pin1 + + + + ostree-admin-set-origin1 + + + + ostree-admin-status1 + + + + ostree-admin-switch1 + + + + ostree-admin-undeploy1 + + + + ostree-admin-unlock1 + + + + ostree-admin-upgrade1 + + + + ostree-admin1 + + + + ostree-cat1 + + + + ostree-checkout1 + + + + ostree-checksum1 + + + + ostree-commit1 + + + + ostree-config1 + + + + ostree-create-usb1 + + + + ostree-diff1 + + + + ostree-export1 + + + + ostree-find-remotes1 + + + + ostree-fsck1 + + + + ostree-gpg-sign1 + + + + ostree-init1 + + + + ostree-log1 + + + + ostree-ls1 + + + + ostree-prune1 + + + + ostree-pull-local1 + + + + ostree-pull1 + + + + ostree-refs1 + + + + ostree-remote1 + + + + ostree.repo-config5 + + + + ostree.repo5 + + + + ostree-reset1 + + + + ostree-rev-parse1 + + + + ostree-show1 + + + + ostree-sign1 + + + + ostree-static-delta1 + + + + ostree-summary1 + + + + ostree-trivial-httpd1 + + + + rofiles-fuse1 + + From 9b6a8171c5a627f3a12829526ad7d80a0e157e77 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Fri, 8 Apr 2022 15:30:07 -0600 Subject: [PATCH 26/35] docs: Publish man pages Make a copy of `man/html` to `docs/man` and then configure Jekyll to include it verbatim like the API docs. A link is added to the main index and the necessary commands are added to the github docs workflow. --- .github/workflows/docs.yml | 5 +++-- docs/_config.yml | 6 +++--- docs/index.md | 4 ++++ docs/prep-docs.sh | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fe120fde..72fde7ad 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,10 +26,11 @@ jobs: - name: Install dependencies run: ./ci/installdeps.sh - - name: Build API docs + - name: Build API docs and manpages run: | - ./autogen.sh --enable-gtk-doc + ./autogen.sh --enable-gtk-doc --enable-man --enable-man-html make -C apidoc + make manhtml - name: Build and publish jekyll docs uses: helaili/jekyll-action@v2 diff --git a/docs/_config.yml b/docs/_config.yml index ed1c2a63..991d2adc 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -20,9 +20,9 @@ exclude: - prep-docs.sh - vendor/ -# This is a copy of the apidoc/html directory. Run prep-docs.sh before -# jekyll to put it in place. -include: [reference] +# These are copies of the apidoc/html and man/html directories. Run +# prep-docs.sh before jekyll to put it in place. +include: [reference, man] remote_theme: coreos/just-the-docs plugins: diff --git a/docs/index.md b/docs/index.md index db729be3..b1c4da3b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -146,6 +146,10 @@ make install DESTDIR=/path/to/dest The libostree API documentation is available in [Reference](reference/). +## Manual Pages + +The ostree manual pages are available in [Manual](man/). + ## Contributing See [Contributing]({{ site.baseurl }}{% link CONTRIBUTING.md %}). diff --git a/docs/prep-docs.sh b/docs/prep-docs.sh index 2ae15a74..5975d5cc 100755 --- a/docs/prep-docs.sh +++ b/docs/prep-docs.sh @@ -21,3 +21,17 @@ fi echo "Copying $apidocs to $refdir" rm -rf "$refdir" cp -r "$apidocs" "$refdir" + +# Make sure the manpages have been generated and copy them to the man +# directory. +manhtml="$topdir/man/html" +mandir="$docsdir/man" +if [ ! -d "$manhtml" ]; then + echo "error: HTML manpages $manhtml have not been generated" >&2 + echo "Rebuild with --enable-man option and run `make manhtml`" >&2 + exit 1 +fi + +echo "Copying $manhtml to $mandir" +rm -rf "$mandir" +cp -r "$manhtml" "$mandir" From 7e83726faab0b68eb79c5e35bbd6c336e429625a Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Tue, 12 Apr 2022 09:53:54 +0000 Subject: [PATCH 27/35] tests: move fixture to fix installed tests This moves around the packed archive for bare-split-xattrs mode, in order to fix installed tests. --- Makefile-tests.am | 2 +- .../basic.tar.xz => bare-split-xattrs-basic.tar.xz} | Bin tests/test-basic-bare-split-xattrs.sh | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/{fixtures/bare-split-xattrs/basic.tar.xz => bare-split-xattrs-basic.tar.xz} (100%) diff --git a/Makefile-tests.am b/Makefile-tests.am index 5d39ee5e..29de6c7a 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -202,7 +202,7 @@ dist_installed_test_data = tests/archive-test.sh \ tests/ostree-path-traverse.tar.gz \ tests/pre-signed-pull-data.tar.gz \ tests/libtest-core.sh \ - tests/fixtures/bare-split-xattrs/basic.tar.xz \ + tests/bare-split-xattrs-basic.tar.xz \ $(NULL) EXTRA_DIST += tests/libtest.sh diff --git a/tests/fixtures/bare-split-xattrs/basic.tar.xz b/tests/bare-split-xattrs-basic.tar.xz similarity index 100% rename from tests/fixtures/bare-split-xattrs/basic.tar.xz rename to tests/bare-split-xattrs-basic.tar.xz diff --git a/tests/test-basic-bare-split-xattrs.sh b/tests/test-basic-bare-split-xattrs.sh index ac8ebffd..bf828eb7 100755 --- a/tests/test-basic-bare-split-xattrs.sh +++ b/tests/test-basic-bare-split-xattrs.sh @@ -56,7 +56,7 @@ rm -rf -- repo files if [ "${PRIVILEGED}" = "true" ]; then COMMIT="d614c428015227259031b0f19b934dade908942fd71c49047e0daa70e7800a5d" cd ${test_tmpdir} - ${SUDO} tar --same-permissions --same-owner -xaf ${test_srcdir}/fixtures/bare-split-xattrs/basic.tar.xz + ${SUDO} tar --same-permissions --same-owner -xaf ${test_srcdir}/bare-split-xattrs-basic.tar.xz ${SUDO} ${OSTREE} fsck --all ${OSTREE} log ${COMMIT} > out.txt assert_file_has_content_literal out.txt "fixtures: bare-split-xattrs repo" From 4fd0d6b4c1566a75cb234a41e895a603c19448d4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 20 Apr 2022 15:16:22 -0400 Subject: [PATCH 28/35] fetcher/curl: Consistently check return value `curl_easy_setopt` Static analyzers don't like when we only check it sometimes. And we definitely want to know if any of these are failing. --- src/libostree/ostree-fetcher-curl.c | 101 ++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/src/libostree/ostree-fetcher-curl.c b/src/libostree/ostree-fetcher-curl.c index c63369fd..75038ecc 100644 --- a/src/libostree/ostree-fetcher-curl.c +++ b/src/libostree/ostree-fetcher-curl.c @@ -764,11 +764,13 @@ initiate_next_curl_request (FetcherRequest *req, GUri *baseuri = req->mirrorlist->pdata[req->idx]; { g_autofree char *uri = request_get_uri (req, baseuri); - curl_easy_setopt (req->easy, CURLOPT_URL, uri); + rc = curl_easy_setopt (req->easy, CURLOPT_URL, uri); + g_assert_cmpint (rc, ==, CURLM_OK); } - (void) curl_easy_setopt (req->easy, CURLOPT_USERAGENT, + rc = curl_easy_setopt (req->easy, CURLOPT_USERAGENT, self->custom_user_agent ?: OSTREE_FETCHER_USERAGENT_STRING); + g_assert_cmpint (rc, ==, CURLM_OK); /* Set caching request headers */ if (req->if_none_match != NULL) @@ -791,7 +793,10 @@ initiate_next_curl_request (FetcherRequest *req, req->req_headers = curl_slist_append (req->req_headers, l->data); if (req->req_headers != NULL) - curl_easy_setopt (req->easy, CURLOPT_HTTPHEADER, req->req_headers); + { + rc = curl_easy_setopt (req->easy, CURLOPT_HTTPHEADER, req->req_headers); + g_assert_cmpint (rc, ==, CURLM_OK); + } if (self->cookie_jar_path) { @@ -808,10 +813,17 @@ initiate_next_curl_request (FetcherRequest *req, } if (self->tls_ca_db_path) - curl_easy_setopt (req->easy, CURLOPT_CAINFO, self->tls_ca_db_path); + { + rc = curl_easy_setopt (req->easy, CURLOPT_CAINFO, self->tls_ca_db_path); + g_assert_cmpint (rc, ==, CURLM_OK); + } + if ((self->config_flags & OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE) > 0) - curl_easy_setopt (req->easy, CURLOPT_SSL_VERIFYPEER, 0L); + { + rc = curl_easy_setopt (req->easy, CURLOPT_SSL_VERIFYPEER, 0L); + g_assert_cmpint (rc, ==, CURLM_OK); + } if (self->tls_client_cert_path) { @@ -826,28 +838,43 @@ initiate_next_curl_request (FetcherRequest *req, */ if (g_str_has_prefix (self->tls_client_key_path, "pkcs11:")) { - curl_easy_setopt (req->easy, CURLOPT_SSLENGINE, "pkcs11"); - curl_easy_setopt (req->easy, CURLOPT_SSLENGINE_DEFAULT, 1L); - curl_easy_setopt (req->easy, CURLOPT_SSLKEYTYPE, "ENG"); + rc = curl_easy_setopt (req->easy, CURLOPT_SSLENGINE, "pkcs11"); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_SSLENGINE_DEFAULT, 1L); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_SSLKEYTYPE, "ENG"); + g_assert_cmpint (rc, ==, CURLM_OK); } if (g_str_has_prefix (self->tls_client_cert_path, "pkcs11:")) - curl_easy_setopt (req->easy, CURLOPT_SSLCERTTYPE, "ENG"); + { + rc = curl_easy_setopt (req->easy, CURLOPT_SSLCERTTYPE, "ENG"); + g_assert_cmpint (rc, ==, CURLM_OK); + } - curl_easy_setopt (req->easy, CURLOPT_SSLCERT, self->tls_client_cert_path); - curl_easy_setopt (req->easy, CURLOPT_SSLKEY, self->tls_client_key_path); + rc = curl_easy_setopt (req->easy, CURLOPT_SSLCERT, self->tls_client_cert_path); + g_assert_cmpint (rc, ==, CURLM_OK); + + rc = curl_easy_setopt (req->easy, CURLOPT_SSLKEY, self->tls_client_key_path); + g_assert_cmpint (rc, ==, CURLM_OK); } if ((self->config_flags & OSTREE_FETCHER_FLAGS_TRANSFER_GZIP) > 0) - curl_easy_setopt (req->easy, CURLOPT_ACCEPT_ENCODING, ""); + { + rc = curl_easy_setopt (req->easy, CURLOPT_ACCEPT_ENCODING, ""); + g_assert_cmpint (rc, ==, CURLM_OK); + } /* If we have e.g. basic auth in the URL string, let's honor that */ const char *username = g_uri_get_user (baseuri); - curl_easy_setopt (req->easy, CURLOPT_USERNAME, username); + rc = curl_easy_setopt (req->easy, CURLOPT_USERNAME, username); + g_assert_cmpint (rc, ==, CURLM_OK); const char *password = g_uri_get_password (baseuri); - curl_easy_setopt (req->easy, CURLOPT_PASSWORD, password); + rc = curl_easy_setopt (req->easy, CURLOPT_PASSWORD, password); + g_assert_cmpint (rc, ==, CURLM_OK); /* We should only speak HTTP; TODO: only enable file if specified */ - curl_easy_setopt (req->easy, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE)); + rc = curl_easy_setopt (req->easy, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE)); + g_assert_cmpint (rc, ==, CURLM_OK); /* Picked the current version in F25 as of 20170127, since * there are numerous HTTP/2 fixes since the original version in * libcurl 7.43.0. @@ -855,25 +882,37 @@ initiate_next_curl_request (FetcherRequest *req, if (!(self->config_flags & OSTREE_FETCHER_FLAGS_DISABLE_HTTP2)) { #if CURL_AT_LEAST_VERSION(7, 51, 0) - curl_easy_setopt (req->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + rc = curl_easy_setopt (req->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + g_assert_cmpint (rc, ==, CURLM_OK); #endif /* https://github.com/curl/curl/blob/curl-7_53_0/docs/examples/http2-download.c */ #if (CURLPIPE_MULTIPLEX > 0) /* wait for pipe connection to confirm */ - curl_easy_setopt (req->easy, CURLOPT_PIPEWAIT, 1L); + rc = curl_easy_setopt (req->easy, CURLOPT_PIPEWAIT, 1L); + g_assert_cmpint (rc, ==, CURLM_OK); #endif } - curl_easy_setopt (req->easy, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt (req->easy, CURLOPT_HEADERFUNCTION, response_header_cb); + rc = curl_easy_setopt (req->easy, CURLOPT_WRITEFUNCTION, write_cb); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_HEADERFUNCTION, response_header_cb); + g_assert_cmpint (rc, ==, CURLM_OK); if (g_getenv ("OSTREE_DEBUG_HTTP")) - curl_easy_setopt (req->easy, CURLOPT_VERBOSE, 1L); - curl_easy_setopt (req->easy, CURLOPT_ERRORBUFFER, req->error); + { + rc = curl_easy_setopt (req->easy, CURLOPT_VERBOSE, 1L); + g_assert_cmpint (rc, ==, CURLM_OK); + } + rc = curl_easy_setopt (req->easy, CURLOPT_ERRORBUFFER, req->error); + g_assert_cmpint (rc, ==, CURLM_OK); /* Note that the "easy" object's privdata is the task */ - curl_easy_setopt (req->easy, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt (req->easy, CURLOPT_PROGRESSFUNCTION, prog_cb); - curl_easy_setopt (req->easy, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt (req->easy, CURLOPT_CONNECTTIMEOUT, 30L); + rc = curl_easy_setopt (req->easy, CURLOPT_NOPROGRESS, 1L); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_PROGRESSFUNCTION, prog_cb); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_FOLLOWLOCATION, 1L); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_CONNECTTIMEOUT, 30L); + g_assert_cmpint (rc, ==, CURLM_OK); /* We used to set CURLOPT_LOW_SPEED_LIMIT and CURLOPT_LOW_SPEED_TIME * here, but see https://github.com/ostreedev/ostree/issues/878#issuecomment-347228854 * basically those options don't play well with HTTP2 at the moment @@ -883,10 +922,14 @@ initiate_next_curl_request (FetcherRequest *req, */ /* closure bindings -> task */ - curl_easy_setopt (req->easy, CURLOPT_PRIVATE, task); - curl_easy_setopt (req->easy, CURLOPT_WRITEDATA, task); - curl_easy_setopt (req->easy, CURLOPT_HEADERDATA, task); - curl_easy_setopt (req->easy, CURLOPT_PROGRESSDATA, task); + rc = curl_easy_setopt (req->easy, CURLOPT_PRIVATE, task); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_WRITEDATA, task); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_HEADERDATA, task); + g_assert_cmpint (rc, ==, CURLM_OK); + rc = curl_easy_setopt (req->easy, CURLOPT_PROGRESSDATA, task); + g_assert_cmpint (rc, ==, CURLM_OK); CURLMcode multi_rc = curl_multi_add_handle (self->multi, req->easy); g_assert (multi_rc == CURLM_OK); From b92dbc49f0736a734a5f4550cf7b784263d0eef9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 20 Apr 2022 20:23:10 -0400 Subject: [PATCH 29/35] ci: Mask zincati for synthetic update Our CI started falling over because coreos-assembler checks for units stuck activating as of https://github.com/coreos/coreos-assembler/pull/2810 Really need to centralize the code for this and fix the root problem, but...not today. xref https://github.com/coreos/coreos-assembler/pull/2814 --- tests/kolainst/destructive/staged-deploy.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/kolainst/destructive/staged-deploy.sh b/tests/kolainst/destructive/staged-deploy.sh index 406911ca..df40f115 100755 --- a/tests/kolainst/destructive/staged-deploy.sh +++ b/tests/kolainst/destructive/staged-deploy.sh @@ -40,6 +40,9 @@ EOF cd /ostree/repo/tmp # https://github.com/ostreedev/ostree/issues/1569 ostree checkout -H ${commit} t + # xref https://github.com/coreos/coreos-assembler/pull/2814 + systemctl mask --now zincati + # Create a synthetic commit for upgrade ostree commit --no-bindings --parent="${commit}" -b staged-deploy -I --consume t newcommit=$(ostree rev-parse staged-deploy) orig_mtime=$(stat -c '%.Y' /sysroot/ostree/deploy) From 614d30acf3353425d045810a76e551884b90e160 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 21 Apr 2022 12:04:33 -0400 Subject: [PATCH 30/35] docs: Add new IMA document Now that the fixed code for `ima-sign` landed in https://github.com/ostreedev/ostree-rs-ext/pull/283 --- docs/ima.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 docs/ima.md diff --git a/docs/ima.md b/docs/ima.md new file mode 100644 index 00000000..e2bb51c8 --- /dev/null +++ b/docs/ima.md @@ -0,0 +1,111 @@ +--- +nav_order: 10 +--- + +# Using Linux IMA with OSTree +{: .no_toc } + +1. TOC +{:toc} + +## Linux IMA + +The [Linux Integrity Measurement Architecture](https://sourceforge.net/p/linux-ima/wiki/Home/) +provides a mechanism to cryptographically sign the digest of a regular +file, and policies can be applied to e.g. require that code executed +by the root user have a valid signed digest. + +The alignment between Linux IMA and ostree is quite strong. OSTree +provides a content-addressable object store, where files are intended +to be immutable. This is implemented with a basic read-only bind mount. + +While IMA does not actually prevent mutating files, any changed (or unsigned) +files would (depending on policy) not be readable or executable. + +## IMA signatures and OSTree checksum + +Mechanically, IMA signatures appear as a `security.ima` extended attribute +on the file. This is a signed digest of just the file content. + +OSTree has first-class support for extended attributes; +they are included in the object digest along with other key file attributes +such as uid, gid and mode. + +Together, this means that adding an IMA signature to a file in the OSTree +model appears as a new object (with a new digest). + +## Signing + +To apply IMA signatures to an OSTree commit, there is an `ima-sign` +command implemented currently in the [ostree-rs-ext](https://github.com/ostreedev/ostree-rs-ext/) +project. + +### Generating a key + +There is documentation for this in `man evmctl` and the upstream IMA +page; we will not replicate it here. + +### Signing a commit + +`ima-sign` requires 4 things: + +- An OSTree repository (could be any mode; `archive` or e.g. `bare-user`) +- A ref or commit digest (e.g. `exampleos/x86_64/stable`) +- A digest algorthim (usually `sha256`, but you may use e.g. `sha512` as well) +- An RSA private key + +You can then add IMA signatures to all regular files in the commit: + +``` +$ ostree-ext-cli ima-sign --repo=repo exampleos/x86_64/stable sha256 /path/to/key.pem +``` + +Many different choices are possible for the signing model. For example, +your build system could store individual components/packages in their own +ostree refs, and sign them at build time. This would avoid re-signing +all binaries when creating production builds. Although note you +still likely want to sign generated artifacts from unioning individual +components, such as a dpkg/rpm database or equivalent and cache files +such as the `ldconfig` and GTK+ icon caches, etc. + +### Applying a policy + +Signing a commit by itself will have little to no effect. You will also +need to include in your builds an [IMA policy](https://sourceforge.net/p/linux-ima/wiki/Home/#defining-an-lsm-specific-policy). + +### Linux EVM + +The EVM subsystem builds on IMA, and adds another signature which +covers most file data, such as the uid, gid and mode and selected +security-relevant extended attributes. + +If you've been following along, note this is very, very close to what ostree +checksums as well! + +However, the focus of the EVM design seems to mostly +be on machine-specific signatures with keys stored in a TPM. +Note that doing this on a per-machine basis would add a new +`security.evm` extended attribute, and crucially that +*changes the ostree digest* - so from ostree's perspective, +these objects will appear corrupt. + +In the future, ostree may learn to ignore the presence of `security.evm` +extended attributes. + +There is also some support for "portable" EVM signatures - by +default, EVM signatures also include the inode number and generation +which are inherently machine-specific. + +A future ostree enhancement may instead also focus on supporting +signing commits with these "portable" EVM signatures in addition to IMA. + +## Further references + +- https://sourceforge.net/p/linux-ima/wiki/Home/ +- https://wiki.gentoo.org/wiki/Integrity_Measurement_Architecture +- https://fedoraproject.org/wiki/Changes/Signed_RPM_Contents +- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/managing_monitoring_and_updating_the_kernel/enhancing-security-with-the-kernel-integrity-subsystem_managing-monitoring-and-updating-the-kernel + + +###### Licensing for this document: +`SPDX-License-Identifier: (CC-BY-SA-3.0 OR GFDL-1.3-or-later)` From b070e0f54a52500f35e98ed5ee21f91d72dd31f7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 21 Apr 2022 16:19:14 -0400 Subject: [PATCH 31/35] Update docs/ima.md Co-authored-by: Jonathan Lebon --- docs/ima.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/ima.md b/docs/ima.md index e2bb51c8..6483763c 100644 --- a/docs/ima.md +++ b/docs/ima.md @@ -25,14 +25,17 @@ files would (depending on policy) not be readable or executable. ## IMA signatures and OSTree checksum Mechanically, IMA signatures appear as a `security.ima` extended attribute -on the file. This is a signed digest of just the file content. +on the file. This is a signed digest of just the file content (and not +any metadata) -OSTree has first-class support for extended attributes; -they are included in the object digest along with other key file attributes -such as uid, gid and mode. +OSTree's checksums in contrast include not just the file content, but also +metadata such as uid, gid and mode and extended attributes; Together, this means that adding an IMA signature to a file in the OSTree -model appears as a new object (with a new digest). +model appears as a new object (with a new digest). A nice property is that +this enables the transactional addition (or removal) of IMA signatures. +However, adding IMA signatures to files that were previously unsigned +also today duplicates disk space. ## Signing @@ -51,7 +54,7 @@ page; we will not replicate it here. - An OSTree repository (could be any mode; `archive` or e.g. `bare-user`) - A ref or commit digest (e.g. `exampleos/x86_64/stable`) -- A digest algorthim (usually `sha256`, but you may use e.g. `sha512` as well) +- A digest algorithm (usually `sha256`, but you may use e.g. `sha512` as well) - An RSA private key You can then add IMA signatures to all regular files in the commit: @@ -79,8 +82,9 @@ The EVM subsystem builds on IMA, and adds another signature which covers most file data, such as the uid, gid and mode and selected security-relevant extended attributes. -If you've been following along, note this is very, very close to what ostree -checksums as well! +This is quite close to the ostree native checksum - the ordering +of the fields is different so the checksums are physically different, but +logically they are very close. However, the focus of the EVM design seems to mostly be on machine-specific signatures with keys stored in a TPM. @@ -106,6 +110,5 @@ signing commits with these "portable" EVM signatures in addition to IMA. - https://fedoraproject.org/wiki/Changes/Signed_RPM_Contents - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/managing_monitoring_and_updating_the_kernel/enhancing-security-with-the-kernel-integrity-subsystem_managing-monitoring-and-updating-the-kernel + -###### Licensing for this document: -`SPDX-License-Identifier: (CC-BY-SA-3.0 OR GFDL-1.3-or-later)` From d3d3e4ea13944911a243690523d941ed0b4b0041 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 22 Apr 2022 18:46:28 -0400 Subject: [PATCH 32/35] Add an `ostree-boot-complete.service` to propagate staging failures Quite a while ago we added staged deployments, which solved a bunch of issues around the `/etc` merge. However...a persistent problem since then is that any failures in that process that happened in the *previous* boot are not very visible. We ship custom code in `rpm-ostree status` to query the previous journal. But that has a few problems - one is that on systems that have been up a while, that failure message may even get rotated out. And second, some systems may not even have a persistent journal at all. A general thing we do in e.g. Fedora CoreOS testing is to check for systemd unit failures. We do that both in our automated tests, and we even ship code that displays them on ssh logins. And beyond that obviously a lot of other projects do the same; it's easy via `systemctl --failed`. So to make failures more visible, change our `ostree-finalize-staged.service` to have an internal wrapper around the process that "catches" any errors, and copies the error message into a file in `/boot/ostree`. Then, a new `ostree-boot-complete.service` looks for this file on startup and re-emits the error message, and fails. It also deletes the file. The rationale is to avoid *continually* warning. For example we need to handle the case when an upgrade process creates a new staged deployment. Now, we could change the ostree core code to delete the warning file when that happens instead, but this is trying to be a conservative change. This should make failures here much more visible as is. --- Makefile-boot.am | 2 + Makefile-ostree.am | 1 + src/boot/ostree-boot-complete.service | 33 +++++++++++ src/libostree/ostree-cmdprivate.c | 1 + src/libostree/ostree-cmdprivate.h | 1 + src/libostree/ostree-impl-system-generator.c | 2 + src/libostree/ostree-sysroot-deploy.c | 62 ++++++++++++++++++-- src/libostree/ostree-sysroot-private.h | 7 +++ src/libostree/ostree-sysroot.c | 2 + src/ostree/ot-admin-builtin-boot-complete.c | 58 ++++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + src/ostree/ot-builtin-admin.c | 3 + tests/kolainst/destructive/staged-deploy.sh | 12 ++++ 13 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 src/boot/ostree-boot-complete.service create mode 100644 src/ostree/ot-admin-builtin-boot-complete.c diff --git a/Makefile-boot.am b/Makefile-boot.am index ec10a0d6..e42e5180 100644 --- a/Makefile-boot.am +++ b/Makefile-boot.am @@ -38,6 +38,7 @@ endif if BUILDOPT_SYSTEMD systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \ src/boot/ostree-remount.service \ + src/boot/ostree-boot-complete.service \ src/boot/ostree-finalize-staged.service \ src/boot/ostree-finalize-staged.path \ $(NULL) @@ -64,6 +65,7 @@ endif EXTRA_DIST += src/boot/dracut/module-setup.sh \ src/boot/dracut/ostree.conf \ src/boot/mkinitcpio \ + src/boot/ostree-boot-complete.service \ src/boot/ostree-prepare-root.service \ src/boot/ostree-finalize-staged.path \ src/boot/ostree-remount.service \ diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 82af1681..0fe2c5f8 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -70,6 +70,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ src/ostree/ot-admin-builtin-finalize-staged.c \ + src/ostree/ot-admin-builtin-boot-complete.c \ src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-instutil.c \ src/ostree/ot-admin-builtin-cleanup.c \ diff --git a/src/boot/ostree-boot-complete.service b/src/boot/ostree-boot-complete.service new file mode 100644 index 00000000..5c09fdc9 --- /dev/null +++ b/src/boot/ostree-boot-complete.service @@ -0,0 +1,33 @@ +# Copyright (C) 2022 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +[Unit] +Description=OSTree Complete Boot +Documentation=man:ostree(1) +# For now, this is the only condition on which we start, but it's +# marked as a triggering condition in case in the future we want +# to do something else. +ConditionPathExists=|/boot/ostree/finalize-failure.stamp +RequiresMountsFor=/boot +# Ensure that we propagate the failure into the current boot before +# any further finalization attempts. +Before=ostree-finalize-staged.service + +[Service] +Type=oneshot +# To write to /boot while keeping it read-only +MountFlags=slave +RemainAfterExit=yes +ExecStart=/usr/bin/ostree admin boot-complete diff --git a/src/libostree/ostree-cmdprivate.c b/src/libostree/ostree-cmdprivate.c index c9a6e2e1..f6c114f4 100644 --- a/src/libostree/ostree-cmdprivate.c +++ b/src/libostree/ostree-cmdprivate.c @@ -51,6 +51,7 @@ ostree_cmd__private__ (void) _ostree_repo_static_delta_delete, _ostree_repo_verify_bindings, _ostree_sysroot_finalize_staged, + _ostree_sysroot_boot_complete, }; return &table; diff --git a/src/libostree/ostree-cmdprivate.h b/src/libostree/ostree-cmdprivate.h index 46452ebd..17f943c8 100644 --- a/src/libostree/ostree-cmdprivate.h +++ b/src/libostree/ostree-cmdprivate.h @@ -33,6 +33,7 @@ typedef struct { gboolean (* ostree_static_delta_delete) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error); gboolean (* ostree_repo_verify_bindings) (const char *collection_id, const char *ref_name, GVariant *commit, GError **error); gboolean (* ostree_finalize_staged) (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); + gboolean (* ostree_boot_complete) (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); } OstreeCmdPrivateVTable; /* Note this not really "public", we just export the symbol, but not the header */ diff --git a/src/libostree/ostree-impl-system-generator.c b/src/libostree/ostree-impl-system-generator.c index 769f0cbd..92d71605 100644 --- a/src/libostree/ostree-impl-system-generator.c +++ b/src/libostree/ostree-impl-system-generator.c @@ -134,6 +134,8 @@ require_internal_units (const char *normal_dir, return FALSE; if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-finalize-staged.path", normal_dir_dfd, "multi-user.target.wants/ostree-finalize-staged.path") < 0) return glnx_throw_errno_prefix (error, "symlinkat"); + if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-boot-complete.service", normal_dir_dfd, "multi-user.target.wants/ostree-boot-complete.service") < 0) + return glnx_throw_errno_prefix (error, "symlinkat"); return TRUE; #else diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 5219e2a4..404f336f 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -3375,10 +3375,10 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, } /* Invoked at shutdown time by ostree-finalize-staged.service */ -gboolean -_ostree_sysroot_finalize_staged (OstreeSysroot *self, - GCancellable *cancellable, - GError **error) +static gboolean +_ostree_sysroot_finalize_staged_inner (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) { /* It's totally fine if there's no staged deployment; perhaps down the line * though we could teach the ostree cmdline to tell systemd to activate the @@ -3475,9 +3475,63 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self, if (!ostree_sysroot_prepare_cleanup (self, cancellable, error)) return FALSE; + // Cleanup will have closed some FDs, re-ensure writability + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + return TRUE; } +/* Invoked at shutdown time by ostree-finalize-staged.service */ +gboolean +_ostree_sysroot_finalize_staged (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GError) finalization_error = NULL; + if (!_ostree_sysroot_ensure_boot_fd (self, error)) + return FALSE; + if (!_ostree_sysroot_finalize_staged_inner (self, cancellable, &finalization_error)) + { + g_autoptr(GError) writing_error = NULL; + g_assert_cmpint (self->boot_fd, !=, -1); + if (!glnx_file_replace_contents_at (self->boot_fd, _OSTREE_FINALIZE_STAGED_FAILURE_PATH, + (guint8*)finalization_error->message, -1, + 0, cancellable, &writing_error)) + { + // We somehow failed to write the failure message...that's not great. Maybe ENOSPC on /boot. + g_printerr ("Failed to write %s: %s\n", _OSTREE_FINALIZE_STAGED_FAILURE_PATH, writing_error->message); + } + g_propagate_error (error, g_steal_pointer (&finalization_error)); + return FALSE; + } + return TRUE; +} + +/* Invoked at bootup time by ostree-boot-complete.service */ +gboolean +_ostree_sysroot_boot_complete (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + if (!_ostree_sysroot_ensure_boot_fd (self, error)) + return FALSE; + + glnx_autofd int failure_fd = -1; + if (!ot_openat_ignore_enoent (self->boot_fd, _OSTREE_FINALIZE_STAGED_FAILURE_PATH, &failure_fd, error)) + return FALSE; + // If we didn't find a failure log, then there's nothing to do right now. + // (Actually this unit shouldn't even be invoked, but we may do more in the future) + if (failure_fd == -1) + return TRUE; + g_autofree char *failure_data = glnx_fd_readall_utf8 (failure_fd, NULL, cancellable, error); + if (failure_data == NULL) + return glnx_prefix_error (error, "Reading from %s", _OSTREE_FINALIZE_STAGED_FAILURE_PATH); + // Remove the file; we don't want to continually error out. + (void) unlinkat (self->boot_fd, _OSTREE_FINALIZE_STAGED_FAILURE_PATH, 0); + return glnx_throw (error, "ostree-finalize-staged.service failed on previous boot: %s", failure_data); +} + /** * ostree_sysroot_deployment_set_kargs: * @self: Sysroot diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index cb34eeb3..a49a406c 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -96,6 +96,9 @@ struct OstreeSysroot { #define _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "ostree/initramfs-overlays" #define _OSTREE_SYSROOT_INITRAMFS_OVERLAYS "boot/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS +// Relative to /boot, consumed by ostree-boot-complete.service +#define _OSTREE_FINALIZE_STAGED_FAILURE_PATH "ostree/finalize-failure.stamp" + gboolean _ostree_sysroot_ensure_writable (OstreeSysroot *self, GError **error); @@ -142,6 +145,10 @@ gboolean _ostree_sysroot_finalize_staged (OstreeSysroot *self, GCancellable *cancellable, GError **error); +gboolean +_ostree_sysroot_boot_complete (OstreeSysroot *self, + GCancellable *cancellable, + GError **error); OstreeDeployment * _ostree_sysroot_deserialize_deployment_from_variant (GVariant *v, diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 0e0521da..67afb5d2 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -357,6 +357,8 @@ _ostree_sysroot_ensure_writable (OstreeSysroot *self, ostree_sysroot_unload (self); if (!ensure_sysroot_fd (self, error)) return FALSE; + if (!_ostree_sysroot_ensure_boot_fd (self, error)) + return FALSE; return TRUE; } diff --git a/src/ostree/ot-admin-builtin-boot-complete.c b/src/ostree/ot-admin-builtin-boot-complete.c new file mode 100644 index 00000000..6e1052f5 --- /dev/null +++ b/src/ostree/ot-admin-builtin-boot-complete.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include + +#include "ot-main.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree.h" +#include "otutil.h" + +#include "ostree-cmdprivate.h" + +static GOptionEntry options[] = { + { NULL } +}; + +gboolean +ot_admin_builtin_boot_complete (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + /* Just a sanity check; we shouldn't be called outside of the service though. + */ + struct stat stbuf; + if (fstatat (AT_FDCWD, OSTREE_PATH_BOOTED, &stbuf, 0) < 0) + return TRUE; + // We must have been invoked via systemd which should have set up a mount namespace. + g_assert (getenv ("INVOCATION_ID")); + + g_autoptr(GOptionContext) context = g_option_context_new (""); + g_autoptr(OstreeSysroot) sysroot = NULL; + if (!ostree_admin_option_context_parse (context, options, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, + invocation, &sysroot, cancellable, error)) + return FALSE; + + if (!ostree_cmd__private__()->ostree_boot_complete (sysroot, cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index d32b617e..8d9451be 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -39,6 +39,7 @@ BUILTINPROTO(deploy); BUILTINPROTO(cleanup); BUILTINPROTO(pin); BUILTINPROTO(finalize_staged); +BUILTINPROTO(boot_complete); BUILTINPROTO(unlock); BUILTINPROTO(status); BUILTINPROTO(set_origin); diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index e0d2a60c..af09a614 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -43,6 +43,9 @@ static OstreeCommand admin_subcommands[] = { { "finalize-staged", OSTREE_BUILTIN_FLAG_NO_REPO | OSTREE_BUILTIN_FLAG_HIDDEN, ot_admin_builtin_finalize_staged, "Internal command to run at shutdown time" }, + { "boot-complete", OSTREE_BUILTIN_FLAG_NO_REPO | OSTREE_BUILTIN_FLAG_HIDDEN, + ot_admin_builtin_boot_complete, + "Internal command to run at boot after an update was applied" }, { "init-fs", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_init_fs, "Initialize a root filesystem" }, diff --git a/tests/kolainst/destructive/staged-deploy.sh b/tests/kolainst/destructive/staged-deploy.sh index df40f115..7e1991bb 100755 --- a/tests/kolainst/destructive/staged-deploy.sh +++ b/tests/kolainst/destructive/staged-deploy.sh @@ -146,6 +146,18 @@ EOF # Cleanup refs ostree refs --delete staged-deploy nonstaged-deploy echo "ok cleanup refs" + + # Now finally, try breaking staged updates and verify that ostree-boot-complete fails on the next boot + unshare -m /bin/sh -c 'mount -o remount,rw /boot; chattr +i /boot' + rpm-ostree kargs --append=foo=bar + /tmp/autopkgtest-reboot "3" + ;; + "3") + (systemctl status ostree-boot-complete.service || true) | tee out.txt + assert_file_has_content out.txt 'error: ostree-finalize-staged.service failed on previous boot.*Operation not permitted' + systemctl show -p Result ostree-boot-complete.service > out.txt + assert_file_has_content out.txt='Result=exit-code' + echo "ok boot-complete.service" ;; *) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;; esac From 540e60c3e3ace66dd4e6cf825488fc918260a642 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 30 Apr 2022 12:20:11 +0100 Subject: [PATCH 33/35] repo: Factor out _ostree_repo_auto_transaction_new() This will allow the direct allocation in ostree_repo_prepare_transaction() to be replaced with a call to this function, avoiding breaking encapsulation. Signed-off-by: Simon McVittie --- src/libostree/ostree-repo-private.h | 4 ++++ src/libostree/ostree-repo.c | 32 ++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 988c2179..96253e77 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -554,4 +554,8 @@ GType _ostree_repo_auto_transaction_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_unref); +/* Internal function to break a circular dependency: + * should not be made into public API, even if the rest is */ +OstreeRepoAutoTransaction *_ostree_repo_auto_transaction_new (OstreeRepo *repo); + G_END_DECLS diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 54438b78..ad071c17 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -710,6 +710,32 @@ ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *auto_lock) } } +/** + * _ostree_repo_auto_transaction_new: + * @repo: (not nullable): an #OsreeRepo object + * @cancellable: Cancellable + * @error: a #GError + * + * Return a guard for a transaction in @repo. + * + * Do not call this function outside the OstreeRepo transaction implementation. + * Use _ostree_repo_auto_transaction_start() instead. + * + * Returns: (transfer full): an #OstreeRepoAutoTransaction guard on success, + * %NULL otherwise. + */ +OstreeRepoAutoTransaction * +_ostree_repo_auto_transaction_new (OstreeRepo *repo) +{ + g_assert (repo != NULL); + + OstreeRepoAutoTransaction *txn = g_malloc(sizeof(OstreeRepoAutoTransaction)); + txn->atomic_refcount = 1; + txn->repo = g_object_ref (repo); + + return g_steal_pointer (&txn); +} + /** * _ostree_repo_auto_transaction_start: * @repo: (not nullable): an #OsreeRepo object @@ -731,11 +757,7 @@ _ostree_repo_auto_transaction_start (OstreeRepo *repo, if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) return NULL; - OstreeRepoAutoTransaction *txn = g_malloc(sizeof(OstreeRepoAutoTransaction)); - txn->atomic_refcount = 1; - txn->repo = g_object_ref (repo); - - return g_steal_pointer (&txn); + return _ostree_repo_auto_transaction_new (repo); } /** From 71304e854cdb344adb8b1ae7866929fbdde6c327 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 30 Apr 2022 12:53:42 +0100 Subject: [PATCH 34/35] repo: Correctly initialize refcount of temporary transaction Previously, the reference count was left uninitialized as a result of bypassing the constructor, and the intended abort-on-error usually wouldn't have happened. Fixes: 8a9737a "repo/private: move OstreeRepoAutoTransaction to a boxed type" Resolves: https://github.com/ostreedev/ostree/issues/2592 Signed-off-by: Simon McVittie --- src/libostree/ostree-repo-commit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index d688e9b7..0af8fee3 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -1689,10 +1689,10 @@ ostree_repo_prepare_transaction (OstreeRepo *self, g_debug ("Preparing transaction in repository %p", self); /* Set up to abort the transaction if we return early from this function. - * This needs to be manually built here due to a circular dependency. */ - g_autoptr(OstreeRepoAutoTransaction) txn = g_malloc(sizeof(OstreeRepoAutoTransaction)); + * We can't call _ostree_repo_auto_transaction_start() here, because that + * would be a circular dependency; use the lower-level version instead. */ + g_autoptr(OstreeRepoAutoTransaction) txn = _ostree_repo_auto_transaction_new (self); g_assert (txn != NULL); - txn->repo = self; memset (&self->txn.stats, 0, sizeof (OstreeRepoTransactionStats)); From 0554beb4c2b11c54dd3dedd747f982fb6ce5eb70 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 4 May 2022 13:50:06 -0400 Subject: [PATCH 35/35] Release 2022.3 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 693261ca..f59e9d7d 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ m4_define([year_version], [2022]) m4_define([release_version], [3]) m4_define([package_version], [year_version.release_version]) AC_INIT([libostree], [package_version], [walters@verbum.org]) -is_release_build=no +is_release_build=yes AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([buildutil]) AC_CONFIG_AUX_DIR([build-aux])