mirror of
https://github.com/systemd/systemd
synced 2024-09-06 08:46:19 +00:00
Merge pull request #26693 from poettering/udev-loop-links
udev: add /dev/loop/by-inode/… + /dev/loop/by-ref/… loopback block device symlinks
This commit is contained in:
commit
862481ece0
9
TODO
9
TODO
|
@ -132,6 +132,12 @@ Features:
|
|||
* set MS_NOSYMFOLLOW for ESP and XBOOTLDR mounts both in gpt-generator and in
|
||||
dissect.c
|
||||
|
||||
* rework loopback support in fstab: when "loop" option is used, then
|
||||
instantiate a new systemd-loop@.service for the source path, set the
|
||||
lo_file_name field for it to something recognizable derived from the fstab
|
||||
line, and then generate a mount unit for it using a udev generated symlink
|
||||
based on lo_file_name.
|
||||
|
||||
* remove tomoyo support, it's obsolete and unmaintained apparently
|
||||
|
||||
* journald: add varlink service that allows subscribing to certain log events,
|
||||
|
@ -277,9 +283,6 @@ Features:
|
|||
* systemd-sysext: for sysext DDIs picked up via EFI stub, set much stricter
|
||||
image policy by default
|
||||
|
||||
* systemd-dissect: maybe add "--attach" and "--detach" verbs which
|
||||
synchronously attach a DDI to a loopback device but not actually mount them.
|
||||
|
||||
* pam_systemd_home: add module parameter to control whether to only accept
|
||||
only password or only pcks11/fido2 auth, and then use this to hook nicely
|
||||
into two of the three PAM stacks gdm provides.
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--umount</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--attach</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--detach</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--list</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg></command>
|
||||
</cmdsynopsis>
|
||||
|
@ -173,6 +179,25 @@
|
|||
<listitem><para>This is a shortcut for <option>--umount --rmdir</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--attach</option></term>
|
||||
|
||||
<listitem><para>Attach the specified disk image to an automatically allocated loopback block device,
|
||||
and print the path to the loopback block device to standard output. This is similar to an invocation
|
||||
of <command>losetup --find --show</command>, but will validate the image as DDI before attaching, and
|
||||
derive the correct sector size to use automatically. Moreover, it ensures the per-partition block
|
||||
devices are created before returning. Takes a path to a disk image file.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--detach</option></term>
|
||||
|
||||
<listitem><para>Detach the specified disk image from a loopback block device. This undoes the effect
|
||||
of <option>--attach</option> above. This expects either a path to a loopback block device as an
|
||||
argument, or the path to the backing image file. In the latter case it will automatically determine
|
||||
the right device to detach.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--list</option></term>
|
||||
<term><option>-l</option></term>
|
||||
|
@ -361,6 +386,25 @@
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--loop-ref=</option></term>
|
||||
|
||||
<listitem><para>Configures the "reference" string the kernel shall report as backing file for the
|
||||
loopback block device. While this is supposed to be a path or filename referencing the backing file,
|
||||
this is not enforced and the kernel accepts arbitrary free-form strings, chosen by the user. Accepts
|
||||
arbitrary strings up to a length of 63 characters. This sets the kernel's
|
||||
<literal>.lo_file_name</literal> field for the block device. Note this is distinct from the
|
||||
<filename>/sys/class/block/loopX/loop/backing_file</filename> attribute file that always reports a
|
||||
path referring to the actual backing file. The latter is subject to mount namespace translation, the
|
||||
former is not.</para>
|
||||
|
||||
<para>This setting is particularly useful in combination with the <option>--attach</option> command,
|
||||
as it allows later referencing the allocated loop device via <filename>/dev/loop/by-ref/…</filename>
|
||||
symlinks. Example: first, set up the loopback device via <command>systemd-dissect attach
|
||||
--loop-ref=quux foo.raw</command>, and then reference it in a command via the specified filename:
|
||||
<command>cfdisk /dev/loop/by-ref/quux</command>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
|
|
|
@ -141,4 +141,15 @@ ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/
|
|||
ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"
|
||||
ENV{DISKSEQ}=="?*", ENV{DEVTYPE}=="partition", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}-part%n"
|
||||
|
||||
# Create symlinks that allow referencing loopback devices by their backing file's inode number
|
||||
ENV{DEVTYPE}!="partition", ENV{ID_LOOP_BACKING_DEVICE}!="", ENV{ID_LOOP_BACKING_INODE}!="", SYMLINK+="loop/by-inode/$env{ID_LOOP_BACKING_DEVICE}-$env{ID_LOOP_BACKING_INODE}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_LOOP_BACKING_DEVICE}!="", ENV{ID_LOOP_BACKING_INODE}!="", SYMLINK+="loop/by-inode/$env{ID_LOOP_BACKING_DEVICE}-$env{ID_LOOP_BACKING_INODE}-part%n"
|
||||
|
||||
# Similar, but uses the .lo_file_name field of the loopback device (note that
|
||||
# this is basically just a free-form string passed from userspace to the kernel
|
||||
# when the device is created, it is not necessarily a file system path like the
|
||||
# "loop/backing_file" sysfs attribute, which is always an absolute path)
|
||||
ENV{DEVTYPE}!="partition", ENV{ID_LOOP_BACKING_FILENAME_ENC}!="", SYMLINK+="loop/by-ref/$env{ID_LOOP_BACKING_FILENAME_ENC}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_LOOP_BACKING_FILENAME_ENC}!="", SYMLINK+="loop/by-ref/$env{ID_LOOP_BACKING_FILENAME_ENC}-part%n"
|
||||
|
||||
LABEL="persistent_storage_end"
|
||||
|
|
|
@ -52,6 +52,8 @@ static enum {
|
|||
ACTION_DISSECT,
|
||||
ACTION_MOUNT,
|
||||
ACTION_UMOUNT,
|
||||
ACTION_ATTACH,
|
||||
ACTION_DETACH,
|
||||
ACTION_LIST,
|
||||
ACTION_MTREE,
|
||||
ACTION_WITH,
|
||||
|
@ -79,9 +81,11 @@ static bool arg_legend = true;
|
|||
static bool arg_rmdir = false;
|
||||
static bool arg_in_memory = false;
|
||||
static char **arg_argv = NULL;
|
||||
static char *arg_loop_ref = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_argv, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_loop_ref, freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
|
@ -94,6 +98,8 @@ static int help(void) {
|
|||
printf("%1$s [OPTIONS...] IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mount IMAGE PATH\n"
|
||||
"%1$s [OPTIONS...] --umount PATH\n"
|
||||
"%1$s [OPTIONS...] --attach IMAGE\n"
|
||||
"%1$s [OPTIONS...] --detach PATH\n"
|
||||
"%1$s [OPTIONS...] --list IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mtree IMAGE\n"
|
||||
"%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n"
|
||||
|
@ -119,6 +125,7 @@ static int help(void) {
|
|||
" not embedded in IMAGE\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --loop-ref=NAME Set reference string for loopback device\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
|
@ -126,6 +133,8 @@ static int help(void) {
|
|||
" -M Shortcut for --mount --mkdir\n"
|
||||
" -u --umount Unmount the image from the specified directory\n"
|
||||
" -U Shortcut for --umount --rmdir\n"
|
||||
" --attach Attach the disk image to a loopback block device\n"
|
||||
" --detach Detach a loopback block device gain\n"
|
||||
" -l --list List all the files and directories of the specified\n"
|
||||
" OS image\n"
|
||||
" --mtree Show BSD mtree manifest of OS image\n"
|
||||
|
@ -206,6 +215,9 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_JSON,
|
||||
ARG_MTREE,
|
||||
ARG_DISCOVER,
|
||||
ARG_ATTACH,
|
||||
ARG_DETACH,
|
||||
ARG_LOOP_REF,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -215,6 +227,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "mount", no_argument, NULL, 'm' },
|
||||
{ "umount", no_argument, NULL, 'u' },
|
||||
{ "attach", no_argument, NULL, ARG_ATTACH },
|
||||
{ "detach", no_argument, NULL, ARG_DETACH },
|
||||
{ "with", no_argument, NULL, ARG_WITH },
|
||||
{ "read-only", no_argument, NULL, 'r' },
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
|
@ -232,6 +246,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "copy-to", no_argument, NULL, 'a' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "loop-ref", required_argument, NULL, ARG_LOOP_REF },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -291,6 +306,14 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_rmdir = true;
|
||||
break;
|
||||
|
||||
case ARG_ATTACH:
|
||||
arg_action = ACTION_ATTACH;
|
||||
break;
|
||||
|
||||
case ARG_DETACH:
|
||||
arg_action = ACTION_DETACH;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
arg_action = ACTION_LIST;
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
|
@ -417,6 +440,20 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_action = ACTION_DISCOVER;
|
||||
break;
|
||||
|
||||
case ARG_LOOP_REF:
|
||||
if (isempty(optarg)) {
|
||||
arg_loop_ref = mfree(arg_loop_ref);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen(optarg) >= sizeof_field(struct loop_info64, lo_file_name))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Loop device ref string '%s' is too long.", optarg);
|
||||
|
||||
r = free_and_strdup_warn(&arg_loop_ref, optarg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -454,6 +491,22 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_path = argv[optind];
|
||||
break;
|
||||
|
||||
case ACTION_ATTACH:
|
||||
if (optind + 1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
break;
|
||||
|
||||
case ACTION_DETACH:
|
||||
if (optind + 1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path or loopback device as only argument.");
|
||||
|
||||
arg_image = argv[optind];
|
||||
break;
|
||||
|
||||
case ACTION_LIST:
|
||||
case ACTION_MTREE:
|
||||
if (optind + 1 != argc)
|
||||
|
@ -1486,6 +1539,113 @@ static int action_discover(void) {
|
|||
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
}
|
||||
|
||||
static int action_attach(DissectedImage *m, LoopDevice *d) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
r = loop_device_set_autoclear(d, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable auto-clear logic on loopback device: %m");
|
||||
|
||||
r = dissected_image_relinquish(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
|
||||
|
||||
puts(d->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_detach(const char *path) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
fd = open(path, O_PATH|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat '%s': %m", path);
|
||||
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
r = loop_device_open_from_fd(fd, O_RDONLY, LOCK_EX, &loop);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open '%s' as loopback block device: %m", path);
|
||||
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
sd_device *d;
|
||||
|
||||
/* If a regular file is specified, search for a loopback block device that is backed by it */
|
||||
|
||||
r = sd_device_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate enumerator: %m");
|
||||
|
||||
r = sd_device_enumerator_add_match_subsystem(e, "block", true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to match block devices: %m");
|
||||
|
||||
r = sd_device_enumerator_add_match_sysname(e, "loop*");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to match loopback block devices: %m");
|
||||
|
||||
(void) sd_device_enumerator_allow_uninitialized(e);
|
||||
|
||||
FOREACH_DEVICE(e, d) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *entry_loop = NULL;
|
||||
const char *name, *devtype;
|
||||
|
||||
r = sd_device_get_sysname(d, &name);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to get enumerated device's sysname, skipping: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = sd_device_get_devtype(d, &devtype);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to get devtype of '%s', skipping: %m", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!streq(devtype, "disk")) /* Filter out partition block devices */
|
||||
continue;
|
||||
|
||||
r = loop_device_open(d, O_RDONLY, LOCK_SH, &entry_loop);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to open loopback block device '%s', skipping: %m", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry_loop->backing_devno == st.st_dev && entry_loop->backing_inode == st.st_ino) {
|
||||
/* Found it! The kernel allows attaching a single file to multiple loopback
|
||||
* devices. Let's destruct them in reverse order, i.e. find the last matching
|
||||
* loopback device here, rather than the first. */
|
||||
|
||||
loop_device_unref(loop);
|
||||
loop = TAKE_PTR(entry_loop);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loop)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No loopback block device backed by '%s' found.", path);
|
||||
|
||||
r = loop_device_flock(loop, LOCK_EX);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to upgrade device lock: %m");
|
||||
}
|
||||
|
||||
r = loop_device_set_autoclear(loop, true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to enable autoclear logic on '%s', ignoring: %m", loop->node);
|
||||
|
||||
loop_device_unrelinquish(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
|
@ -1503,6 +1663,8 @@ static int run(int argc, char *argv[]) {
|
|||
|
||||
if (arg_action == ACTION_UMOUNT)
|
||||
return action_umount(arg_path);
|
||||
if (arg_action == ACTION_DETACH)
|
||||
return action_detach(arg_image);
|
||||
if (arg_action == ACTION_DISCOVER)
|
||||
return action_discover();
|
||||
|
||||
|
@ -1528,6 +1690,12 @@ static int run(int argc, char *argv[]) {
|
|||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
|
||||
|
||||
if (arg_loop_ref) {
|
||||
r = loop_device_set_filename(d, arg_loop_ref);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
|
||||
}
|
||||
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&arg_verity_settings,
|
||||
|
@ -1537,6 +1705,9 @@ static int run(int argc, char *argv[]) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_action == ACTION_ATTACH)
|
||||
return action_attach(m, d);
|
||||
|
||||
r = dissected_image_load_verity_sig_partition(
|
||||
m,
|
||||
d->fd,
|
||||
|
@ -1547,29 +1718,23 @@ static int run(int argc, char *argv[]) {
|
|||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT:
|
||||
r = action_dissect(m, d);
|
||||
break;
|
||||
return action_dissect(m, d);
|
||||
|
||||
case ACTION_MOUNT:
|
||||
r = action_mount(m, d);
|
||||
break;
|
||||
return action_mount(m, d);
|
||||
|
||||
case ACTION_LIST:
|
||||
case ACTION_MTREE:
|
||||
case ACTION_COPY_FROM:
|
||||
case ACTION_COPY_TO:
|
||||
r = action_list_or_mtree_or_copy(m, d);
|
||||
break;
|
||||
return action_list_or_mtree_or_copy(m, d);
|
||||
|
||||
case ACTION_WITH:
|
||||
r = action_with(m, d);
|
||||
break;
|
||||
return action_with(m, d);
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
|
|
@ -595,6 +595,8 @@ static int loop_device_make_internal(
|
|||
}
|
||||
|
||||
d->backing_file = TAKE_PTR(backing_file);
|
||||
d->backing_inode = st.st_ino;
|
||||
d->backing_devno = st.st_dev;
|
||||
|
||||
log_debug("Successfully acquired %s, devno=%u:%u, nr=%i, diskseq=%" PRIu64,
|
||||
d->node,
|
||||
|
@ -841,11 +843,12 @@ int loop_device_open(
|
|||
|
||||
_cleanup_close_ int fd = -EBADF, lock_fd = -EBADF;
|
||||
_cleanup_free_ char *node = NULL, *backing_file = NULL;
|
||||
dev_t devnum, backing_devno = 0;
|
||||
struct loop_info64 info;
|
||||
ino_t backing_inode = 0;
|
||||
uint64_t diskseq = 0;
|
||||
LoopDevice *d;
|
||||
const char *s;
|
||||
dev_t devnum;
|
||||
int r, nr = -1;
|
||||
|
||||
assert(dev);
|
||||
|
@ -878,6 +881,9 @@ int loop_device_open(
|
|||
if (!backing_file)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
backing_devno = info.lo_device;
|
||||
backing_inode = info.lo_inode;
|
||||
}
|
||||
|
||||
r = fd_get_diskseq(fd, &diskseq);
|
||||
|
@ -913,6 +919,8 @@ int loop_device_open(
|
|||
.node = TAKE_PTR(node),
|
||||
.dev = sd_device_ref(dev),
|
||||
.backing_file = TAKE_PTR(backing_file),
|
||||
.backing_inode = backing_inode,
|
||||
.backing_devno = backing_devno,
|
||||
.relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */
|
||||
.devno = devnum,
|
||||
.diskseq = diskseq,
|
||||
|
@ -1103,3 +1111,57 @@ int loop_device_sync(LoopDevice *d) {
|
|||
|
||||
return RET_NERRNO(fsync(d->fd));
|
||||
}
|
||||
|
||||
int loop_device_set_autoclear(LoopDevice *d, bool autoclear) {
|
||||
struct loop_info64 info;
|
||||
|
||||
assert(d);
|
||||
|
||||
if (ioctl(d->fd, LOOP_GET_STATUS64, &info) < 0)
|
||||
return -errno;
|
||||
|
||||
if (autoclear == FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR))
|
||||
return 0;
|
||||
|
||||
SET_FLAG(info.lo_flags, LO_FLAGS_AUTOCLEAR, autoclear);
|
||||
|
||||
if (ioctl(d->fd, LOOP_SET_STATUS64, &info) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int loop_device_set_filename(LoopDevice *d, const char *name) {
|
||||
struct loop_info64 info;
|
||||
|
||||
assert(d);
|
||||
|
||||
/* Sets the .lo_file_name of the loopback device. This is supposed to contain the path to the file
|
||||
* backing the block device, but is actually just a free-form string you can pass to the kernel. Most
|
||||
* tools that actually care for the backing file path use the sysfs attribute file loop/backing_file
|
||||
* which is a kernel generated string, subject to file system namespaces and such.
|
||||
*
|
||||
* .lo_file_name is useful since userspace can select it freely when creating a loopback block
|
||||
* device, and we can use it for /dev/loop/by-ref/ symlinks, and similar, so that apps can recognize
|
||||
* their own loopback files. */
|
||||
|
||||
if (name && strlen(name) >= sizeof(info.lo_file_name))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (ioctl(d->fd, LOOP_GET_STATUS64, &info) < 0)
|
||||
return -errno;
|
||||
|
||||
if (strneq((char*) info.lo_file_name, strempty(name), sizeof(info.lo_file_name)))
|
||||
return 0;
|
||||
|
||||
if (name) {
|
||||
strncpy((char*) info.lo_file_name, name, sizeof(info.lo_file_name)-1);
|
||||
info.lo_file_name[sizeof(info.lo_file_name)-1] = 0;
|
||||
} else
|
||||
memzero(info.lo_file_name, sizeof(info.lo_file_name));
|
||||
|
||||
if (ioctl(d->fd, LOOP_SET_STATUS64, &info) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -14,12 +14,14 @@ struct LoopDevice {
|
|||
unsigned n_ref;
|
||||
int fd;
|
||||
int lock_fd;
|
||||
int nr; /* The loopback device index (i.e. 4 for /dev/loop4); if this object encapsulates a non-loopback block device, set to -1 */
|
||||
dev_t devno;
|
||||
int nr; /* The loopback device index (i.e. 4 for /dev/loop4); if this object encapsulates a non-loopback block device, set to -1 */
|
||||
dev_t devno; /* The loopback device's own dev_t */
|
||||
char *node;
|
||||
sd_device *dev;
|
||||
char *backing_file;
|
||||
bool relinquished;
|
||||
dev_t backing_devno; /* The backing file's dev_t */
|
||||
ino_t backing_inode; /* The backing file's ino_t */
|
||||
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 */
|
||||
|
@ -47,3 +49,6 @@ int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size);
|
|||
|
||||
int loop_device_flock(LoopDevice *d, int operation);
|
||||
int loop_device_sync(LoopDevice *d);
|
||||
|
||||
int loop_device_set_autoclear(LoopDevice *d, bool autoclear);
|
||||
int loop_device_set_filename(LoopDevice *d, const char *name);
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
* Copyright © 2011 Karel Zak <kzak@redhat.com>
|
||||
*/
|
||||
|
||||
#if HAVE_VALGRIND_MEMCHECK_H
|
||||
#include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/loop.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
@ -17,6 +23,7 @@
|
|||
#include "alloc-util.h"
|
||||
#include "blkid-util.h"
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
|
@ -234,11 +241,86 @@ static int probe_superblocks(blkid_probe pr) {
|
|||
return blkid_do_safeprobe(pr);
|
||||
}
|
||||
|
||||
static int read_loopback_backing_inode(
|
||||
sd_device *dev,
|
||||
int fd,
|
||||
dev_t *ret_devno,
|
||||
ino_t *ret_inode,
|
||||
char **ret_fname) {
|
||||
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
struct loop_info64 info;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(dev);
|
||||
assert(fd >= 0);
|
||||
assert(ret_devno);
|
||||
assert(ret_inode);
|
||||
assert(ret_fname);
|
||||
|
||||
/* Retrieves various fields of the current loopback device backing file, so that we can ultimately
|
||||
* use it to create stable symlinks to loopback block devices, based on what they are backed by. We
|
||||
* pick up inode/device as well as file name field. Note that we pick up the "lo_file_name" field
|
||||
* here, which is an arbitrary free-form string provided by userspace. We do not return the sysfs
|
||||
* attribute loop/backing_file here, because that is directly accessible from udev rules anyway. And
|
||||
* sometimes, depending on context, it's a good thing to return the string userspace can freely pick
|
||||
* over the string automatically generated by the kernel. */
|
||||
|
||||
r = sd_device_get_sysname(dev, &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!startswith(name, "loop"))
|
||||
goto notloop;
|
||||
|
||||
if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto notloop;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#if HAVE_VALGRIND_MEMCHECK_H
|
||||
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
|
||||
#endif
|
||||
|
||||
if (isempty((char*) info.lo_file_name) ||
|
||||
strnlen((char*) info.lo_file_name, sizeof(info.lo_file_name)-1) == sizeof(info.lo_file_name)-1)
|
||||
/* Don't pick up file name if it is unset or possibly truncated. (Note: we can't really know
|
||||
* the file file name is truncated if it uses sizeof(info.lo_file_name)-1 as length; it could
|
||||
* also just mean the string is just that long and wasn't truncated — but the fact is simply
|
||||
* that we cannot know in that case if it was truncated or not, hence we assume the worst and
|
||||
* suppress — at least for now. For shorter strings we know for sure it wasn't truncated,
|
||||
* hence that's always safe.) */
|
||||
fn = NULL;
|
||||
else {
|
||||
fn = memdup_suffix0(info.lo_file_name, sizeof(info.lo_file_name));
|
||||
if (!fn)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret_inode = info.lo_inode;
|
||||
*ret_devno = info.lo_device;
|
||||
*ret_fname = TAKE_PTR(fn);
|
||||
return 1;
|
||||
|
||||
|
||||
notloop:
|
||||
*ret_devno = 0;
|
||||
*ret_inode = 0;
|
||||
*ret_fname = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) {
|
||||
const char *devnode, *root_partition = NULL, *data, *name;
|
||||
_cleanup_(blkid_free_probep) blkid_probe pr = NULL;
|
||||
_cleanup_free_ char *backing_fname = NULL;
|
||||
bool noraid = false, is_gpt = false;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
ino_t backing_inode = 0;
|
||||
dev_t backing_devno = 0;
|
||||
int64_t offset = 0;
|
||||
int r;
|
||||
|
||||
|
@ -352,6 +434,32 @@ static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv
|
|||
if (is_gpt)
|
||||
find_gpt_root(dev, pr, test);
|
||||
|
||||
r = read_loopback_backing_inode(
|
||||
dev,
|
||||
fd,
|
||||
&backing_devno,
|
||||
&backing_inode,
|
||||
&backing_fname);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(dev, r, "Failed to read loopback backing inode, ignoring: %m");
|
||||
else if (r > 0) {
|
||||
udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno));
|
||||
udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode);
|
||||
|
||||
if (backing_fname) {
|
||||
/* In the worst case blkid_encode_string() will blow up to 4x the string
|
||||
* length. Hence size the buffer to 4x of the longest string
|
||||
* read_loopback_backing_inode() might return */
|
||||
char encoded[sizeof_field(struct loop_info64, lo_file_name) * 4 + 1];
|
||||
|
||||
assert(strlen(backing_fname) < ELEMENTSOF(encoded) / 4);
|
||||
blkid_encode_string(backing_fname, encoded, ELEMENTSOF(encoded));
|
||||
|
||||
udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME", backing_fname);
|
||||
udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME_ENC", encoded);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,3 +134,21 @@ int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) {
|
||||
_cleanup_free_ char *val = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(dev);
|
||||
assert(key);
|
||||
assert(valf);
|
||||
|
||||
va_start(ap, valf);
|
||||
r = vasprintf(&val, valf, ap);
|
||||
va_end(ap);
|
||||
if (r < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
return udev_builtin_add_property(dev, test, key, val);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "sd-device.h"
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef enum UdevBuiltinCommand {
|
||||
#if HAVE_BLKID
|
||||
UDEV_BUILTIN_BLKID,
|
||||
|
@ -78,5 +80,6 @@ int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd,
|
|||
void udev_builtin_list(void);
|
||||
bool udev_builtin_should_reload(void);
|
||||
int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
|
||||
int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) _printf_(4, 5);
|
||||
int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
|
||||
const char *filter, bool test);
|
||||
|
|
|
@ -430,6 +430,28 @@ mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard
|
|||
umount -R "$T"
|
||||
rmdir "$T"
|
||||
|
||||
LOOP="$(systemd-dissect --attach --loop-ref=waldo "${image}.raw")"
|
||||
|
||||
# Wait until the symlinks we want to test are established
|
||||
udevadm trigger -w "$LOOP"
|
||||
|
||||
# Check if the /dev/loop/* symlinks really reference the right device
|
||||
test /dev/loop/by-ref/waldo -ef "$LOOP"
|
||||
|
||||
if [ "$(stat -c '%Hd:%Ld' "${image}.raw")" != '?d:?d' ] ; then
|
||||
# Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
|
||||
# instead. Let's simply skip the test on such old systems.
|
||||
test "$(stat -c '/dev/loop/by-inode/%Hd:%Ld-%i' "${image}.raw")" -ef "$LOOP"
|
||||
fi
|
||||
|
||||
# Detach by loopback device
|
||||
systemd-dissect --detach "$LOOP"
|
||||
|
||||
# Detach by backing inode
|
||||
systemd-dissect --attach --loop-ref=waldo "${image}.raw"
|
||||
systemd-dissect --detach "${image}.raw"
|
||||
(! systemd-dissect --detach "${image}.raw")
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
exit 0
|
||||
|
|
Loading…
Reference in a new issue