Merge pull request #7335 from poettering/dissect-meta-info
beef up image dissection, to gather image metadata
This commit is contained in:
@@ -25,19 +25,28 @@
|
||||
#endif
|
||||
#endif
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "architecture.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "blkid-util.h"
|
||||
#include "copy.h"
|
||||
#include "def.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "linux-3.13/dm-ioctl.h"
|
||||
#include "mount-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
@@ -635,6 +644,10 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
|
||||
free(m->partitions[i].decrypted_node);
|
||||
}
|
||||
|
||||
free(m->hostname);
|
||||
strv_free(m->machine_info);
|
||||
strv_free(m->os_release);
|
||||
|
||||
free(m);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1193,6 +1206,174 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
|
||||
enum {
|
||||
META_HOSTNAME,
|
||||
META_MACHINE_ID,
|
||||
META_MACHINE_INFO,
|
||||
META_OS_RELEASE,
|
||||
_META_MAX,
|
||||
};
|
||||
|
||||
static const char *const paths[_META_MAX] = {
|
||||
[META_HOSTNAME] = "/etc/hostname\0",
|
||||
[META_MACHINE_ID] = "/etc/machine-id\0",
|
||||
[META_MACHINE_INFO] = "/etc/machine-info\0",
|
||||
[META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0",
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
|
||||
_cleanup_(rmdir_and_freep) char *t = NULL;
|
||||
_cleanup_(sigkill_waitp) pid_t child = 0;
|
||||
sd_id128_t machine_id = SD_ID128_NULL;
|
||||
_cleanup_free_ char *hostname = NULL;
|
||||
unsigned n_meta_initialized = 0, k;
|
||||
int fds[2 * _META_MAX], r;
|
||||
siginfo_t si;
|
||||
|
||||
BLOCK_SIGNALS(SIGCHLD);
|
||||
|
||||
assert(m);
|
||||
|
||||
for (; n_meta_initialized < _META_MAX; n_meta_initialized ++)
|
||||
if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
child = raw_clone(SIGCHLD|CLONE_NEWNS);
|
||||
if (child < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
|
||||
(void) reset_all_signal_handlers();
|
||||
(void) reset_signal_mask();
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
/* Make sure we never propagate to the host */
|
||||
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
for (k = 0; k < _META_MAX; k++) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
const char *p;
|
||||
|
||||
fds[2*k] = safe_close(fds[2*k]);
|
||||
|
||||
NULSTR_FOREACH(p, paths[k]) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
|
||||
r = chase_symlinks(p, t, CHASE_PREFIX_ROOT, &q);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd >= 0)
|
||||
break;
|
||||
}
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
for (k = 0; k < _META_MAX; k++) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
|
||||
f = fdopen(fds[2*k], "re");
|
||||
if (!f) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fds[2*k] = -1;
|
||||
|
||||
switch (k) {
|
||||
|
||||
case META_HOSTNAME:
|
||||
r = read_etc_hostname_stream(f, &hostname);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read /etc/hostname: %m");
|
||||
|
||||
break;
|
||||
|
||||
case META_MACHINE_ID: {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read /etc/machine-id: %m");
|
||||
else if (r == 33) {
|
||||
r = sd_id128_from_string(line, &machine_id);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
|
||||
} else if (r == 0)
|
||||
log_debug("/etc/machine-id file is empty.");
|
||||
else
|
||||
log_debug("/etc/machine-id has unexpected length %i.", r);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case META_MACHINE_INFO:
|
||||
r = load_env_file_pairs(f, "machine-info", NULL, &machine_info);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read /etc/machine-info: %m");
|
||||
|
||||
break;
|
||||
|
||||
case META_OS_RELEASE:
|
||||
r = load_env_file_pairs(f, "os-release", NULL, &os_release);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read OS release file: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = wait_for_terminate(child, &si);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
child = 0;
|
||||
|
||||
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
|
||||
r = -EPROTO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
free_and_replace(m->hostname, hostname);
|
||||
m->machine_id = machine_id;
|
||||
strv_free_and_replace(m->machine_info, machine_info);
|
||||
strv_free_and_replace(m->os_release, os_release);
|
||||
|
||||
finish:
|
||||
for (k = 0; k < n_meta_initialized; k++)
|
||||
safe_close_pair(fds + 2*k);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char *const partition_designator_table[] = {
|
||||
[PARTITION_ROOT] = "root",
|
||||
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
||||
|
||||
@@ -77,7 +77,13 @@ struct DissectedImage {
|
||||
bool encrypted:1;
|
||||
bool verity:1; /* verity available and usable */
|
||||
bool can_verity:1; /* verity available, but not necessarily used */
|
||||
|
||||
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
|
||||
|
||||
char *hostname;
|
||||
sd_id128_t machine_id;
|
||||
char **machine_info;
|
||||
char **os_release;
|
||||
};
|
||||
|
||||
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
|
||||
@@ -89,6 +95,8 @@ int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const voi
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
|
||||
|
||||
int dissected_image_acquire_metadata(DissectedImage *m);
|
||||
|
||||
DecryptedImage* decrypted_image_unref(DecryptedImage *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
|
||||
int decrypted_image_relinquish(DecryptedImage *d);
|
||||
|
||||
@@ -34,12 +34,17 @@
|
||||
#include "chattr-util.h"
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "lockfile-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "machine-image.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
@@ -65,6 +70,11 @@ Image *image_unref(Image *i) {
|
||||
|
||||
free(i->name);
|
||||
free(i->path);
|
||||
|
||||
free(i->hostname);
|
||||
strv_free(i->machine_info);
|
||||
strv_free(i->os_release);
|
||||
|
||||
return mfree(i);
|
||||
}
|
||||
|
||||
@@ -761,6 +771,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
|
||||
int image_read_only(Image *i, bool b) {
|
||||
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||
int r;
|
||||
|
||||
assert(i);
|
||||
|
||||
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
|
||||
@@ -924,6 +935,118 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
|
||||
return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
|
||||
}
|
||||
|
||||
int image_read_metadata(Image *i) {
|
||||
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||
int r;
|
||||
|
||||
assert(i);
|
||||
|
||||
r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (i->type) {
|
||||
|
||||
case IMAGE_SUBVOLUME:
|
||||
case IMAGE_DIRECTORY: {
|
||||
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
|
||||
sd_id128_t machine_id = SD_ID128_NULL;
|
||||
_cleanup_free_ char *hostname = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
|
||||
else if (r >= 0) {
|
||||
r = read_etc_hostname(path, &hostname);
|
||||
if (r < 0)
|
||||
log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name);
|
||||
}
|
||||
|
||||
path = mfree(path);
|
||||
|
||||
r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name);
|
||||
else if (r >= 0) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
log_debug_errno(errno, "Failed to open %s: %m", path);
|
||||
else {
|
||||
r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
|
||||
}
|
||||
}
|
||||
|
||||
path = mfree(path);
|
||||
|
||||
r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
|
||||
else if (r >= 0) {
|
||||
r = load_env_file_pairs(NULL, path, NULL, &machine_info);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
|
||||
}
|
||||
|
||||
path = mfree(path);
|
||||
|
||||
r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r == -ENOENT)
|
||||
r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT, &path);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to chase os-release in image: %m");
|
||||
else if (r >= 0) {
|
||||
r = load_env_file_pairs(NULL, path, NULL, &os_release);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name);
|
||||
}
|
||||
|
||||
free_and_replace(i->hostname, hostname);
|
||||
i->machine_id = machine_id;
|
||||
strv_free_and_replace(i->machine_info, machine_info);
|
||||
strv_free_and_replace(i->os_release, os_release);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_RAW:
|
||||
case IMAGE_BLOCK: {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
|
||||
r = loop_device_make_by_path(i->path, O_RDONLY, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissected_image_acquire_metadata(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free_and_replace(i->hostname, m->hostname);
|
||||
i->machine_id = m->machine_id;
|
||||
strv_free_and_replace(i->machine_info, m->machine_info);
|
||||
strv_free_and_replace(i->os_release, m->os_release);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
i->metadata_valid = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int image_name_lock(const char *name, int operation, LockFile *ret) {
|
||||
const char *p;
|
||||
|
||||
|
||||
@@ -53,6 +53,13 @@ typedef struct Image {
|
||||
uint64_t limit;
|
||||
uint64_t limit_exclusive;
|
||||
|
||||
char *hostname;
|
||||
sd_id128_t machine_id;
|
||||
char **machine_info;
|
||||
char **os_release;
|
||||
|
||||
bool metadata_valid;
|
||||
|
||||
void *userdata;
|
||||
} Image;
|
||||
|
||||
@@ -80,6 +87,8 @@ int image_name_lock(const char *name, int operation, LockFile *ret);
|
||||
|
||||
int image_set_limit(Image *i, uint64_t referenced_max);
|
||||
|
||||
int image_read_metadata(Image *i);
|
||||
|
||||
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
|
||||
assert(i);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user