import,pull: add --zero-fill= switch to fill up the destination range with zeroes
When writing a raw image into a section of an existing file or block device (i.e. with --offset= and --size-max=, as sysupdate does when updating a partition), we so far wrote precisely as many bytes as the payload was large, leaving whatever followed in the destination range untouched. For some payloads that's a problem: a verity signature partition for example must carry NUL bytes only after the JSON object, and if a shorter signature is written over a longer one, the tail of the old one survives. Add a --zero-fill= boolean switch to both "systemd-import raw" and "systemd-pull raw", which fills up the remainder of the destination range (from the end of the payload up to --size-max=) with zeroes once the payload has been written, via blockdev_zero_out(). It defaults to off, and requires both --offset= and --size-max= to be specified. (cherry picked from commit aff5aff407d421fbedc9eb0229c40996ca5570a1)
This commit is contained in:
committed by
Luca Boccassi
parent
c4e7a0fdfe
commit
f9cfc019f9
@@ -6,6 +6,7 @@
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "acl-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "compress.h"
|
||||
#include "dirent-util.h"
|
||||
@@ -432,3 +433,26 @@ int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_zero_fill(int fd, uint64_t offset, uint64_t size_max, uint64_t skip) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(offset != UINT64_MAX);
|
||||
assert(size_max != UINT64_MAX);
|
||||
|
||||
/* Fill up whatever is left of the destination range (i.e. the range starting at 'offset' and
|
||||
* 'size_max' bytes long, minus the first 'skip' bytes we already wrote) with zeroes, so that no
|
||||
* remnants of previous, larger contents survive after our data. */
|
||||
|
||||
if (skip > size_max)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Number of bytes to skip larger than maximum size of destination range.");
|
||||
if (offset > UINT64_MAX - skip)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Destination offset overflows.");
|
||||
|
||||
r = blockdev_zero_out(fd, offset + skip, size_max - skip);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fill up destination with zeroes: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,17 @@ typedef enum ImportFlags {
|
||||
IMPORT_DIRECT = 1 << 6, /* import without rename games */
|
||||
IMPORT_SYNC = 1 << 7, /* fsync() right before we are done */
|
||||
IMPORT_FOREIGN_UID = 1 << 8, /* tar: go via nsresourced/mountfsd and make owned by foreign UID */
|
||||
IMPORT_ZERO_FILL = 1 << 9, /* raw: fill up the remainder of the destination range (as defined by offset/size max) with zeroes */
|
||||
|
||||
/* When pulling these flags are defined too */
|
||||
IMPORT_PULL_SETTINGS = 1 << 9, /* download .nspawn settings file */
|
||||
IMPORT_PULL_ROOTHASH = 1 << 10, /* only for raw: download .roothash file for verity */
|
||||
IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 11, /* only for raw: download .roothash.p7s file for verity */
|
||||
IMPORT_PULL_VERITY = 1 << 12, /* only for raw: download .verity file for verity */
|
||||
IMPORT_PULL_SETTINGS = 1 << 10, /* download .nspawn settings file */
|
||||
IMPORT_PULL_ROOTHASH = 1 << 11, /* only for raw: download .roothash file for verity */
|
||||
IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 12, /* only for raw: download .roothash.p7s file for verity */
|
||||
IMPORT_PULL_VERITY = 1 << 13, /* only for raw: download .verity file for verity */
|
||||
|
||||
/* The supported flags for the tar and raw importing */
|
||||
IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC|IMPORT_FOREIGN_UID,
|
||||
IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
|
||||
IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC|IMPORT_ZERO_FILL,
|
||||
|
||||
/* The supported flags for the tar, raw, oci pulling */
|
||||
IMPORT_PULL_FLAGS_MASK_TAR = IMPORT_FLAGS_MASK_TAR|IMPORT_PULL_KEEP_DOWNLOAD|IMPORT_PULL_SETTINGS,
|
||||
@@ -49,3 +50,5 @@ int import_allocate_event_with_signals(sd_event **ret);
|
||||
int import_make_foreign_userns(int *userns_fd);
|
||||
|
||||
int import_remove_tree(const char *path, int *userns_fd, ImportFlags flags);
|
||||
|
||||
int import_zero_fill(int fd, uint64_t offset, uint64_t size_max, uint64_t skip);
|
||||
|
||||
@@ -217,6 +217,12 @@ static int raw_import_finish(RawImport *i) {
|
||||
assert(i);
|
||||
assert(i->output_fd >= 0);
|
||||
|
||||
if (FLAGS_SET(i->flags, IMPORT_ZERO_FILL)) {
|
||||
r = import_zero_fill(i->output_fd, i->offset, i->size_max, i->written_uncompressed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Nothing of what is below applies to block devices */
|
||||
if (S_ISBLK(i->output_stat.st_mode)) {
|
||||
|
||||
@@ -510,6 +516,7 @@ int raw_import_start(
|
||||
assert(local);
|
||||
assert(!(flags & ~IMPORT_FLAGS_MASK_RAW));
|
||||
assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
|
||||
assert(!FLAGS_SET(flags, IMPORT_ZERO_FILL) || (offset != UINT64_MAX && size_max != UINT64_MAX));
|
||||
|
||||
if (!import_validate_local(local, flags))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -343,6 +343,13 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
|
||||
break;
|
||||
|
||||
OPTION_LONG("zero-fill", "BOOL", "Controls whether to fill up the destination range with zeroes"):
|
||||
r = parse_boolean_argument("--zero-fill=", opts.arg, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
SET_FLAG(arg_import_flags, IMPORT_ZERO_FILL, r);
|
||||
break;
|
||||
|
||||
OPTION_LONG("offset", "BYTES", "Offset to seek to in destination"): {
|
||||
uint64_t u;
|
||||
|
||||
@@ -397,6 +404,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
|
||||
|
||||
if (FLAGS_SET(arg_import_flags, IMPORT_ZERO_FILL) && (arg_offset == UINT64_MAX || arg_size_max == UINT64_MAX))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Zero filling only supported if both --offset= and --size-max= are specified.");
|
||||
|
||||
if (!arg_image_root) {
|
||||
r = image_root_pick(arg_runtime_scope < 0 ? RUNTIME_SCOPE_SYSTEM : arg_runtime_scope, arg_class, /* runtime= */ false, &arg_image_root);
|
||||
if (r < 0)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "import-common.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
@@ -458,6 +459,12 @@ static int pull_job_curl_on_finished(CurlSlot *slot, CURL *curl, CURLcode result
|
||||
return pull_job_finish(j, log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum of downloaded resource does not match expected checksum, yikes."));
|
||||
}
|
||||
|
||||
if (j->disk_fd >= 0 && j->zero_fill) {
|
||||
r = import_zero_fill(j->disk_fd, j->offset, j->uncompressed_max, j->written_uncompressed);
|
||||
if (r < 0)
|
||||
return pull_job_finish(j, r);
|
||||
}
|
||||
|
||||
/* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no
|
||||
* offset is specified, which indicates we only own the file partially) */
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef struct PullJob {
|
||||
|
||||
bool sync;
|
||||
bool force_memory;
|
||||
bool zero_fill;
|
||||
|
||||
char *authentication_challenge;
|
||||
} PullJob;
|
||||
|
||||
@@ -810,6 +810,7 @@ int raw_pull_start(
|
||||
assert((verify < 0) || !iovec_is_set(checksum));
|
||||
assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW));
|
||||
assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
|
||||
assert(!FLAGS_SET(flags, IMPORT_ZERO_FILL) || (offset != UINT64_MAX && size_max != UINT64_MAX));
|
||||
assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !(flags & IMPORT_DIRECT));
|
||||
assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !iovec_is_set(checksum));
|
||||
|
||||
@@ -859,6 +860,7 @@ int raw_pull_start(
|
||||
p->raw_job->uncompressed_max = size_max;
|
||||
if (offset != UINT64_MAX)
|
||||
p->raw_job->offset = p->offset = offset;
|
||||
p->raw_job->zero_fill = FLAGS_SET(flags, IMPORT_ZERO_FILL);
|
||||
|
||||
if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
|
||||
r = pull_find_old_etags(url, p->image_root, DT_REG, ".raw-", ".raw", &p->raw_job->old_etags);
|
||||
|
||||
@@ -459,6 +459,13 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
|
||||
break;
|
||||
|
||||
OPTION_LONG("zero-fill", "BOOL", "Controls whether to fill up the destination range with zeroes"):
|
||||
r = parse_boolean_argument("--zero-fill=", opts.arg, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
SET_FLAG(arg_import_flags, IMPORT_ZERO_FILL, r);
|
||||
break;
|
||||
|
||||
OPTION_LONG("offset", "BYTES", "Offset to seek to in destination"): {
|
||||
uint64_t u;
|
||||
|
||||
@@ -523,6 +530,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
|
||||
|
||||
if (FLAGS_SET(arg_import_flags, IMPORT_ZERO_FILL) && (arg_offset == UINT64_MAX || arg_size_max == UINT64_MAX))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Zero filling only supported if both --offset= and --size-max= are specified.");
|
||||
|
||||
if (iovec_is_set(&arg_checksum) && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user