From 9950f102668af532de5b73bd23ebed797e437234 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 22 Jun 2022 12:35:53 +0200 Subject: [PATCH] Add ostree composefs-generate command This allows you to build a composefs based on a single commit, or by unioning several commits on top of each others. --- Makefile-ostree.am | 1 + bash/ostree | 50 ++++++++ man/ostree-composefs-generate.xml | 125 +++++++++++++++++++ src/ostree/main.c | 3 + src/ostree/ot-builtin-composefs.c | 200 ++++++++++++++++++++++++++++++ src/ostree/ot-builtins.h | 1 + 6 files changed, 380 insertions(+) create mode 100644 man/ostree-composefs-generate.xml create mode 100644 src/ostree/ot-builtin-composefs.c diff --git a/Makefile-ostree.am b/Makefile-ostree.am index fb377075eb..bd4d456ebf 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -27,6 +27,7 @@ ostree_SOURCES = src/ostree/main.c \ src/ostree/ot-builtin-checkout.c \ src/ostree/ot-builtin-checksum.c \ src/ostree/ot-builtin-commit.c \ + src/ostree/ot-builtin-composefs.c \ src/ostree/ot-builtin-create-usb.c \ src/ostree/ot-builtin-diff.c \ src/ostree/ot-builtin-export.c \ diff --git a/bash/ostree b/bash/ostree index 46363315c4..1d50ff3dc7 100644 --- a/bash/ostree +++ b/bash/ostree @@ -292,6 +292,55 @@ _ostree_checkout() { return 0 } +_ostree_composefs_generate() { + local boolean_options=" + $main_boolean_options + --from-stdin + --union-add + --union-identical + --whiteouts + " + + local options_with_args=" + --from-file + --repo + --subpath + " + + local options_with_args_glob=$( __ostree_to_extglob "$options_with_args" ) + + case "$prev" in + --from-file) + __ostree_compreply_all_files + return 0 + ;; + --repo|--subpath) + __ostree_compreply_dirs_only + return 0 + ;; + $options_with_args_glob ) + return 0 + ;; + esac + + case "$cur" in + -*) + local all_options="$boolean_options $options_with_args" + __ostree_compreply_all_options + ;; + *) + local argpos=$( __ostree_pos_first_nonflag $( __ostree_to_alternatives "$options_with_args" ) ) + + if [ $cword -eq $argpos ]; then + __ostree_compreply_commits + elif [ $cword -eq $(($argpos + 1)) ]; then + __ostree_compreply_all_files + fi + esac + + return 0 +} + _ostree_checksum() { local boolean_options=" $main_boolean_options @@ -1852,6 +1901,7 @@ _ostree() { checkout checksum commit + composefs-generate config create-usb diff diff --git a/man/ostree-composefs-generate.xml b/man/ostree-composefs-generate.xml new file mode 100644 index 0000000000..f5fef63241 --- /dev/null +++ b/man/ostree-composefs-generate.xml @@ -0,0 +1,125 @@ + + + + + + + + + ostree composefs-generate + OSTree + + + + Developer + Colin + Walters + walters@verbum.org + + + + + + ostree composefs-generate + 1 + + + + ostree-composefs-generate + Create a composefs image out of commits + + + + + ostree composefs-generate OPTIONS COMMIT DESTINATION + + + + + Description + + + Creates a composefs image out the given commit(s) into the filesystem under directory DESTINATION. If DESTINATION is not specified, the COMMIT will become the destination checkout target. + + + + + Options + + + + + ="PATH" + + + Checkout sub-directory PATH. + + + + + + + + When combining multiple commits, keep existing files. The default is to overwrite existing files. + + + + + + + When combining multiple commits, error out + if a file would be replaced with a different file. Add new files + and directories, ignore identical files, and keep existing + directories. + + + + + + + Process whiteout files (Docker style). + + + + + + + + Process many checkouts from standard input. + + + + + ="FILE" + + + Process many checkouts from input file. + + + + + + + + Example + $ ostree composefs-generate --repo=/my/repo my-branch image.cfs + + diff --git a/src/ostree/main.c b/src/ostree/main.c index 7d17080cf4..bcbfb0af40 100644 --- a/src/ostree/main.c +++ b/src/ostree/main.c @@ -51,6 +51,9 @@ static OstreeCommand commands[] = { { "commit", OSTREE_BUILTIN_FLAG_NONE, ostree_builtin_commit, "Commit a new revision" }, + { "composefs-generate", OSTREE_BUILTIN_FLAG_NONE, + ostree_builtin_composefs_generate, + "Create a composefs image from a commit" }, { "config", OSTREE_BUILTIN_FLAG_NONE, ostree_builtin_config, "Change repo configuration settings" }, diff --git a/src/ostree/ot-builtin-composefs.c b/src/ostree/ot-builtin-composefs.c new file mode 100644 index 0000000000..efb406307c --- /dev/null +++ b/src/ostree/ot-builtin-composefs.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 Alexander Larsson + * + * 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 . + * + * Author: Alexander Larsson + */ + +#include "config.h" + +#include +#include + +#include "ot-main.h" +#include "ot-builtins.h" +#include "ostree.h" +#include "otutil.h" + +static char *opt_subpath; +static gboolean opt_union_add; +static gboolean opt_union_identical; +static gboolean opt_whiteouts; +static gboolean opt_from_stdin; +static gboolean opt_fsverity; +static char *opt_from_file; + +/* ATTENTION: + * Please remember to update the bash-completion script (bash/ostree) and + * man page (man/ostree-composefs-generate.xml) when changing the option list. + */ + +static GOptionEntry options[] = { + { "subpath", 0, 0, G_OPTION_ARG_FILENAME, &opt_subpath, "Process sub-directory PATH", "PATH" }, + { "fsverity", 0, 0, G_OPTION_ARG_NONE, &opt_fsverity, "Add fsverity digests for files", NULL }, + { "union-add", 0, 0, G_OPTION_ARG_NONE, &opt_union_add, "Keep existing files/directories, only add new", NULL }, + { "union-identical", 0, 0, G_OPTION_ARG_NONE, &opt_union_identical, "When layering checkouts, error out if a file would be replaced with a different version, but add new files and directories", NULL }, + { "whiteouts", 0, 0, G_OPTION_ARG_NONE, &opt_whiteouts, "Process 'whiteout' (Docker style) entries", NULL }, + { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL }, + { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" }, + { NULL } +}; + +static gboolean +process_many_commits (OstreeRepo *repo, + OstreeRepoCheckoutComposefsOptions *checkout_options, + OstreeComposefsTarget *composefs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gsize len; + GError *temp_error = NULL; + g_autoptr(GInputStream) instream = NULL; + g_autoptr(GDataInputStream) datastream = NULL; + g_autofree char *revision = NULL; + g_autofree char *subpath = NULL; + g_autofree char *resolved_commit = NULL; + + if (opt_from_stdin) + { + instream = (GInputStream*)g_unix_input_stream_new (0, FALSE); + } + else + { + g_autoptr(GFile) f = g_file_new_for_path (opt_from_file); + + instream = (GInputStream*)g_file_read (f, cancellable, error); + if (!instream) + goto out; + } + + datastream = g_data_input_stream_new (instream); + + while ((revision = g_data_input_stream_read_upto (datastream, "", 1, &len, + cancellable, &temp_error)) != NULL) + { + if (revision[0] == '\0') + break; + + /* Read the null byte */ + (void) g_data_input_stream_read_byte (datastream, cancellable, NULL); + g_free (subpath); + subpath = g_data_input_stream_read_upto (datastream, "", 1, &len, + cancellable, &temp_error); + if (temp_error) + { + g_propagate_error (error, temp_error); + goto out; + } + + /* Read the null byte */ + (void) g_data_input_stream_read_byte (datastream, cancellable, NULL); + + if (!ostree_repo_resolve_rev (repo, revision, FALSE, &resolved_commit, error)) + goto out; + + checkout_options->subpath = subpath; + if (!ostree_repo_checkout_composefs (repo, checkout_options, composefs, + resolved_commit, cancellable, error)) + { + g_prefix_error (error, "Processing tree %s: ", resolved_commit); + goto out; + } + + g_free (revision); + } + + if (temp_error) + { + g_propagate_error (error, temp_error); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_builtin_composefs_generate (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("COMMIT [DESTINATION]"); + g_autoptr(OstreeRepo) repo = NULL; + g_autoptr(OstreeComposefsTarget) composefs = ostree_composefs_target_new (); + const char *destination; + OstreeRepoCheckoutComposefsOptions checkout_options = { 0 }; + + if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) + return FALSE; + + if (argc < 2) + { + g_autofree char *help = g_option_context_get_help (context, TRUE, NULL); + g_printerr ("%s\n", help); + return glnx_throw (error, "COMMIT must be specified"); + } + + if (opt_union_add && opt_union_identical) + return glnx_throw (error, "Cannot specify both --union-add and --union-identical"); + if (opt_union_add) + checkout_options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES; + else if (opt_union_identical) + checkout_options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL; + else + checkout_options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES; + + + if (opt_whiteouts) + checkout_options.process_whiteouts = TRUE; + + if (opt_subpath) + checkout_options.subpath = opt_subpath; + + if (opt_fsverity) + checkout_options.enable_fsverity = TRUE; + + if (opt_from_stdin || opt_from_file) + { + destination = argv[1]; + + if (!process_many_commits (repo, &checkout_options, composefs, cancellable, error)) + return FALSE; + } + else + { + const char *commit = argv[1]; + if (argc < 3) + destination = commit; + else + destination = argv[2]; + + g_autofree char *resolved_commit = NULL; + if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) + return FALSE; + + if (!ostree_repo_checkout_composefs (repo, &checkout_options, composefs, + resolved_commit, cancellable, error)) + return FALSE; + } + + if (!ostree_composefs_target_write_at (composefs, + AT_FDCWD, destination, NULL, + cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h index 286c2e998e..603cf5688d 100644 --- a/src/ostree/ot-builtins.h +++ b/src/ostree/ot-builtins.h @@ -35,6 +35,7 @@ BUILTINPROTO(config); BUILTINPROTO(checkout); BUILTINPROTO(checksum); BUILTINPROTO(commit); +BUILTINPROTO(composefs_generate); BUILTINPROTO(diff); BUILTINPROTO(export); BUILTINPROTO(find_remotes);