along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#if HAVE_LIBCRYPTSETUP
-#include <libcryptsetup.h>
-#ifndef CRYPT_LUKS
-#define CRYPT_LUKS NULL
-#endif
-#endif
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
+#include "sd-id128.h"
+
#include "architecture.h"
#include "ask-password-api.h"
#include "blkid-util.h"
+#include "blockdev-util.h"
#include "copy.h"
+#include "crypt-util.h"
#include "def.h"
+#include "device-nodes.h"
#include "dissect-image.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "linux-3.13/dm-ioctl.h"
+#include "missing.h"
#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "udev-util.h"
+#include "user-util.h"
#include "xattr-util.h"
-_unused_ static int probe_filesystem(const char *node, char **ret_fstype) {
+int probe_filesystem(const char *node, char **ret_fstype) {
+ /* Try to find device content type and return it in *ret_fstype. If nothing is found,
+ * 0/NULL will be returned. -EUCLEAN will be returned for ambigous results, and an
+ * different error otherwise. */
+
#if HAVE_BLKID
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype;
int r;
+ errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b)
- return -ENOMEM;
+ return -errno ?: -ENOMEM;
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) {
- log_debug("Failed to identify any partition type on partition %s", node);
+ if (r == 1) {
+ log_debug("No type detected on partition %s", node);
goto not_found;
}
+ if (r == -2) {
+ log_debug("Results ambiguous for partition %s", node);
+ return -EUCLEAN;
+ }
if (r != 0)
return -errno ?: -EIO;
#endif
}
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
+/* Detect RPMB and Boot partitions, which are not listed by blkid.
+ * See https://github.com/systemd/systemd/issues/5806. */
+static bool device_is_mmc_special_partition(struct udev_device *d) {
+ const char *sysname = udev_device_get_sysname(d);
+ return (sysname && startswith(sysname, "mmcblk") &&
+ (endswith(sysname, "rpmb") || endswith(sysname, "boot0") || endswith(sysname, "boot1")));
+}
+
+int dissect_image(
+ int fd,
+ const void *root_hash,
+ size_t root_hash_size,
+ DissectImageFlags flags,
+ DissectedImage **ret) {
#if HAVE_BLKID
sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
.rw = true,
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
- .fstype = t,
- .node = n,
+ .fstype = TAKE_PTR(t),
+ .node = TAKE_PTR(n),
};
- t = n = NULL;
-
m->encrypted = streq(fstype, "crypto_LUKS");
- *ret = m;
- m = NULL;
+ *ret = TAKE_PTR(m);
return 0;
}
/* Count the partitions enumerated by the kernel */
n = 0;
first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first)
+ udev_list_entry_foreach(item, first) {
+ _cleanup_udev_device_unref_ struct udev_device *q;
+
+ q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!q)
+ return -errno;
+
+ if (device_is_mmc_special_partition(q))
+ continue;
n++;
+ }
/* Count the partitions enumerated by blkid */
z = blkid_partlist_numof_partitions(pl);
_cleanup_udev_device_unref_ struct udev_device *q;
unsigned long long pflags;
blkid_partition pp;
- const char *node, *sysname;
+ const char *node;
dev_t qn;
int nr;
if (st.st_rdev == qn)
continue;
- /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see
- * https://github.com/systemd/systemd/issues/5806 */
- sysname = udev_device_get_sysname(q);
- if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb"))
+ if (device_is_mmc_special_partition(q))
continue;
node = udev_device_get_devnode(q);
.partno = nr,
.rw = rw,
.architecture = architecture,
- .node = n,
- .fstype = t,
+ .node = TAKE_PTR(n),
+ .fstype = TAKE_PTR(t),
.uuid = id,
};
-
- n = t = NULL;
}
} else if (is_mbr) {
.rw = generic_rw,
.partno = generic_nr,
.architecture = _ARCHITECTURE_INVALID,
- .node = generic_node,
+ .node = TAKE_PTR(generic_node),
.uuid = generic_uuid,
};
-
- generic_node = NULL;
}
}
if (!p->fstype && p->node) {
r = probe_filesystem(p->node, &p->fstype);
- if (r < 0)
+ if (r < 0 && r != -EUCLEAN)
return r;
}
p->rw = false;
}
- *ret = m;
- m = NULL;
+ *ret = TAKE_PTR(m);
return 0;
#else
strv_free(m->machine_info);
strv_free(m->os_release);
- free(m);
- return NULL;
+ return mfree(m);
}
static int is_loop_device(const char *path) {
- char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
+ char s[SYS_BLOCK_PATH_MAX("/../loop/")];
struct stat st;
assert(path);
if (!S_ISBLK(st.st_mode))
return -ENOTBLK;
- xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
+ xsprintf_sys_block_path(s, "/loop/", st.st_dev);
if (access(s, F_OK) < 0) {
if (errno != ENOENT)
return -errno;
/* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
- xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
+ xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
if (access(s, F_OK) < 0)
return errno == ENOENT ? false : -errno;
}
DissectedPartition *m,
const char *where,
const char *directory,
+ uid_t uid_shift,
DissectImageFlags flags) {
- const char *p, *options = NULL, *node, *fstype;
- _cleanup_free_ char *chased = NULL;
+ _cleanup_free_ char *chased = NULL, *options = NULL;
+ const char *p, *node, *fstype;
bool rw;
int r;
/* If requested, turn on discard support. */
if (fstype_can_discard(fstype) &&
((flags & DISSECT_IMAGE_DISCARD) ||
- ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
- options = "discard";
+ ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) {
+ options = strdup("discard");
+ if (!options)
+ return -ENOMEM;
+ }
+
+ if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
+ _cleanup_free_ char *uid_option = NULL;
+
+ if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
+ return -ENOMEM;
+
+ if (!strextend_with_separator(&options, ",", uid_option, NULL))
+ return -ENOMEM;
+ }
return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
}
-int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
+int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
int r;
assert(m);
if (!m->partitions[PARTITION_ROOT].found)
return -ENXIO;
- r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
- if (r < 0)
- return r;
+ if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
+ r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
+ if (r < 0)
+ return r;
+ }
+
+ if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY))
+ return 0;
- r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
+ r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
if (r < 0)
return r;
- r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
+ r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
if (r < 0)
return r;
r = dir_is_empty(p);
if (r > 0) {
- r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+ r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags);
if (r < 0)
return r;
}
if (!node)
return -ENOMEM;
- *ret_name = name;
- *ret_node = node;
+ *ret_name = TAKE_PTR(name);
+ *ret_node = TAKE_PTR(node);
- name = node = NULL;
return 0;
}
DecryptedImage *d) {
_cleanup_free_ char *node = NULL, *name = NULL;
- struct crypt_device *cd;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
assert(m);
if (!streq(m->fstype, "crypto_LUKS"))
return 0;
+ if (!passphrase)
+ return -ENOKEY;
+
r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
r = crypt_load(cd, CRYPT_LUKS, NULL);
- if (r < 0) {
- log_debug_errno(r, "Failed to load LUKS metadata: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to load LUKS metadata: %m");
r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
- if (r < 0)
+ if (r < 0) {
log_debug_errno(r, "Failed to activate LUKS device: %m");
- if (r == -EPERM) {
- r = -EKEYREJECTED;
- goto fail;
+ return r == -EPERM ? -EKEYREJECTED : r;
}
- if (r < 0)
- goto fail;
-
- d->decrypted[d->n_decrypted].name = name;
- name = NULL;
- d->decrypted[d->n_decrypted].device = cd;
+ d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
+ d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
d->n_decrypted++;
- m->decrypted_node = node;
- node = NULL;
+ m->decrypted_node = TAKE_PTR(node);
return 0;
-
-fail:
- crypt_free(cd);
- return r;
}
static int verity_partition(
DecryptedImage *d) {
_cleanup_free_ char *node = NULL, *name = NULL;
- struct crypt_device *cd;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
assert(m);
r = crypt_load(cd, CRYPT_VERITY, NULL);
if (r < 0)
- goto fail;
+ return r;
r = crypt_set_data_device(cd, m->node);
if (r < 0)
- goto fail;
+ return r;
r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
if (r < 0)
- goto fail;
-
- d->decrypted[d->n_decrypted].name = name;
- name = NULL;
+ return r;
- d->decrypted[d->n_decrypted].device = cd;
+ d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
+ d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
d->n_decrypted++;
- m->decrypted_node = node;
- node = NULL;
+ m->decrypted_node = TAKE_PTR(node);
return 0;
-
-fail:
- crypt_free(cd);
- return r;
}
#endif
DissectImageFlags flags,
DecryptedImage **ret) {
- _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
#if HAVE_LIBCRYPTSETUP
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
unsigned i;
int r;
#endif
}
#if HAVE_LIBCRYPTSETUP
- if (m->encrypted && !passphrase)
- return -ENOKEY;
-
d = new0(DecryptedImage, 1);
if (!d)
return -ENOMEM;
if (!p->decrypted_fstype && p->decrypted_node) {
r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
- if (r < 0)
+ if (r < 0 && r != -EUCLEAN)
return r;
}
}
- *ret = d;
- d = NULL;
+ *ret = TAKE_PTR(d);
return 1;
#else
if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
return r;
- fn = newa(char, strlen(image) + strlen(".roothash") + 1);
+ fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
n = stpcpy(fn, image);
e = endswith(fn, ".raw");
if (e)
if (l < sizeof(sd_id128_t))
return -EINVAL;
- *ret = k;
+ *ret = TAKE_PTR(k);
*ret_size = l;
- k = NULL;
-
return 1;
}
_cleanup_free_ char *hostname = NULL;
unsigned n_meta_initialized = 0, k;
int fds[2 * _META_MAX], r;
- siginfo_t si;
BLOCK_SIGNALS(SIGCHLD);
if (r < 0)
goto finish;
- child = raw_clone(SIGCHLD|CLONE_NEWNS);
- if (child < 0) {
- r = -errno;
+ r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child);
+ if (r < 0)
goto finish;
- }
-
- if (child == 0) {
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
+ if (r == 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);
+ r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
if (r < 0)
_exit(EXIT_FAILURE);
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);
+ fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
if (fd >= 0)
break;
}
- if (fd < 0)
+ if (fd < 0) {
+ log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
continue;
+ }
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
if (r < 0)
}
}
- r = wait_for_terminate(child, &si);
- if (r < 0)
- goto finish;
+ r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
child = 0;
-
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
- r = -EPROTO;
+ if (r < 0)
goto finish;
- }
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
free_and_replace(m->hostname, hostname);
m->machine_id = machine_id;
return r;
}
+int dissect_image_and_warn(
+ int fd,
+ const char *name,
+ const void *root_hash,
+ size_t root_hash_size,
+ DissectImageFlags flags,
+ DissectedImage **ret) {
+
+ _cleanup_free_ char *buffer = NULL;
+ int r;
+
+ if (!name) {
+ r = fd_get_path(fd, &buffer);
+ if (r < 0)
+ return r;
+
+ name = buffer;
+ }
+
+ r = dissect_image(fd, root_hash, root_hash_size, flags, ret);
+
+ switch (r) {
+
+ case -EOPNOTSUPP:
+ return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
+
+ case -ENOPKG:
+ return log_error_errno(r, "Couldn't identify a suitable partition table or file system in '%s'.", name);
+
+ case -EADDRNOTAVAIL:
+ return log_error_errno(r, "No root partition for specified root hash found in '%s'.", name);
+
+ case -ENOTUNIQ:
+ return log_error_errno(r, "Multiple suitable root partitions found in image '%s'.", name);
+
+ case -ENXIO:
+ return log_error_errno(r, "No suitable root partition found in image '%s'.", name);
+
+ case -EPROTONOSUPPORT:
+ return log_error_errno(r, "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", name);
+
+ default:
+ if (r < 0)
+ return log_error_errno(r, "Failed to dissect image '%s': %m", name);
+
+ return r;
+ }
+}
+
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",