summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2017-07-05 10:27:58 -0400
committerGitHub <noreply@github.com>2017-07-05 10:27:58 -0400
commit0d5e394c01c6b9cf898880d64e824047dddcd09e (patch)
tree396f3372ef60c430ea52e5f5198c22851c0d32a1
parenttest-fs-util: re-order test_readlink_and_make_absolute and test_get_files_in_... (diff)
parentsystemd-mount: support relative paths (diff)
downloadsystemd-0d5e394c01c6b9cf898880d64e824047dddcd09e.tar.gz
systemd-0d5e394c01c6b9cf898880d64e824047dddcd09e.tar.bz2
systemd-0d5e394c01c6b9cf898880d64e824047dddcd09e.zip
Merge pull request #6236 from yuwata/mount-loop
systemd-mount: support discovery of loop backing file
-rw-r--r--man/systemd-mount.xml7
-rw-r--r--src/mount/mount-tool.c460
2 files changed, 330 insertions, 137 deletions
diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml
index f3910ca3f..63e4fc40f 100644
--- a/man/systemd-mount.xml
+++ b/man/systemd-mount.xml
@@ -83,7 +83,8 @@
checker to execute a priori), and may make use of the auto-mounting logic.</para>
<para>The command takes either one or two arguments. If only one argument is specified it should refer to a block
- device containing a file system (e.g. <literal>/dev/sdb1</literal>), which is then probed for a label and other
+ device or regular file containing a file system (e.g. <literal>/dev/sdb1</literal> or
+ <literal>/path/to/disk.img</literal>). If it is a block device, which is then probed for a label and other
metadata, and is mounted to a directory whose name is generated from the label. In this mode the block device must
exist at the time of invocation of the command, so that it may be probed. If the device is found to be a removable
block device (e.g. a USB stick) an automount point instead of a regular mount point is created (i.e. the
@@ -259,8 +260,8 @@
which can be mount points, devices, <filename>/etc/fstab</filename> style node names, or backing files
corresponding to loop devices, like
<command>systemd-mount --umount /path/to/umount /dev/sda1 UUID=xxxxxx-xxxx LABEL=xxxxx /path/to/disk.img</command>.
- </para>
- </listitem>
+ Note that when <option>-H</option> or <option>-M</option> is specified, only absolute paths to mount points are
+ supported.</para></listitem>
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="user" />
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index 65d57d86b..0ab06ac3b 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -311,8 +311,13 @@ static int parse_argv(int argc, char *argv[]) {
}
if (arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Unmounting devices only supported locally.");
- return -EOPNOTSUPP;
+ int i;
+
+ for (i = optind; i < argc; i++)
+ if (!path_is_absolute(argv[i]) ) {
+ log_error("Only absolute path is supported: %s", argv[i]);
+ return -EINVAL;
+ }
}
} else {
if (optind >= argc) {
@@ -325,14 +330,58 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- arg_mount_what = fstab_node_to_udev_node(argv[optind]);
- if (!arg_mount_what)
- return log_oom();
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ _cleanup_free_ char *u = NULL, *p = NULL;
- if (argc > optind+1) {
- r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where);
+ u = fstab_node_to_udev_node(argv[optind]);
+ if (!u)
+ return log_oom();
+
+ r = path_make_absolute_cwd(u, &p);
if (r < 0)
return log_error_errno(r, "Failed to make path absolute: %m");
+
+ arg_mount_what = canonicalize_file_name(p);
+ if (!arg_mount_what)
+ return log_error_errno(errno, "Failed to canonicalize path: %m");
+
+ } else {
+ arg_mount_what = strdup(argv[optind+1]);
+ if (!arg_mount_what)
+ return log_oom();
+
+ path_kill_slashes(arg_mount_what);
+
+ if (!path_is_absolute(arg_mount_what)) {
+ log_error("Only absolute path is supported: %s", arg_mount_what);
+ return -EINVAL;
+ }
+ }
+
+ if (argc > optind+1) {
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ _cleanup_free_ char *p = NULL;
+
+ r = path_make_absolute_cwd(argv[optind+1], &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make path absolute: %m");
+
+ arg_mount_where = canonicalize_file_name(p);
+ if (!arg_mount_where)
+ return log_error_errno(errno, "Failed to canonicalize path: %m");
+
+ } else {
+ arg_mount_where = strdup(argv[optind+1]);
+ if (!arg_mount_where)
+ return log_oom();
+
+ path_kill_slashes(arg_mount_where);
+
+ if (!path_is_absolute(arg_mount_where)) {
+ log_error("Only absolute path is supported: %s", arg_mount_where);
+ return -EINVAL;
+ }
+ }
} else
arg_discover = true;
@@ -636,6 +685,125 @@ static int start_transient_automount(
return 0;
}
+static int find_mount_points(const char *what, char ***list) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ size_t bufsize = 0, n = 0;
+
+ assert(what);
+ assert(list);
+
+ /* Returns all mount points obtained from /proc/self/mountinfo in *list,
+ * and the number of mount points as return value. */
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return log_error_errno(errno, "Can't open /proc/self/mountinfo: %m");
+
+ for (;;) {
+ _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL;
+ int r;
+
+ r = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%ms" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path, &dev);
+ if (r != 2) {
+ if (r == EOF)
+ break;
+
+ continue;
+ }
+
+ if (!streq(what, dev))
+ continue;
+
+ r = cunescape(path, UNESCAPE_RELAX, &where);
+ if (r < 0)
+ continue;
+
+ /* one extra slot is needed for the terminating NULL */
+ if (!GREEDY_REALLOC(l, bufsize, n + 2))
+ return log_oom();
+
+ l[n] = strdup(where);
+ if (!l[n])
+ return log_oom();
+
+ n++;
+ }
+
+ l[n] = NULL;
+ *list = l;
+ l = NULL; /* avoid freeing */
+
+ return n;
+}
+
+static int find_loop_device(const char *backing_file, char **loop_dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ _cleanup_free_ char *l = NULL;
+
+ assert(backing_file);
+ assert(loop_dev);
+
+ d = opendir("/sys/devices/virtual/block");
+ if (!d) {
+ if (errno == ENOENT)
+ return -ENOENT;
+ return log_error_errno(errno, "Can't open directory /sys/devices/virtual/block: %m");
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ _cleanup_free_ char *sys = NULL, *fname = NULL;
+ int r;
+
+ dirent_ensure_type(d, de);
+
+ if (de->d_type != DT_DIR)
+ continue;
+
+ if (!startswith(de->d_name, "loop"))
+ continue;
+
+ sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file");
+ if (!sys)
+ return log_oom();
+
+ r = read_one_line_file(sys, &fname);
+ if (r < 0)
+ continue;
+
+ if (files_same(fname, backing_file, 0) <= 0)
+ continue;
+
+ l = strjoin("/dev/", de->d_name);
+ if (!l)
+ return log_oom();
+
+ break;
+ }
+
+ if (!l)
+ return -ENOENT;
+
+ *loop_dev = l;
+ l = NULL; /* avoid freeing */
+
+ return 0;
+}
+
static int stop_mount(
sd_bus *bus,
const char *where,
@@ -729,64 +897,13 @@ static int stop_mounts(
return 0;
}
-static int umount_by_mountinfo(sd_bus *bus, const char *what) {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
- int r, r2 = 0;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- for (;;) {
- _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL;
- int k;
-
- k = fscanf(proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%*s" /* (6) mount options */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%*s " /* (9) file system type */
- "%ms" /* (10) mount source */
- "%*s" /* (11) mount options 2 */
- "%*[^\n]", /* some rubbish at the end */
- &path, &dev);
- if (k != 2) {
- if (k == EOF)
- break;
-
- continue;
- }
-
- if (!streq(what, dev))
- continue;
-
- r = cunescape(path, UNESCAPE_RELAX, &where);
- if (r < 0) {
- r2 = r;
- continue;
- }
-
- r = stop_mounts(bus, where);
- if (r < 0) {
- r2 = r;
- continue;
- }
- }
-
- return r2;
-}
-
static int umount_by_device(sd_bus *bus, const char *what) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_free_ char *where = NULL;
+ _cleanup_strv_free_ char **list = NULL;
struct stat st;
const char *v;
+ char **l;
int r, r2 = 0;
assert(what);
@@ -814,78 +931,33 @@ static int umount_by_device(sd_bus *bus, const char *what) {
}
v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
- if (!isempty(v)) {
- where = strdup(v);
- if (!where)
- return log_oom();
- r2 = stop_mounts(bus, where);
- }
+ if (!isempty(v))
+ r2 = stop_mounts(bus, v);
+
+ r = find_mount_points(what, &list);
+ if (r < 0)
+ return r;
- v = udev_device_get_devnode(d);
- if (!isempty(v)) {
- r = umount_by_mountinfo(bus, v);
+ for (l = list; *l; l++) {
+ r = stop_mounts(bus, *l);
if (r < 0)
- return r;
+ r2 = r;
}
return r2;
}
static int umount_loop(sd_bus *bus, const char *backing_file) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r, r2 = 0;
- bool found = false;
+ _cleanup_free_ char *loop_dev = NULL;
+ int r;
assert(backing_file);
- d = opendir("/sys/devices/virtual/block");
- if (!d) {
- if (errno == ENOENT)
- return log_error_errno(errno, "File %s is not mounted.", backing_file);
- return log_error_errno(errno, "Can't open directory /sys/devices/virtual/block: %m");
- }
-
- FOREACH_DIRENT(de, d, return -errno) {
- _cleanup_free_ char *sys = NULL, *what = NULL, *fname = NULL;
-
- dirent_ensure_type(d, de);
-
- if (de->d_type != DT_DIR)
- continue;
-
- if (!startswith(de->d_name, "loop"))
- continue;
-
- sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file");
- if (!sys)
- return log_oom();
-
- r = read_one_line_file(sys, &fname);
- if (r < 0) {
- if (r != -ENOENT)
- r2 = log_error_errno(r, "Can't read %s: %m", sys);
- continue;
- }
-
- if (!files_same(fname, backing_file, 0))
- continue;
-
- found = true;
-
- what = strjoin("/dev/", de->d_name);
- if (!what)
- return log_oom();
-
- r = umount_by_device(bus, what);
- if (r < 0)
- r2 = r;
- }
-
- if (!found)
- return log_error_errno(ENOENT, "File %s is not mounted.", backing_file);
+ r = find_loop_device(backing_file, &loop_dev);
+ if (r < 0)
+ return log_error_errno(r, r == -ENOENT ? "File %s is not mounted." : "Can't get loop device for %s: %m", backing_file);
- return r2;
+ return umount_by_device(bus, loop_dev);
}
static int action_umount(
@@ -895,21 +967,38 @@ static int action_umount(
int i, r, r2 = 0;
+ if (arg_transport != BUS_TRANSPORT_LOCAL) {
+ for (i = optind; i < argc; i++) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strdup(argv[i]);
+ if (!p)
+ return log_oom();
+
+ path_kill_slashes(p);
+
+ r = stop_mounts(bus, p);
+ if (r < 0)
+ r2 = r;
+ }
+ return r2;
+ }
+
for (i = optind; i < argc; i++) {
- _cleanup_free_ char *u = NULL, *p = NULL;
+ _cleanup_free_ char *u = NULL, *a = NULL, *p = NULL;
struct stat st;
u = fstab_node_to_udev_node(argv[i]);
if (!u)
return log_oom();
- r = path_make_absolute_cwd(u, &p);
+ r = path_make_absolute_cwd(u, &a);
if (r < 0) {
r2 = log_error_errno(r, "Failed to make path absolute: %m");
continue;
}
- path_kill_slashes(p);
+ p = canonicalize_file_name(a);
if (stat(p, &st) < 0)
return log_error_errno(errno, "Can't stat %s: %m", p);
@@ -1019,6 +1108,8 @@ static int acquire_mount_where(struct udev_device *d) {
}
escaped = xescape(name, "\\");
+ if (!escaped)
+ return log_oom();
if (!filename_is_valid(escaped))
return 0;
@@ -1033,6 +1124,32 @@ static int acquire_mount_where(struct udev_device *d) {
return 1;
}
+static int acquire_mount_where_for_loop_dev(const char *loop_dev) {
+ _cleanup_strv_free_ char **list = NULL;
+ int r;
+
+ if (arg_mount_where)
+ return 0;
+
+ r = find_mount_points(loop_dev, &list);
+ if (r < 0)
+ return r;
+ else if (r == 0) {
+ log_error("Can't find mount point of %s. It is expected that %s is already mounted on a place.", loop_dev, loop_dev);
+ return -EINVAL;
+ } else if (r >= 2) {
+ log_error("%s is mounted on %d places. It is expected that %s is mounted on a place.", loop_dev, r, loop_dev);
+ return -EINVAL;
+ }
+
+ arg_mount_where = strdup(list[0]);
+ if (!arg_mount_where)
+ return log_oom();
+
+ log_debug("Discovered where=%s", arg_mount_where);
+ return 1;
+}
+
static int acquire_description(struct udev_device *d) {
const char *model, *label;
@@ -1104,27 +1221,95 @@ static int acquire_removable(struct udev_device *d) {
return 1;
}
-static int discover_device(void) {
+static int discover_loop_backing_file(void) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_free_ char *loop_dev = NULL;
struct stat st;
const char *v;
int r;
- if (!arg_discover)
+ r = find_loop_device(arg_mount_what, &loop_dev);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(errno, "Can't get loop device for %s: %m", arg_mount_what);
+
+ if (r == -ENOENT) {
+ _cleanup_free_ char *escaped = NULL;
+
+ if (arg_mount_where)
+ return 0;
+
+ escaped = xescape(basename(arg_mount_what), "\\");
+ if (!escaped)
+ return log_oom();
+ if (!filename_is_valid(escaped))
+ return -EINVAL;
+
+ arg_mount_where = strjoin("/run/media/system/", escaped);
+ if (!arg_mount_where)
+ return log_oom();
+
+ log_debug("Discovered where=%s", arg_mount_where);
return 0;
+ }
+
+ if (stat(loop_dev, &st) < 0)
+ return log_error_errno(errno, "Can't stat %s: %m", loop_dev);
+
+ if (!S_ISBLK(st.st_mode)) {
+ log_error("Invalid file type: %s", loop_dev);
+ return -EINVAL;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!d)
+ return log_oom();
- if (!is_device_path(arg_mount_what)) {
- log_error("Discovery only supported for block devices, don't know what to do.");
+ v = udev_device_get_property_value(d, "ID_FS_USAGE");
+ if (!streq_ptr(v, "filesystem")) {
+ log_error("%s does not contain a known file system.", arg_mount_what);
return -EINVAL;
}
+ r = acquire_mount_type(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_mount_options(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_mount_where_for_loop_dev(loop_dev);
+ if (r < 0)
+ return r;
+
+ r = acquire_description(d);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int discover_device(void) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ struct stat st;
+ const char *v;
+ int r;
+
if (stat(arg_mount_what, &st) < 0)
return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
+ if (S_ISREG(st.st_mode))
+ return discover_loop_backing_file();
+
if (!S_ISBLK(st.st_mode)) {
- log_error("Path %s is not a block device, don't know what to do.", arg_mount_what);
- return -ENOTBLK;
+ log_error("Invalid file type: %s", arg_mount_what);
+ return -EINVAL;
}
udev = udev_new();
@@ -1371,17 +1556,24 @@ int main(int argc, char* argv[]) {
goto finish;
}
- r = discover_device();
- if (r < 0)
+ if (!path_is_safe(arg_mount_what)) {
+ log_error("Path contains unsafe components: %s", arg_mount_what);
+ r = -EINVAL;
goto finish;
+ }
+
+ if (arg_discover) {
+ r = discover_device();
+ if (r < 0)
+ goto finish;
+ }
+
if (!arg_mount_where) {
log_error("Can't figure out where to mount %s.", arg_mount_what);
r = -EINVAL;
goto finish;
}
- path_kill_slashes(arg_mount_where);
-
if (path_equal(arg_mount_where, "/")) {
log_error("Refusing to operate on root directory.");
r = -EINVAL;
@@ -1389,7 +1581,7 @@ int main(int argc, char* argv[]) {
}
if (!path_is_safe(arg_mount_where)) {
- log_error("Path contains unsafe components.");
+ log_error("Path contains unsafe components: %s", arg_mount_where);
r = -EINVAL;
goto finish;
}