From 08e98e904289464e00cd68ac7aa675d23d78971f Mon Sep 17 00:00:00 2001 From: Luca BRUNO Date: Wed, 2 Mar 2022 16:45:00 +0000 Subject: [PATCH] lib/core: introduce 'bare-split-xattrs' mode --- Makefile-tests.am | 1 + docs/formats.md | 24 +++++++++++++++----- docs/repo.md | 32 ++++++++++++++++++++++----- src/libostree/ostree-core-private.h | 3 ++- src/libostree/ostree-core.c | 1 + src/libostree/ostree-core.h | 2 ++ src/libostree/ostree-repo.c | 5 +++++ tests/test-basic-bare-split-xattrs.sh | 25 +++++++++++++++++++++ 8 files changed, 82 insertions(+), 11 deletions(-) create mode 100755 tests/test-basic-bare-split-xattrs.sh diff --git a/Makefile-tests.am b/Makefile-tests.am index 6bae65cf..5c97bd84 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -59,6 +59,7 @@ test_programs = \ $(NULL) _installed_or_uninstalled_test_scripts = \ tests/test-basic.sh \ + tests/test-basic-bare-split-xattrs.sh \ tests/test-basic-user.sh \ tests/test-basic-user-only.sh \ tests/test-basic-root.sh \ diff --git a/docs/formats.md b/docs/formats.md index 0943aafa..c3723279 100644 --- a/docs/formats.md +++ b/docs/formats.md @@ -63,18 +63,32 @@ Other disadvantages of `archive`: - One doesn't know the total size (compressed or uncompressed) of content before downloading everything -## Aside: the bare and bare-user formats +## Aside: bare formats -The most common operation is to pull from an `archive` repository -into a `bare` or `bare-user` formatted repository. These latter two -are not compressed on disk. In other words, pulling to them is -similar to unpacking (but not installing) an RPM/deb package. +The most common operation is to pull from a remote `archive` repository +into a local one. This latter is not compressed on disk. In other +words, pulling to a local repository is similar to unpacking (but not +installing) the content of an RPM/deb package. + +The `bare` repository format is the simplest one. In this mode regular files +are directly stored to disk, and all metadata (e.g. uid/gid and xattrs) is +reflected to the filesystem. +It allows further direct access to content and metadata, but it may require +elevated privileges when writing objects to the repository. The `bare-user` format is a bit special in that the uid/gid and xattrs from the content are ignored. This is primarily useful if you want to have the same OSTree-managed content that can be run on a host system or an unprivileged container. +Similarly, the `bare-split-xattrs` format is a special mode where xattrs +are stored as separate repository objects, and not directly reflected to +the filesystem. +This is primarily useful when transporting xattrs through lossy environments +(e.g. tar streams and containerized environments). It also allows carrying +security-sensitive xattrs (e.g. SELinux labels) out-of-band without involving +OS filesystem logic. + ## Static deltas OSTree itself was originally focused on a continuous delivery model, where diff --git a/docs/repo.md b/docs/repo.md index 69f26172..580281ca 100644 --- a/docs/repo.md +++ b/docs/repo.md @@ -81,15 +81,37 @@ warnings such as GNU Tar emitting "implausibly old time stamp" with 0; however, until we have a mechanism to transition cleanly to 1, for compatibilty OSTree is reverted to use zero again. +### Xattrs objects + +In some repository modes (e.g. `bare-split-xattrs`), xattrs are stored on the +side of the content objects they refer to. This is done via two dedicated +object types, `file-xattrs` and `file-xattrs-link`. + +`file-xattrs` store xattrs data, encoded as GVariant. Each object is keyed by +the checksum of the xattrs content, allowing for multiple references. + +`file-xattrs-link` are hardlinks which are associated to file objects. +Each object is keyed by the same checksum of the corresponding file +object. The target of the hardlink is an existing `file-xattrs` object. +In case of reaching the limit of too many links, this object could be +a plain file too. + # Repository types and locations -Also unlike git, an OSTree repository can be in one of four separate -modes: `bare`, `bare-user`, `bare-user-only`, and `archive`. A bare repository is -one where content files are just stored as regular files; it's -designed to be the source of a "hardlink farm", where each operating -system checkout is merely links into it. If you want to store files +Also unlike git, an OSTree repository can be in one of five separate +modes: `bare`, `bare-split-xattrs, ``bare-user`, `bare-user-only`, and +`archive`. + +A `bare` repository is one where content files are just stored as regular +files; it's designed to be the source of a "hardlink farm", where each +operating system checkout is merely links into it. If you want to store files owned by e.g. root in this mode, you must run OSTree as root. +The `bare-split-xattrs` mode is similar to the above one, but it does store +xattrs as separate objects. This is meant to avoid conflicts with +kernel-enforced constraints (e.g. on SELinux labels) and with other softwares +that may perform ephemeral changes to xattrs (e.g. container runtimes). + The `bare-user` mode is a later addition that is like `bare` in that files are unpacked, but it can (and should generally) be created as non-root. In this mode, extended metadata such as owner uid, gid, and diff --git a/src/libostree/ostree-core-private.h b/src/libostree/ostree-core-private.h index 34f86a6c..2bd2f984 100644 --- a/src/libostree/ostree-core-private.h +++ b/src/libostree/ostree-core-private.h @@ -197,7 +197,8 @@ _ostree_repo_mode_is_bare (OstreeRepoMode mode) return mode == OSTREE_REPO_MODE_BARE || mode == OSTREE_REPO_MODE_BARE_USER || - mode == OSTREE_REPO_MODE_BARE_USER_ONLY; + mode == OSTREE_REPO_MODE_BARE_USER_ONLY || + mode == OSTREE_REPO_MODE_BARE_SPLIT_XATTRS; } #ifndef OSTREE_DISABLE_GPGME diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 2ff72086..f0d0e698 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -39,6 +39,7 @@ G_STATIC_ASSERT(OSTREE_REPO_MODE_ARCHIVE_Z2 == 1); G_STATIC_ASSERT(OSTREE_REPO_MODE_ARCHIVE == OSTREE_REPO_MODE_ARCHIVE_Z2); G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_USER == 2); G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_USER_ONLY == 3); +G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_SPLIT_XATTRS == 4); static GBytes *variant_to_lenprefixed_buffer (GVariant *variant); diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 638c40ac..9a14192d 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -193,6 +193,7 @@ typedef enum { * @OSTREE_REPO_MODE_ARCHIVE_Z2: Legacy alias for `OSTREE_REPO_MODE_ARCHIVE` * @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user. Hardlinks work only in user checkouts. * @OSTREE_REPO_MODE_BARE_USER_ONLY: Same as BARE_USER, but all metadata is not stored, so it can only be used for user checkouts. Does not need xattrs. + * @OSTREE_REPO_MODE_BARE_SPLIT_XATTRS: Same as BARE_USER, but xattrs are stored separately from file content, with dedicated object types. * * See the documentation of #OstreeRepo for more information about the * possible modes. @@ -203,6 +204,7 @@ typedef enum { OSTREE_REPO_MODE_ARCHIVE_Z2 = OSTREE_REPO_MODE_ARCHIVE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY, + OSTREE_REPO_MODE_BARE_SPLIT_XATTRS, } OstreeRepoMode; /** diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 6c541029..71a01d59 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -2704,6 +2704,9 @@ ostree_repo_mode_to_string (OstreeRepoMode mode, /* Legacy alias */ ret_mode ="archive-z2"; break; + case OSTREE_REPO_MODE_BARE_SPLIT_XATTRS: + ret_mode = "bare-split-xattrs"; + break; default: return glnx_throw (error, "Invalid mode '%d'", mode); } @@ -2734,6 +2737,8 @@ ostree_repo_mode_from_string (const char *mode, else if (strcmp (mode, "archive-z2") == 0 || strcmp (mode, "archive") == 0) ret_mode = OSTREE_REPO_MODE_ARCHIVE; + else if (strcmp (mode, "bare-split-xattrs") == 0) + ret_mode = OSTREE_REPO_MODE_BARE_SPLIT_XATTRS; else return glnx_throw (error, "Invalid mode '%s' in repository configuration", mode); diff --git a/tests/test-basic-bare-split-xattrs.sh b/tests/test-basic-bare-split-xattrs.sh new file mode 100755 index 00000000..8bd6430d --- /dev/null +++ b/tests/test-basic-bare-split-xattrs.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# SPDX-License-Identifier: LGPL-2.0+ + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +mode="bare-split-xattrs" +OSTREE="${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo" + +cd ${test_tmpdir} +${OSTREE} init --mode "${mode}" +${OSTREE} config get core.mode > mode.txt +assert_file_has_content mode.txt "${mode}" +tap_ok "repo init" +rm -rf -- repo mode.txt + +cd ${test_tmpdir} +${OSTREE} init --mode "${mode}" +${OSTREE} fsck --all +tap_ok "repo fsck" +rm -rf -- repo + +tap_end