Merge pull request #26092 from poettering/dissect-sector-size
auto-detect intended sector size from DDI disk images, and configure loopback devices for it
This commit is contained in:
@@ -2093,6 +2093,7 @@ int setup_namespace(
|
||||
r = loop_device_make_by_path(
|
||||
root_image,
|
||||
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
|
||||
/* sector_size= */ UINT32_MAX,
|
||||
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
|
||||
LOCK_SH,
|
||||
&loop_device);
|
||||
|
||||
@@ -601,9 +601,13 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
else if (arg_json_format_flags & JSON_FORMAT_OFF)
|
||||
printf(" Size: %s\n", FORMAT_BYTES(size));
|
||||
|
||||
printf(" Sec. Size: %" PRIu32 "\n", m->sector_size);
|
||||
|
||||
if (arg_json_format_flags & JSON_FORMAT_OFF)
|
||||
putc('\n', stdout);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
r = dissected_image_acquire_metadata(m, 0);
|
||||
if (r == -ENXIO)
|
||||
return log_error_errno(r, "No root partition discovered.");
|
||||
@@ -704,6 +708,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)),
|
||||
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)),
|
||||
JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
|
||||
JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)),
|
||||
JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)),
|
||||
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
|
||||
JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)),
|
||||
@@ -1437,9 +1442,9 @@ static int run(int argc, char *argv[]) {
|
||||
loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
|
||||
|
||||
if (arg_in_memory)
|
||||
r = loop_device_make_by_path_memory(arg_image, open_flags, loop_flags, LOCK_SH, &d);
|
||||
r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
else
|
||||
r = loop_device_make_by_path(arg_image, open_flags, loop_flags, LOCK_SH, &d);
|
||||
r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
|
||||
|
||||
|
||||
@@ -1378,7 +1378,15 @@ int home_setup_luks(
|
||||
return r;
|
||||
}
|
||||
|
||||
r = loop_device_make(setup->image_fd, O_RDWR, offset, size, user_record_luks_sector_size(h), 0, LOCK_UN, &setup->loop);
|
||||
r = loop_device_make(
|
||||
setup->image_fd,
|
||||
O_RDWR,
|
||||
offset,
|
||||
size,
|
||||
h->luks_sector_size == UINT64_MAX ? UINT32_MAX : user_record_luks_sector_size(h), /* if sector size is not specified, select UINT32_MAX, i.e. auto-probe */
|
||||
/* loop_flags= */ 0,
|
||||
LOCK_UN,
|
||||
&setup->loop);
|
||||
if (r == -ENOENT) {
|
||||
log_error_errno(r, "Loopback block device support is not available on this system.");
|
||||
return -ENOLINK; /* make recognizable */
|
||||
@@ -1832,6 +1840,7 @@ static int luks_format(
|
||||
|
||||
static int make_partition_table(
|
||||
int fd,
|
||||
uint32_t sector_size,
|
||||
const char *label,
|
||||
sd_id128_t uuid,
|
||||
uint64_t *ret_offset,
|
||||
@@ -1859,7 +1868,7 @@ static int make_partition_table(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize partition type: %m");
|
||||
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open device: %m");
|
||||
|
||||
@@ -2309,6 +2318,7 @@ int home_create_luks(
|
||||
|
||||
r = make_partition_table(
|
||||
setup->image_fd,
|
||||
user_record_luks_sector_size(h),
|
||||
user_record_user_name_and_realm(h),
|
||||
partition_uuid,
|
||||
&partition_offset,
|
||||
@@ -2319,7 +2329,15 @@ int home_create_luks(
|
||||
|
||||
log_info("Writing of partition table completed.");
|
||||
|
||||
r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, user_record_luks_sector_size(h), 0, LOCK_EX, &setup->loop);
|
||||
r = loop_device_make(
|
||||
setup->image_fd,
|
||||
O_RDWR,
|
||||
partition_offset,
|
||||
partition_size,
|
||||
user_record_luks_sector_size(h),
|
||||
0,
|
||||
LOCK_EX,
|
||||
&setup->loop);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container
|
||||
* or similar and loopback bock devices are not available, return a
|
||||
@@ -2688,7 +2706,7 @@ static int prepare_resize_partition(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, UINT32_MAX, &c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open device: %m");
|
||||
|
||||
@@ -2772,6 +2790,7 @@ static int apply_resize_partition(
|
||||
|
||||
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
|
||||
_cleanup_free_ void *two_zero_lbas = NULL;
|
||||
uint32_t ssz;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
@@ -2792,18 +2811,22 @@ static int apply_resize_partition(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to change partition size: %m");
|
||||
|
||||
two_zero_lbas = malloc0(1024U);
|
||||
r = probe_sector_size(fd, &ssz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine current sector size: %m");
|
||||
|
||||
two_zero_lbas = malloc0(ssz * 2);
|
||||
if (!two_zero_lbas)
|
||||
return log_oom();
|
||||
|
||||
/* libfdisk appears to get confused by the existing PMBR. Let's explicitly flush it out. */
|
||||
n = pwrite(fd, two_zero_lbas, 1024U, 0);
|
||||
n = pwrite(fd, two_zero_lbas, ssz * 2, 0);
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to wipe partition table: %m");
|
||||
if (n != 1024)
|
||||
if ((size_t) n != ssz * 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while wiping partition table.");
|
||||
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, ssz, &c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open device: %m");
|
||||
|
||||
|
||||
@@ -5726,6 +5726,7 @@ static int run(int argc, char *argv[]) {
|
||||
r = loop_device_make_by_path(
|
||||
arg_image,
|
||||
arg_read_only ? O_RDONLY : O_RDWR,
|
||||
/* sector_size= */ UINT32_MAX,
|
||||
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
|
||||
LOCK_SH,
|
||||
&loop);
|
||||
|
||||
+26
-9
@@ -1978,11 +1978,26 @@ static int context_load_partition_table(Context *context) {
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
if (arg_sector_size > 0) {
|
||||
if (arg_sector_size > 0)
|
||||
r = fdisk_save_user_sector_size(c, /* phy= */ 0, arg_sector_size);
|
||||
else {
|
||||
uint32_t ssz;
|
||||
|
||||
if (context->backing_fd < 0) {
|
||||
context->backing_fd = open(context->node, O_RDONLY|O_CLOEXEC);
|
||||
if (context->backing_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open device '%s': %m", context->node);
|
||||
}
|
||||
|
||||
/* Auto-detect sector size if not specified. */
|
||||
r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set sector size: %m");
|
||||
return log_error_errno(r, "Failed to probe sector size of '%s': %m", context->node);
|
||||
|
||||
r = fdisk_save_user_sector_size(c, /* phy= */ 0, ssz);
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set sector size: %m");
|
||||
|
||||
/* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
|
||||
* /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
|
||||
@@ -1990,7 +2005,6 @@ static int context_load_partition_table(Context *context) {
|
||||
c,
|
||||
context->backing_fd >= 0 ? FORMAT_PROC_FD_PATH(context->backing_fd) : context->node,
|
||||
arg_dry_run);
|
||||
|
||||
if (r == -EINVAL && arg_size_auto) {
|
||||
struct stat st;
|
||||
|
||||
@@ -6160,7 +6174,7 @@ static int find_root(Context *context) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
|
||||
}
|
||||
|
||||
static int resize_pt(int fd) {
|
||||
static int resize_pt(int fd, uint64_t sector_size) {
|
||||
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
|
||||
int r;
|
||||
|
||||
@@ -6168,7 +6182,7 @@ static int resize_pt(int fd) {
|
||||
* possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
|
||||
* immediately write it again, with no changes. */
|
||||
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
|
||||
r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
|
||||
|
||||
@@ -6192,7 +6206,8 @@ static int resize_backing_fd(
|
||||
const char *node, /* The primary way we access the disk image to operate on */
|
||||
int *fd, /* An O_RDONLY fd referring to that inode */
|
||||
const char *backing_file, /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
|
||||
LoopDevice *loop_device) {
|
||||
LoopDevice *loop_device,
|
||||
uint64_t sector_size) {
|
||||
|
||||
_cleanup_close_ int writable_fd = -EBADF;
|
||||
uint64_t current_size;
|
||||
@@ -6304,7 +6319,7 @@ static int resize_backing_fd(
|
||||
node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
|
||||
|
||||
done:
|
||||
r = resize_pt(writable_fd);
|
||||
r = resize_pt(writable_fd, sector_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -6429,7 +6444,8 @@ static int run(int argc, char *argv[]) {
|
||||
context->node,
|
||||
&context->backing_fd,
|
||||
node_is_our_loop ? arg_image : NULL,
|
||||
node_is_our_loop ? loop_device : NULL);
|
||||
node_is_our_loop ? loop_device : NULL,
|
||||
context->sector_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -6503,7 +6519,8 @@ static int run(int argc, char *argv[]) {
|
||||
context->node,
|
||||
&context->backing_fd,
|
||||
node_is_our_loop ? arg_image : NULL,
|
||||
node_is_our_loop ? loop_device : NULL);
|
||||
node_is_our_loop ? loop_device : NULL,
|
||||
context->sector_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ static int portable_extract_by_path(
|
||||
|
||||
assert(path);
|
||||
|
||||
r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
|
||||
r = loop_device_make_by_path(path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
|
||||
if (r == -EISDIR) {
|
||||
_cleanup_free_ char *image_name = NULL;
|
||||
|
||||
|
||||
@@ -762,3 +762,18 @@ int blockdev_reread_partition_table(sd_device *dev) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blockdev_get_sector_size(int fd, uint32_t *ret) {
|
||||
int ssz = 0;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (ioctl(fd, BLKSSZGET, &ssz) < 0)
|
||||
return -errno;
|
||||
if (ssz <= 0) /* make sure the field is initialized */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Block device reported invalid sector size %i.", ssz);
|
||||
|
||||
*ret = ssz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -54,3 +54,5 @@ int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret);
|
||||
int block_device_remove_all_partitions(sd_device *dev, int fd);
|
||||
int block_device_has_partitions(sd_device *dev);
|
||||
int blockdev_reread_partition_table(sd_device *dev);
|
||||
|
||||
int blockdev_get_sector_size(int fd, uint32_t *ret);
|
||||
|
||||
@@ -1197,7 +1197,7 @@ int image_read_metadata(Image *i) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
|
||||
r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
|
||||
r = loop_device_make_by_path(i->path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+114
-1
@@ -62,6 +62,7 @@
|
||||
#include "raw-clone.h"
|
||||
#include "resize-fs.h"
|
||||
#include "signal-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
@@ -107,6 +108,105 @@ int dissect_fstype_ok(const char *fstype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int probe_sector_size(int fd, uint32_t *ret) {
|
||||
|
||||
struct gpt_header {
|
||||
char signature[8];
|
||||
le32_t revision;
|
||||
le32_t header_size;
|
||||
le32_t crc32;
|
||||
le32_t reserved;
|
||||
le64_t my_lba;
|
||||
le64_t alternate_lba;
|
||||
le64_t first_usable_lba;
|
||||
le64_t last_usable_lba;
|
||||
sd_id128_t disk_guid;
|
||||
le64_t partition_entry_lba;
|
||||
le32_t number_of_partition_entries;
|
||||
le32_t size_of_partition_entry;
|
||||
le32_t partition_entry_array_crc32;
|
||||
} _packed_;
|
||||
|
||||
/* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching
|
||||
* for the GPT headers at the relevant byte offsets */
|
||||
|
||||
assert_cc(sizeof(struct gpt_header) == 92);
|
||||
|
||||
/* We expect a sector size in the range 512…4096. The GPT header is located in the second
|
||||
* sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
|
||||
* read with granularity of the largest sector size we care about. Which means 8K. */
|
||||
uint8_t sectors[2 * 4096];
|
||||
uint32_t found = 0;
|
||||
ssize_t n;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
n = pread(fd, sectors, sizeof(sectors), 0);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n != sizeof(sectors)) /* too short? */
|
||||
goto not_found;
|
||||
|
||||
/* Let's see if we find the GPT partition header with various expected sector sizes */
|
||||
for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
|
||||
struct gpt_header *p;
|
||||
|
||||
assert(sizeof(sectors) >= sz * 2);
|
||||
p = (struct gpt_header*) (sectors + sz);
|
||||
|
||||
if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
|
||||
continue;
|
||||
|
||||
if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
|
||||
continue;
|
||||
|
||||
if (le32toh(p->header_size) < sizeof(struct gpt_header))
|
||||
continue;
|
||||
|
||||
if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
|
||||
continue;
|
||||
|
||||
if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
|
||||
continue;
|
||||
|
||||
if (found != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
|
||||
"Detected valid partition table at offsets matching multiple sector sizes, refusing.");
|
||||
|
||||
found = sz;
|
||||
}
|
||||
|
||||
if (found != 0) {
|
||||
log_debug("Determined sector size %" PRIu32 " based on discovered partition table.", found);
|
||||
*ret = found;
|
||||
return 1; /* indicate we *did* find it */
|
||||
}
|
||||
|
||||
not_found:
|
||||
log_debug("Couldn't find any partition table to derive sector size of.");
|
||||
*ret = 512; /* pick the traditional default */
|
||||
return 0; /* indicate we didn't find it */
|
||||
}
|
||||
|
||||
int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret) {
|
||||
struct stat st;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
/* Just like probe_sector_size(), but if we are looking at a block device, will use the already
|
||||
* configured sector size rather than probing by contents */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (S_ISBLK(st.st_mode))
|
||||
return blockdev_get_sector_size(fd, ret);
|
||||
|
||||
return probe_sector_size(fd, ret);
|
||||
}
|
||||
|
||||
int probe_filesystem_full(
|
||||
int fd,
|
||||
const char *path,
|
||||
@@ -464,6 +564,7 @@ static int dissect_image(
|
||||
assert(!verity || verity->root_hash_sig || verity->root_hash_sig_size == 0);
|
||||
assert(!verity || (verity->root_hash || !verity->root_hash_sig));
|
||||
assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
|
||||
assert(m->sector_size > 0);
|
||||
|
||||
/* Probes a disk image, and returns information about what it found in *ret.
|
||||
*
|
||||
@@ -512,6 +613,11 @@ static int dissect_image(
|
||||
if (r != 0)
|
||||
return errno_or_else(ENOMEM);
|
||||
|
||||
errno = 0;
|
||||
r = blkid_probe_set_sectorsize(b, m->sector_size);
|
||||
if (r != 0)
|
||||
return errno_or_else(EIO);
|
||||
|
||||
if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
|
||||
/* Look for file system superblocks, unless we only shall look for GPT partition tables */
|
||||
blkid_probe_enable_superblocks(b, 1);
|
||||
@@ -1244,6 +1350,10 @@ int dissect_image_file(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = probe_sector_size(fd, &m->sector_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(m, fd, path, verity, mount_options, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -3061,6 +3171,7 @@ int dissect_loop_device(
|
||||
return r;
|
||||
|
||||
m->loop = loop_device_ref(loop);
|
||||
m->sector_size = m->loop->sector_size;
|
||||
|
||||
r = dissect_image(m, loop->fd, loop->node, verity, mount_options, flags);
|
||||
if (r < 0)
|
||||
@@ -3236,6 +3347,7 @@ int mount_image_privately_interactively(
|
||||
r = loop_device_make_by_path(
|
||||
image,
|
||||
FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
|
||||
/* sector_size= */ UINT32_MAX,
|
||||
FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
|
||||
LOCK_SH,
|
||||
&d);
|
||||
@@ -3327,7 +3439,8 @@ int verity_dissect_and_mount(
|
||||
* accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
|
||||
r = loop_device_make_by_path(
|
||||
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
|
||||
-1,
|
||||
/* open_flags= */ -1,
|
||||
/* sector_size= */ UINT32_MAX,
|
||||
verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
|
||||
LOCK_SH,
|
||||
&loop_device);
|
||||
|
||||
@@ -93,6 +93,8 @@ struct DissectedImage {
|
||||
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
|
||||
DecryptedImage *decrypted_image;
|
||||
|
||||
uint32_t sector_size;
|
||||
|
||||
/* Meta information extracted from /etc/os-release and similar */
|
||||
char *image_name;
|
||||
sd_id128_t image_uuid;
|
||||
@@ -186,3 +188,6 @@ int mount_image_privately_interactively(const char *path, DissectImageFlags flag
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
|
||||
|
||||
int dissect_fstype_ok(const char *fstype);
|
||||
|
||||
int probe_sector_size(int fd, uint32_t *ret);
|
||||
int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret);
|
||||
|
||||
+19
-1
@@ -1,11 +1,17 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdisk-util.h"
|
||||
|
||||
#if HAVE_LIBFDISK
|
||||
|
||||
int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret) {
|
||||
int fdisk_new_context_fd(
|
||||
int fd,
|
||||
bool read_only,
|
||||
uint32_t sector_size,
|
||||
struct fdisk_context **ret) {
|
||||
|
||||
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
|
||||
int r;
|
||||
|
||||
@@ -18,6 +24,18 @@ int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret) {
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sector_size == UINT32_MAX) {
|
||||
r = probe_sector_size_prefer_ioctl(fd, §or_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sector_size != 0) {
|
||||
r = fdisk_save_user_sector_size(c, /* phy= */ 0, sector_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), read_only);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -14,7 +14,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_partition*, fdisk_unref_partition,
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_parttype*, fdisk_unref_parttype, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_table*, fdisk_unref_table, NULL);
|
||||
|
||||
int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret);
|
||||
int fdisk_new_context_fd(int fd, bool read_only, uint32_t sector_size, struct fdisk_context **ret);
|
||||
|
||||
int fdisk_partition_get_uuid_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
|
||||
int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
|
||||
|
||||
+81
-12
@@ -20,6 +20,7 @@
|
||||
#include "data-fd-util.h"
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
@@ -125,14 +126,16 @@ static int loop_configure_verify(int fd, const struct loop_config *c) {
|
||||
assert(c);
|
||||
|
||||
if (c->block_size != 0) {
|
||||
int z;
|
||||
uint32_t ssz;
|
||||
|
||||
if (ioctl(fd, BLKSSZGET, &z) < 0)
|
||||
return -errno;
|
||||
r = blockdev_get_sector_size(fd, &ssz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(z >= 0);
|
||||
if ((uint32_t) z != c->block_size)
|
||||
log_debug("LOOP_CONFIGURE didn't honour requested block size %u, got %i instead. Ignoring.", c->block_size, z);
|
||||
if (ssz != c->block_size) {
|
||||
log_debug("LOOP_CONFIGURE didn't honour requested block size %" PRIu32 ", got %" PRIu32 " instead. Ignoring.", c->block_size, ssz);
|
||||
broken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->info.lo_sizelimit != 0) {
|
||||
@@ -173,6 +176,7 @@ static int loop_configure_verify(int fd, const struct loop_config *c) {
|
||||
|
||||
static int loop_configure_fallback(int fd, const struct loop_config *c) {
|
||||
struct loop_info64 info_copy;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(c);
|
||||
@@ -220,6 +224,21 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) {
|
||||
if (ioctl(fd, BLKFLSBUF, 0) < 0)
|
||||
log_debug_errno(errno, "Failed to issue BLKFLSBUF ioctl, ignoring: %m");
|
||||
|
||||
/* If a block size is requested then try to configure it. If that doesn't work, ignore errors, but
|
||||
* afterwards, let's validate what is in effect, and if it doesn't match what we want, fail */
|
||||
if (c->block_size != 0) {
|
||||
uint32_t ssz;
|
||||
|
||||
if (ioctl(fd, LOOP_SET_BLOCK_SIZE, (unsigned long) c->block_size) < 0)
|
||||
log_debug_errno(errno, "Failed to set sector size, ignoring: %m");
|
||||
|
||||
r = blockdev_get_sector_size(fd, &ssz);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read sector size: %m");
|
||||
if (ssz != c->block_size)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Sector size of loopback device doesn't match what we requested, refusing.");
|
||||
}
|
||||
|
||||
/* LO_FLAGS_DIRECT_IO is a flags we need to configure via explicit ioctls. */
|
||||
if (FLAGS_SET(c->info.lo_flags, LO_FLAGS_DIRECT_IO))
|
||||
if (ioctl(fd, LOOP_SET_DIRECT_IO, 1UL) < 0)
|
||||
@@ -391,6 +410,7 @@ static int loop_configure(
|
||||
.diskseq = diskseq,
|
||||
.uevent_seqnum_not_before = seqnum,
|
||||
.timestamp_not_before = timestamp,
|
||||
.sector_size = c->block_size,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(d);
|
||||
@@ -403,7 +423,7 @@ static int loop_device_make_internal(
|
||||
int open_flags,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
uint32_t block_size,
|
||||
uint32_t sector_size,
|
||||
uint32_t loop_flags,
|
||||
int lock_op,
|
||||
LoopDevice **ret) {
|
||||
@@ -474,9 +494,50 @@ static int loop_device_make_internal(
|
||||
if (control < 0)
|
||||
return -errno;
|
||||
|
||||
if (sector_size == 0)
|
||||
/* If no sector size is specified, default to the classic default */
|
||||
sector_size = 512;
|
||||
else if (sector_size == UINT32_MAX) {
|
||||
|
||||
if (S_ISBLK(st.st_mode))
|
||||
/* If the sector size is specified as UINT32_MAX we'll propagate the sector size of
|
||||
* the underlying block device. */
|
||||
r = blockdev_get_sector_size(fd, §or_size);
|
||||
else {
|
||||
_cleanup_close_ int non_direct_io_fd = -1;
|
||||
int probe_fd;
|
||||
|
||||
assert(S_ISREG(st.st_mode));
|
||||
|
||||
/* If sector size is specified as UINT32_MAX, we'll try to probe the right sector
|
||||
* size of the image in question by looking for the GPT partition header at various
|
||||
* offsets. This of course only works if the image already has a disk label.
|
||||
*
|
||||
* So here we actually want to read the file contents ourselves. This is quite likely
|
||||
* not going to work if we managed to enable O_DIRECT, because in such a case there
|
||||
* are some pretty strict alignment requirements to offset, size and target, but
|
||||
* there's no way to query what alignment specifically is actually required. Hence,
|
||||
* let's avoid the mess, and temporarily open an fd without O_DIRECT for the probing
|
||||
* logic. */
|
||||
|
||||
if (FLAGS_SET(loop_flags, LO_FLAGS_DIRECT_IO)) {
|
||||
non_direct_io_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (non_direct_io_fd < 0)
|
||||
return non_direct_io_fd;
|
||||
|
||||
probe_fd = non_direct_io_fd;
|
||||
} else
|
||||
probe_fd = fd;
|
||||
|
||||
r = probe_sector_size(probe_fd, §or_size);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
config = (struct loop_config) {
|
||||
.fd = fd,
|
||||
.block_size = block_size,
|
||||
.block_size = sector_size,
|
||||
.info = {
|
||||
/* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */
|
||||
.lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((open_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
|
||||
@@ -560,7 +621,7 @@ int loop_device_make(
|
||||
int open_flags,
|
||||
uint64_t offset,
|
||||
uint64_t size,
|
||||
uint32_t block_size,
|
||||
uint32_t sector_size,
|
||||
uint32_t loop_flags,
|
||||
int lock_op,
|
||||
LoopDevice **ret) {
|
||||
@@ -574,7 +635,7 @@ int loop_device_make(
|
||||
open_flags,
|
||||
offset,
|
||||
size,
|
||||
block_size,
|
||||
sector_size,
|
||||
loop_flags_mangle(loop_flags),
|
||||
lock_op,
|
||||
ret);
|
||||
@@ -583,6 +644,7 @@ int loop_device_make(
|
||||
int loop_device_make_by_path(
|
||||
const char *path,
|
||||
int open_flags,
|
||||
uint32_t sector_size,
|
||||
uint32_t loop_flags,
|
||||
int lock_op,
|
||||
LoopDevice **ret) {
|
||||
@@ -638,12 +700,13 @@ int loop_device_make_by_path(
|
||||
direct ? "enabled" : "disabled",
|
||||
direct != (direct_flags != 0) ? " (O_DIRECT was requested but not supported)" : "");
|
||||
|
||||
return loop_device_make_internal(path, fd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
|
||||
return loop_device_make_internal(path, fd, open_flags, 0, 0, sector_size, loop_flags, lock_op, ret);
|
||||
}
|
||||
|
||||
int loop_device_make_by_path_memory(
|
||||
const char *path,
|
||||
int open_flags,
|
||||
uint32_t sector_size,
|
||||
uint32_t loop_flags,
|
||||
int lock_op,
|
||||
LoopDevice **ret) {
|
||||
@@ -679,7 +742,7 @@ int loop_device_make_by_path_memory(
|
||||
|
||||
fd = safe_close(fd); /* Let's close the original early */
|
||||
|
||||
return loop_device_make_internal(NULL, mfd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
|
||||
return loop_device_make_internal(NULL, mfd, open_flags, 0, 0, sector_size, loop_flags, lock_op, ret);
|
||||
}
|
||||
|
||||
static LoopDevice* loop_device_free(LoopDevice *d) {
|
||||
@@ -821,6 +884,11 @@ int loop_device_open(
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
uint32_t sector_size;
|
||||
r = blockdev_get_sector_size(fd, §or_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_get_devnum(dev, &devnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -850,6 +918,7 @@ int loop_device_open(
|
||||
.diskseq = diskseq,
|
||||
.uevent_seqnum_not_before = UINT64_MAX,
|
||||
.timestamp_not_before = USEC_INFINITY,
|
||||
.sector_size = sector_size,
|
||||
};
|
||||
|
||||
*ret = d;
|
||||
|
||||
@@ -23,14 +23,15 @@ struct LoopDevice {
|
||||
uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */
|
||||
uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */
|
||||
usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
|
||||
uint32_t sector_size;
|
||||
};
|
||||
|
||||
/* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */
|
||||
#define LOOP_DEVICE_IS_FOREIGN(d) ((d)->nr < 0)
|
||||
|
||||
int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_make_by_path_memory(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_make_by_path(const char *path, int open_flags, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_make_by_path_memory(const char *path, int open_flags, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_open(sd_device *dev, int open_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_open_from_fd(int fd, int open_flags, int lock_op, LoopDevice **ret);
|
||||
int loop_device_open_from_path(const char *path, int open_flags, int lock_op, LoopDevice **ret);
|
||||
|
||||
@@ -540,6 +540,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
r = loop_device_make_by_path(
|
||||
img->path,
|
||||
O_RDONLY,
|
||||
/* sector_size= */ UINT32_MAX,
|
||||
FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
|
||||
LOCK_SH,
|
||||
&d);
|
||||
|
||||
Reference in New Issue
Block a user