mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-22 17:35:55 +03:00
repo: Add a checkout option to not hardlink zero-sized files
In rpm-ostree we've hit a few cases where hardlinking zero-sized files causes us problems. The most prominent is lock files in `/usr/etc`, such as `/usr/etc/selinux/semanage.LOCK`. If there are two zero-sized lock files to grab, but they're hardlinked, then locking will fail. Another case here is if one is using ostree inside a container and don't have access to FUSE (i.e. `rofiles-fuse`), then the ostree hardlinking can cause files that aren't ordinarily hardlinked to become so, and mutation of one mutates all. An example where this is concerning is Python `__init__.py` files. Now, these lock files should clearly not be in the tree to begin with, but - we're not gaining a huge amount by hardlinking these files either, so let's add an option to disable it. Closes: #1752 Approved by: jlebon
This commit is contained in:
parent
c70526841e
commit
673cacd633
@ -586,6 +586,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
|||||||
const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK);
|
const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK);
|
||||||
const gboolean is_whiteout = (!is_symlink && options->process_whiteouts &&
|
const gboolean is_whiteout = (!is_symlink && options->process_whiteouts &&
|
||||||
g_str_has_prefix (destination_name, WHITEOUT_PREFIX));
|
g_str_has_prefix (destination_name, WHITEOUT_PREFIX));
|
||||||
|
const gboolean is_reg_zerosized = (!is_symlink && g_file_info_get_size (source_info) == 0);
|
||||||
|
|
||||||
/* First, see if it's a Docker whiteout,
|
/* First, see if it's a Docker whiteout,
|
||||||
* https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go
|
* https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go
|
||||||
@ -604,6 +605,10 @@ checkout_one_file_at (OstreeRepo *repo,
|
|||||||
|
|
||||||
need_copy = FALSE;
|
need_copy = FALSE;
|
||||||
}
|
}
|
||||||
|
else if (options->force_copy_zerosized && is_reg_zerosized)
|
||||||
|
{
|
||||||
|
need_copy = TRUE;
|
||||||
|
}
|
||||||
else if (!options->force_copy)
|
else if (!options->force_copy)
|
||||||
{
|
{
|
||||||
HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED;
|
HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED;
|
||||||
@ -699,6 +704,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
|||||||
if (can_cache
|
if (can_cache
|
||||||
&& !is_whiteout
|
&& !is_whiteout
|
||||||
&& !is_symlink
|
&& !is_symlink
|
||||||
|
&& !is_reg_zerosized
|
||||||
&& need_copy
|
&& need_copy
|
||||||
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE
|
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE
|
||||||
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
|
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||||
@ -762,7 +768,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
|||||||
* succeeded at hardlinking above.
|
* succeeded at hardlinking above.
|
||||||
*/
|
*/
|
||||||
if (options->no_copy_fallback)
|
if (options->no_copy_fallback)
|
||||||
g_assert (is_bare_user_symlink);
|
g_assert (is_bare_user_symlink || is_reg_zerosized);
|
||||||
if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs,
|
if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs,
|
||||||
cancellable, error))
|
cancellable, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -933,7 +933,8 @@ typedef struct {
|
|||||||
gboolean no_copy_fallback;
|
gboolean no_copy_fallback;
|
||||||
gboolean force_copy; /* Since: 2017.6 */
|
gboolean force_copy; /* Since: 2017.6 */
|
||||||
gboolean bareuseronly_dirs; /* Since: 2017.7 */
|
gboolean bareuseronly_dirs; /* Since: 2017.7 */
|
||||||
gboolean unused_bools[5];
|
gboolean force_copy_zerosized; /* Since: 2018.9 */
|
||||||
|
gboolean unused_bools[4];
|
||||||
/* 4 byte hole on 64 bit */
|
/* 4 byte hole on 64 bit */
|
||||||
|
|
||||||
const char *subpath;
|
const char *subpath;
|
||||||
|
@ -45,6 +45,7 @@ static char *opt_from_file;
|
|||||||
static gboolean opt_disable_fsync;
|
static gboolean opt_disable_fsync;
|
||||||
static gboolean opt_require_hardlinks;
|
static gboolean opt_require_hardlinks;
|
||||||
static gboolean opt_force_copy;
|
static gboolean opt_force_copy;
|
||||||
|
static gboolean opt_force_copy_zerosized;
|
||||||
static gboolean opt_bareuseronly_dirs;
|
static gboolean opt_bareuseronly_dirs;
|
||||||
static char *opt_skiplist_file;
|
static char *opt_skiplist_file;
|
||||||
static char *opt_selinux_policy;
|
static char *opt_selinux_policy;
|
||||||
@ -84,6 +85,7 @@ static GOptionEntry options[] = {
|
|||||||
{ "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
|
{ "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
|
||||||
{ "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" },
|
{ "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" },
|
||||||
{ "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
|
{ "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
|
||||||
|
{ "force-copy-zerosized", 'z', 0, G_OPTION_ARG_NONE, &opt_force_copy_zerosized, "Do not hardlink zero-sized files", NULL },
|
||||||
{ "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL },
|
{ "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL },
|
||||||
{ "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL },
|
{ "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL },
|
||||||
{ "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
|
{ "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
|
||||||
@ -130,7 +132,8 @@ process_one_checkout (OstreeRepo *repo,
|
|||||||
* convenient infrastructure for testing C APIs with data.
|
* convenient infrastructure for testing C APIs with data.
|
||||||
*/
|
*/
|
||||||
if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks ||
|
if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks ||
|
||||||
opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical ||
|
opt_union_add || opt_force_copy || opt_force_copy_zerosized ||
|
||||||
|
opt_bareuseronly_dirs || opt_union_identical ||
|
||||||
opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix)
|
opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix)
|
||||||
{
|
{
|
||||||
OstreeRepoCheckoutAtOptions options = { 0, };
|
OstreeRepoCheckoutAtOptions options = { 0, };
|
||||||
@ -218,6 +221,7 @@ process_one_checkout (OstreeRepo *repo,
|
|||||||
|
|
||||||
options.no_copy_fallback = opt_require_hardlinks;
|
options.no_copy_fallback = opt_require_hardlinks;
|
||||||
options.force_copy = opt_force_copy;
|
options.force_copy = opt_force_copy;
|
||||||
|
options.force_copy_zerosized = opt_force_copy_zerosized;
|
||||||
options.bareuseronly_dirs = opt_bareuseronly_dirs;
|
options.bareuseronly_dirs = opt_bareuseronly_dirs;
|
||||||
|
|
||||||
if (!ostree_repo_checkout_at (repo, &options,
|
if (!ostree_repo_checkout_at (repo, &options,
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "1..$((83 + ${extra_basic_tests:-0}))"
|
echo "1..$((84 + ${extra_basic_tests:-0}))"
|
||||||
|
|
||||||
CHECKOUT_U_ARG=""
|
CHECKOUT_U_ARG=""
|
||||||
CHECKOUT_H_ARGS="-H"
|
CHECKOUT_H_ARGS="-H"
|
||||||
@ -694,6 +694,21 @@ for v in bin link; do
|
|||||||
done
|
done
|
||||||
echo "ok checkout union identical conflicts"
|
echo "ok checkout union identical conflicts"
|
||||||
|
|
||||||
|
cd ${test_tmpdir}
|
||||||
|
rm files -rf && mkdir files
|
||||||
|
touch files/anemptyfile
|
||||||
|
touch files/anotheremptyfile
|
||||||
|
$CMD_PREFIX ostree --repo=repo commit --consume -b tree-with-empty-files --tree=dir=files
|
||||||
|
$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} -z tree-with-empty-files tree-with-empty-files
|
||||||
|
if files_are_hardlinked tree-with-empty-files/an{,other}emptyfile; then
|
||||||
|
fatal "--force-copy-zerosized failed"
|
||||||
|
fi
|
||||||
|
rm tree-with-empty-files -rf
|
||||||
|
$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} tree-with-empty-files tree-with-empty-files
|
||||||
|
assert_files_hardlinked tree-with-empty-files/an{,other}emptyfile
|
||||||
|
rm tree-with-empty-files -rf
|
||||||
|
echo "ok checkout --force-copy-zerosized"
|
||||||
|
|
||||||
cd ${test_tmpdir}
|
cd ${test_tmpdir}
|
||||||
rm files -rf && mkdir files
|
rm files -rf && mkdir files
|
||||||
mkdir files/worldwritable-dir
|
mkdir files/worldwritable-dir
|
||||||
|
Loading…
Reference in New Issue
Block a user