mirror of
https://github.com/ostreedev/ostree.git
synced 2025-01-04 09:18:32 +03:00
1858d3d300
When recomputing selinux attrs during commit, we weren't sorting, which could cause various issues like fsck failures. This is a big hammer; change things so we always canonicalize (i.e. sort) the incoming xattrs when creating a file header and directory metadata. I think almost all places in the code were already keeping things sorted, but it's better to ensure correctness first. If we ever have some performance issue (I'm doubtful) we could add something like `_ostree_file_header_known_canonicalized` or so. Closes: https://github.com/ostreedev/ostree/issues/3343 Signed-off-by: Colin Walters <walters@verbum.org>
625 lines
22 KiB
C
625 lines
22 KiB
C
/*
|
|
* Copyright (C) 2016 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <err.h>
|
|
#include <gio/gio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "libglnx.h"
|
|
#include "libostreetest.h"
|
|
|
|
static void
|
|
test_repo_is_not_system (gconstpointer data)
|
|
{
|
|
OstreeRepo *repo = (void *)data;
|
|
g_assert (!ostree_repo_is_system (repo));
|
|
}
|
|
|
|
static GBytes *
|
|
input_stream_to_bytes (GInputStream *input)
|
|
{
|
|
g_autoptr (GOutputStream) mem_out_stream = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (input == NULL)
|
|
return g_bytes_new (NULL, 0);
|
|
|
|
mem_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
|
|
g_output_stream_splice (mem_out_stream, input, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem_out_stream));
|
|
}
|
|
|
|
static void
|
|
test_raw_file_to_archive_stream (gconstpointer data)
|
|
{
|
|
OstreeRepo *repo = OSTREE_REPO (data);
|
|
g_autofree gchar *commit_checksum = NULL;
|
|
g_autoptr (GHashTable) reachable = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
/* branch name of the test repository, see setup_test_repository in libtest.sh */
|
|
const gchar *rev = "test2";
|
|
GHashTableIter iter;
|
|
GVariant *serialized_object;
|
|
guint checks = 0;
|
|
|
|
ostree_repo_resolve_rev (repo, rev, FALSE, &commit_checksum, &error);
|
|
g_assert_no_error (error);
|
|
ostree_repo_traverse_commit (repo, commit_checksum, -1, &reachable, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_hash_table_iter_init (&iter, reachable);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *)&serialized_object, NULL))
|
|
{
|
|
const gchar *object_checksum;
|
|
OstreeObjectType object_type;
|
|
g_autoptr (GInputStream) input = NULL;
|
|
g_autoptr (GFileInfo) info = NULL;
|
|
g_autoptr (GVariant) xattrs = NULL;
|
|
g_autoptr (GBytes) input_bytes = NULL;
|
|
g_autoptr (GInputStream) mem_input = NULL;
|
|
g_autoptr (GInputStream) zlib_stream = NULL;
|
|
g_autoptr (GBytes) zlib_bytes = NULL;
|
|
g_autoptr (GInputStream) mem_zlib = NULL;
|
|
g_autoptr (GInputStream) input2 = NULL;
|
|
g_autoptr (GFileInfo) info2 = NULL;
|
|
g_autoptr (GVariant) xattrs2 = NULL;
|
|
g_autoptr (GBytes) input2_bytes = NULL;
|
|
|
|
ostree_object_name_deserialize (serialized_object, &object_checksum, &object_type);
|
|
if (object_type != OSTREE_OBJECT_TYPE_FILE)
|
|
continue;
|
|
|
|
ostree_repo_load_file (repo, object_checksum, &input, &info, &xattrs, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
input_bytes = input_stream_to_bytes (input);
|
|
/* This is to simulate NULL input received from
|
|
* ostree_repo_load_file. Instead of creating the mem_input
|
|
* variable, I could also rewind the input stream and pass it to
|
|
* the function below, but this would assume that the input
|
|
* stream implements either the GSeekable or
|
|
* GFileDescriptorBased interface. */
|
|
if (input != NULL)
|
|
mem_input = g_memory_input_stream_new_from_bytes (input_bytes);
|
|
ostree_raw_file_to_archive_z2_stream (mem_input, info, xattrs, &zlib_stream, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
zlib_bytes = input_stream_to_bytes (zlib_stream);
|
|
mem_zlib = g_memory_input_stream_new_from_bytes (zlib_bytes);
|
|
ostree_content_stream_parse (FALSE, mem_zlib, g_bytes_get_size (zlib_bytes), FALSE, &input2,
|
|
&info2, &xattrs2, NULL, &error);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
g_clear_error (&error);
|
|
|
|
g_seekable_seek (G_SEEKABLE (mem_zlib), 0, G_SEEK_SET, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
ostree_content_stream_parse (TRUE, mem_zlib, g_bytes_get_size (zlib_bytes), FALSE, &input2,
|
|
&info2, &xattrs2, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
input2_bytes = input_stream_to_bytes (input2);
|
|
g_assert_true (g_bytes_equal (input_bytes, input2_bytes));
|
|
g_assert_true (g_variant_equal (xattrs, xattrs2));
|
|
/* TODO: Not sure how to compare fileinfos */
|
|
++checks;
|
|
}
|
|
/* to make sure we really tested the function */
|
|
g_assert_cmpint (checks, >, 0);
|
|
}
|
|
|
|
static gboolean
|
|
basic_regfile_content_stream_new (const char *contents, GVariant *xattr, GInputStream **out_stream,
|
|
guint64 *out_length, GError **error)
|
|
{
|
|
const size_t len = strlen (contents);
|
|
g_autoptr (GMemoryInputStream) hi_memstream
|
|
= (GMemoryInputStream *)g_memory_input_stream_new_from_data (contents, len, NULL);
|
|
g_autoptr (GFileInfo) finfo = g_file_info_new ();
|
|
g_file_info_set_attribute_uint32 (finfo, "standard::type", G_FILE_TYPE_REGULAR);
|
|
g_file_info_set_attribute_boolean (finfo, "standard::is-symlink", FALSE);
|
|
g_file_info_set_size (finfo, len);
|
|
g_file_info_set_attribute_uint32 (finfo, "unix::uid", 0);
|
|
g_file_info_set_attribute_uint32 (finfo, "unix::gid", 0);
|
|
g_file_info_set_attribute_uint32 (finfo, "unix::mode", S_IFREG | 0644);
|
|
return ostree_raw_file_to_content_stream ((GInputStream *)hi_memstream, finfo, NULL, out_stream,
|
|
out_length, NULL, error);
|
|
}
|
|
|
|
static gboolean
|
|
hi_content_stream_new (GInputStream **out_stream, guint64 *out_length, GError **error)
|
|
{
|
|
return basic_regfile_content_stream_new ("hi", NULL, out_stream, out_length, error);
|
|
}
|
|
|
|
static void
|
|
test_validate_remotename (void)
|
|
{
|
|
const char *valid[] = { "foo", "hello-world" };
|
|
const char *invalid[] = { "foo/bar", "" };
|
|
for (guint i = 0; i < G_N_ELEMENTS (valid); i++)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
g_assert (ostree_validate_remote_name (valid[i], &error));
|
|
g_assert_no_error (error);
|
|
}
|
|
for (guint i = 0; i < G_N_ELEMENTS (invalid); i++)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
g_assert (!ostree_validate_remote_name (invalid[i], &error));
|
|
g_assert (error != NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_object_writes (gconstpointer data)
|
|
{
|
|
OstreeRepo *repo = OSTREE_REPO (data);
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
static const char hi_sha256[]
|
|
= "2301b5923720c3edc1f0467addb5c287fd5559e3e0cd1396e7f1edb6b01be9f0";
|
|
static const char invalid_hi_sha256[]
|
|
= "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe";
|
|
|
|
/* Successful content write */
|
|
{
|
|
g_autoptr (GInputStream) hi_memstream = NULL;
|
|
guint64 len;
|
|
hi_content_stream_new (&hi_memstream, &len, &error);
|
|
g_assert_no_error (error);
|
|
g_autofree guchar *csum = NULL;
|
|
(void)ostree_repo_write_content (repo, hi_sha256, hi_memstream, len, &csum, NULL, &error);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
/* Invalid content write */
|
|
{
|
|
g_autoptr (GInputStream) hi_memstream = NULL;
|
|
guint64 len;
|
|
hi_content_stream_new (&hi_memstream, &len, &error);
|
|
g_assert_no_error (error);
|
|
g_autofree guchar *csum = NULL;
|
|
g_assert (!ostree_repo_write_content (repo, invalid_hi_sha256, hi_memstream, len, &csum, NULL,
|
|
&error));
|
|
g_assert (error);
|
|
g_assert (strstr (error->message, "Corrupted file object"));
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
/* Test a basic regfile inline write, no xattrs */
|
|
g_assert (ostree_repo_write_regfile_inline (repo, hi_sha256, 0, 0, S_IFREG | 0644, NULL,
|
|
(guint8 *)"hi", 2, NULL, &error));
|
|
g_assert_no_error (error);
|
|
|
|
/* Test a basic regfile inline write, erroring on checksum */
|
|
g_assert (!ostree_repo_write_regfile_inline (repo, invalid_hi_sha256, 0, 0, S_IFREG | 0644, NULL,
|
|
(guint8 *)"hi", 2, NULL, &error));
|
|
g_assert (error != NULL);
|
|
g_clear_error (&error);
|
|
|
|
static const char expected_sha256_with_xattrs[]
|
|
= "72473a9e8ace75f89f1504137cfb134feb5372bc1d97e04eb5300e24ad836153";
|
|
|
|
GVariantBuilder builder;
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
|
|
g_variant_builder_add (&builder, "(^ay^ay)", "security.selinux", "foo_t");
|
|
g_variant_builder_add (&builder, "(^ay^ay)", "user.blah", "somedata");
|
|
g_autoptr (GVariant) inorder_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
|
|
g_variant_builder_add (&builder, "(^ay^ay)", "user.blah", "somedata");
|
|
g_variant_builder_add (&builder, "(^ay^ay)", "security.selinux", "foo_t");
|
|
g_autoptr (GVariant) unsorted_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
|
|
/* Now test with xattrs */
|
|
g_assert (ostree_repo_write_regfile_inline (repo, expected_sha256_with_xattrs, 0, 0,
|
|
S_IFREG | 0644, inorder_xattrs, (guint8 *)"hi", 2,
|
|
NULL, &error));
|
|
g_assert_no_error (error);
|
|
|
|
/* And now with a swapped order */
|
|
g_assert (ostree_repo_write_regfile_inline (repo, expected_sha256_with_xattrs, 0, 0,
|
|
S_IFREG | 0644, unsorted_xattrs, (guint8 *)"hi", 2,
|
|
NULL, &error));
|
|
g_assert_no_error (error);
|
|
|
|
/* Tests of directory metadata */
|
|
static const char expected_dirmeta_sha256[]
|
|
= "f773ab98198d8e46f77be6ffff5fc1920888c0af5794426c3b1461131d509f34";
|
|
g_autoptr (GFileInfo) fi = g_file_info_new ();
|
|
g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
|
|
g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
|
|
g_file_info_set_attribute_uint32 (fi, "unix::mode", (0755 | S_IFDIR));
|
|
{
|
|
g_autoptr (GVariant) dirmeta = ostree_create_directory_metadata (fi, inorder_xattrs);
|
|
ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, expected_dirmeta_sha256, dirmeta,
|
|
NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
}
|
|
/* And now with unsorted xattrs */
|
|
{
|
|
g_autoptr (GVariant) dirmeta = ostree_create_directory_metadata (fi, unsorted_xattrs);
|
|
ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, expected_dirmeta_sha256, dirmeta,
|
|
NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
impl_test_break_hardlink (int tmp_dfd, const char *path, GError **error)
|
|
{
|
|
const char *linkedpath = glnx_strjoina (path, ".link");
|
|
struct stat orig_stbuf;
|
|
if (!glnx_fstatat (tmp_dfd, path, &orig_stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
return FALSE;
|
|
|
|
/* Calling ostree_break_hardlink() should be a noop */
|
|
struct stat stbuf;
|
|
if (!ostree_break_hardlink (tmp_dfd, path, TRUE, NULL, error))
|
|
return FALSE;
|
|
if (!glnx_fstatat (tmp_dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
return FALSE;
|
|
|
|
g_assert_cmpint (orig_stbuf.st_dev, ==, stbuf.st_dev);
|
|
g_assert_cmpint (orig_stbuf.st_ino, ==, stbuf.st_ino);
|
|
|
|
if (linkat (tmp_dfd, path, tmp_dfd, linkedpath, 0) < 0)
|
|
return glnx_throw_errno_prefix (error, "linkat");
|
|
|
|
if (!ostree_break_hardlink (tmp_dfd, path, TRUE, NULL, error))
|
|
return FALSE;
|
|
if (!glnx_fstatat (tmp_dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
return FALSE;
|
|
/* This file should be different */
|
|
g_assert_cmpint (orig_stbuf.st_dev, ==, stbuf.st_dev);
|
|
g_assert_cmpint (orig_stbuf.st_ino, !=, stbuf.st_ino);
|
|
/* But this one is still the same */
|
|
if (!glnx_fstatat (tmp_dfd, linkedpath, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
|
return FALSE;
|
|
g_assert_cmpint (orig_stbuf.st_dev, ==, stbuf.st_dev);
|
|
g_assert_cmpint (orig_stbuf.st_ino, ==, stbuf.st_ino);
|
|
|
|
(void)unlinkat (tmp_dfd, path, 0);
|
|
(void)unlinkat (tmp_dfd, linkedpath, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
test_break_hardlink (void)
|
|
{
|
|
int tmp_dfd = AT_FDCWD;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
/* Regular file */
|
|
const char hello_hardlinked_content[] = "hello hardlinked content";
|
|
glnx_file_replace_contents_at (tmp_dfd, "test-hardlink", (guint8 *)hello_hardlinked_content,
|
|
strlen (hello_hardlinked_content), GLNX_FILE_REPLACE_NODATASYNC,
|
|
NULL, &error);
|
|
g_assert_no_error (error);
|
|
(void)impl_test_break_hardlink (tmp_dfd, "test-hardlink", &error);
|
|
g_assert_no_error (error);
|
|
|
|
/* Symlink */
|
|
if (symlinkat ("some-path", tmp_dfd, "test-symhardlink") < 0)
|
|
err (1, "symlinkat");
|
|
(void)impl_test_break_hardlink (tmp_dfd, "test-symhardlink", &error);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
static GVariant *
|
|
xattr_cb (OstreeRepo *repo, const char *path, GFileInfo *file_info, gpointer user_data)
|
|
{
|
|
GVariant *xattr = user_data;
|
|
if (g_str_equal (path, "/baz/cow"))
|
|
return g_variant_ref (xattr);
|
|
return NULL;
|
|
}
|
|
|
|
/* check that using a devino cache doesn't cause us to ignore xattr callbacks */
|
|
static void
|
|
test_devino_cache_xattrs (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
gboolean ret = FALSE;
|
|
|
|
g_autoptr (GFile) repo_path = g_file_new_for_path ("repo");
|
|
|
|
/* re-initialize as bare */
|
|
ret = ot_test_run_libtest ("setup_test_repository bare", &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
gboolean can_relabel;
|
|
ret = ot_check_relabeling (&can_relabel, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
gboolean has_user_xattrs;
|
|
ret = ot_check_user_xattrs (&has_user_xattrs, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
/* we need both because we're bare and our tests target user xattrs */
|
|
if (!can_relabel || !has_user_xattrs)
|
|
{
|
|
g_test_skip ("this test requires full xattr support");
|
|
return;
|
|
}
|
|
|
|
g_autoptr (OstreeRepo) repo = ostree_repo_new (repo_path);
|
|
ret = ostree_repo_open (repo, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autofree char *csum = NULL;
|
|
ret = ostree_repo_resolve_rev (repo, "test2", FALSE, &csum, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (OstreeRepoDevInoCache) cache = ostree_repo_devino_cache_new ();
|
|
|
|
OstreeRepoCheckoutAtOptions options = {
|
|
0,
|
|
};
|
|
options.no_copy_fallback = TRUE;
|
|
options.devino_to_csum_cache = cache;
|
|
ret = ostree_repo_checkout_at (repo, &options, AT_FDCWD, "checkout", csum, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (OstreeMutableTree) mtree = ostree_mutable_tree_new ();
|
|
g_autoptr (OstreeRepoCommitModifier) modifier
|
|
= ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
|
|
ostree_repo_commit_modifier_set_devino_cache (modifier, cache);
|
|
|
|
g_auto (GVariantBuilder) builder;
|
|
g_variant_builder_init (&builder, (GVariantType *)"a(ayay)");
|
|
g_variant_builder_add (&builder, "(@ay@ay)", g_variant_new_bytestring ("user.myattr"),
|
|
g_variant_new_bytestring ("data"));
|
|
g_autoptr (GVariant) orig_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
|
|
ret = ostree_repo_prepare_transaction (repo, NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
ostree_repo_commit_modifier_set_xattr_callback (modifier, xattr_cb, NULL, orig_xattrs);
|
|
ret = ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, "checkout", mtree, modifier, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (GFile) root = NULL;
|
|
ret = ostree_repo_write_mtree (repo, mtree, &root, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
/* now check that the final xattr matches */
|
|
g_autoptr (GFile) baz_child = g_file_get_child (root, "baz");
|
|
g_autoptr (GFile) cow_child = g_file_get_child (baz_child, "cow");
|
|
|
|
g_autoptr (GVariant) xattrs = NULL;
|
|
ret = ostree_repo_file_get_xattrs (OSTREE_REPO_FILE (cow_child), &xattrs, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
gboolean found_xattr = FALSE;
|
|
gsize n = g_variant_n_children (xattrs);
|
|
for (gsize i = 0; i < n; i++)
|
|
{
|
|
const guint8 *name;
|
|
const guint8 *value;
|
|
g_variant_get_child (xattrs, i, "(^&ay^&ay)", &name, &value);
|
|
|
|
if (g_str_equal ((const char *)name, "user.myattr"))
|
|
{
|
|
g_assert_cmpstr ((const char *)value, ==, "data");
|
|
found_xattr = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_assert (found_xattr);
|
|
|
|
OstreeRepoTransactionStats stats;
|
|
ret = ostree_repo_commit_transaction (repo, &stats, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
/* we should only have had to checksum /baz/cow */
|
|
g_assert_cmpint (stats.content_objects_written, ==, 1);
|
|
}
|
|
|
|
/* https://github.com/ostreedev/ostree/issues/1721
|
|
* We should be able to commit large metadata objects now.
|
|
*/
|
|
static void
|
|
test_big_metadata (void)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
gboolean ret = FALSE;
|
|
|
|
g_autoptr (GFile) repo_path = g_file_new_for_path ("repo");
|
|
|
|
/* init as bare-user-only so we run everywhere */
|
|
ret = ot_test_run_libtest ("setup_test_repository bare-user-only", &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (OstreeRepo) repo = ostree_repo_new (repo_path);
|
|
ret = ostree_repo_open (repo, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (GFile) object_to_commit = NULL;
|
|
ret = ostree_repo_read_commit (repo, "test2", &object_to_commit, NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
g_autoptr (OstreeMutableTree) mtree = ostree_mutable_tree_new ();
|
|
ret = ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
|
|
const size_t len = OSTREE_MAX_METADATA_SIZE + 1;
|
|
g_assert_cmpint (len, >, OSTREE_MAX_METADATA_SIZE);
|
|
g_autofree char *large_buf = g_malloc (len);
|
|
memset (large_buf, 0x42, len);
|
|
g_autoptr (GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
|
g_autofree char *commit_checksum = NULL;
|
|
g_variant_builder_add (
|
|
builder, "{sv}", "large-value",
|
|
g_variant_new_fixed_array ((GVariantType *)"y", large_buf, len, sizeof (char)));
|
|
ret = ostree_repo_write_commit (repo, NULL, NULL, NULL, g_variant_builder_end (builder),
|
|
OSTREE_REPO_FILE (object_to_commit), &commit_checksum, NULL,
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert (ret);
|
|
}
|
|
|
|
static void
|
|
compare_xattrs (GVariant *orig, GVariant *new)
|
|
{
|
|
g_assert_cmpint (g_variant_n_children (orig) + 1, ==, g_variant_n_children (new));
|
|
GVariant *kp, *vp;
|
|
GVariantIter iter;
|
|
g_variant_iter_init (&iter, new);
|
|
bool found = false;
|
|
while (g_variant_iter_loop (&iter, "(@ay@ay)", &kp, &vp))
|
|
{
|
|
const char *k = g_variant_get_bytestring (kp);
|
|
if (!g_str_equal (k, "user.ostreetesting"))
|
|
continue;
|
|
g_assert_cmpint (g_variant_get_size (vp), ==, 4);
|
|
found = true;
|
|
}
|
|
g_assert (found);
|
|
}
|
|
|
|
static void
|
|
test_read_xattrs (void)
|
|
{
|
|
g_autoptr (GError) local_error = NULL;
|
|
GError **error = &local_error;
|
|
|
|
g_auto (GLnxTmpDir) tmpd = {
|
|
0,
|
|
};
|
|
// Use /var/tmp to hope we get xattr support
|
|
glnx_mkdtempat (AT_FDCWD, "/var/tmp/ostree-xattrs-test.XXXXXX", 0700, &tmpd, error);
|
|
g_assert_no_error (local_error);
|
|
|
|
const char value[] = "foo";
|
|
|
|
{
|
|
g_autoptr (GVariant) current_xattrs = ostree_fs_get_all_xattrs (tmpd.fd, NULL, error);
|
|
g_assert_no_error (local_error);
|
|
|
|
int r = fsetxattr (tmpd.fd, "user.ostreetesting", value, sizeof (value), 0);
|
|
|
|
if (r != 0)
|
|
{
|
|
g_autofree gchar *message = g_strdup_printf (
|
|
"Unable to set extended attributes in /var/tmp: %s", g_strerror (errno));
|
|
g_test_skip (message);
|
|
return;
|
|
}
|
|
|
|
g_autoptr (GVariant) new_xattrs = ostree_fs_get_all_xattrs (tmpd.fd, NULL, error);
|
|
g_assert_no_error (local_error);
|
|
|
|
compare_xattrs (current_xattrs, new_xattrs);
|
|
}
|
|
|
|
{
|
|
if (symlinkat ("nosuchtarget", tmpd.fd, "somelink") < 0)
|
|
glnx_throw_errno_prefix (error, "symlinkat");
|
|
g_assert_no_error (local_error);
|
|
|
|
g_autoptr (GVariant) current_xattrs
|
|
= ostree_fs_get_all_xattrs_at (tmpd.fd, "somelink", NULL, error);
|
|
g_assert_no_error (local_error);
|
|
(void)current_xattrs;
|
|
|
|
// OK, can't do user. xattrs on symlinks unfortunately.
|
|
|
|
// char pathbuf[PATH_MAX];
|
|
// snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", tmpd.fd, "somelink");
|
|
// int r = lsetxattr (pathbuf, "user.ostreetesting", value, sizeof (value), 0);
|
|
// if (r < 0)
|
|
// glnx_throw_errno_prefix (error, "lsetxattr");
|
|
// g_assert_no_error (local_error);
|
|
|
|
// g_autoptr(GVariant) new_xattrs = ostree_fs_get_all_xattrs_at (tmpd.fd, "somelink", NULL,
|
|
// error); g_assert_no_error (local_error);
|
|
|
|
// compare_xattrs (current_xattrs, new_xattrs);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_dirmeta_xattrs (void)
|
|
{
|
|
g_autoptr (GError) local_error = NULL;
|
|
GError **error = &local_error;
|
|
const guint32 uidgid = GUINT32_TO_BE (42);
|
|
const guint32 mode = GUINT32_TO_BE (S_IFDIR | S_IRWXU);
|
|
g_autoptr (GVariantBuilder) xattr_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ayay)"));
|
|
const char *data = "data";
|
|
g_variant_builder_add (xattr_builder, "(@ay@ay)", g_variant_new_bytestring (""),
|
|
g_variant_new_bytestring (data));
|
|
g_autoptr (GVariant) dirmeta = g_variant_new ("(uuu@a(ayay))", uidgid, uidgid, mode,
|
|
g_variant_builder_end (xattr_builder));
|
|
g_assert (!ostree_validate_structureof_dirmeta (dirmeta, error));
|
|
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
glnx_unref_object OstreeRepo *repo = NULL;
|
|
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
repo = ot_test_setup_repo (NULL, &error);
|
|
if (!repo)
|
|
goto out;
|
|
|
|
g_test_add_data_func ("/repo-not-system", repo, test_repo_is_not_system);
|
|
g_test_add_data_func ("/raw-file-to-archive-stream", repo, test_raw_file_to_archive_stream);
|
|
g_test_add_data_func ("/objectwrites", repo, test_object_writes);
|
|
g_test_add_func ("/xattrs-devino-cache", test_devino_cache_xattrs);
|
|
g_test_add_func ("/break-hardlink", test_break_hardlink);
|
|
g_test_add_func ("/remotename", test_validate_remotename);
|
|
g_test_add_func ("/big-metadata", test_big_metadata);
|
|
g_test_add_func ("/read-xattrs", test_read_xattrs);
|
|
g_test_add_func ("/dirmeta-xattrs", test_dirmeta_xattrs);
|
|
|
|
return g_test_run ();
|
|
out:
|
|
if (error)
|
|
g_error ("%s", error->message);
|
|
return 1;
|
|
}
|