mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
Merge pull request #7237 from keszybz/growfs
Create and grow filesystems
This commit is contained in:
commit
1a2d4d7084
@ -569,6 +569,13 @@ manpages = [
|
||||
['systemd-machine-id-commit.service', '8', [], ''],
|
||||
['systemd-machine-id-setup', '1', [], ''],
|
||||
['systemd-machined.service', '8', ['systemd-machined'], 'ENABLE_MACHINED'],
|
||||
['systemd-makefs@.service',
|
||||
'8',
|
||||
['systemd-growfs',
|
||||
'systemd-growfs@.service',
|
||||
'systemd-makefs',
|
||||
'systemd-makeswap@.service'],
|
||||
''],
|
||||
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
|
||||
['systemd-mount', '1', ['systemd-umount'], ''],
|
||||
['systemd-networkd-wait-online.service',
|
||||
|
120
man/systemd-makefs@.service.xml
Normal file
120
man/systemd-makefs@.service.xml
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<refentry id="systemd-makefs@.service">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-makefs@.service</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Zbigniew</firstname>
|
||||
<surname>Jędrzejewski-Szmek</surname>
|
||||
<email>zbyszek@in.waw.pl</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-makefs@.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-makefs@.service</refname>
|
||||
<refname>systemd-makeswap@.service</refname>
|
||||
<refname>systemd-growfs@.service</refname>
|
||||
<refname>systemd-makefs</refname>
|
||||
<refname>systemd-growfs</refname>
|
||||
<refpurpose>Creating and growing file systems on demand</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-makefs@<replaceable>device</replaceable>.service</filename></para>
|
||||
<para><filename>systemd-makeswap@<replaceable>device</replaceable>.service</filename></para>
|
||||
<para><filename>systemd-growfs@<replaceable>mountpoint</replaceable>.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-makefs</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-growfs</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-makefs@.service</filename>,
|
||||
<filename>systemd-makeswap@.service</filename>, and
|
||||
<filename>systemd-growfs@.service</filename> are used to implement the
|
||||
<option>x-systemd.makefs</option> and <option>x-systemd.growfs</option> options
|
||||
in <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
see <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
They are instantiated for each device for which the file system or swap structure
|
||||
needs to be initalized, and for each mount point where the file system needs to
|
||||
be grown.</para>
|
||||
|
||||
<para>These services are started at boot, either right before or right after the
|
||||
mount point or swap device are used.</para>
|
||||
|
||||
<para><filename>systemd-makefs</filename> knows very little about specific file
|
||||
systems and swap devices, and after checking that the block device does not already
|
||||
contain a file system or other content, it will execute binaries specific to
|
||||
each filesystem type (<filename>/sbin/mkfs.*</filename>).</para>
|
||||
|
||||
<para><filename>systemd-growfs</filename> knows very little about specific file
|
||||
systems and swap devices, and will instruct the kernel to grow the mounted
|
||||
filesystem to full size of the underlying block device. Nevertheless, it needs
|
||||
to know the
|
||||
<citerefentry project='man-pages'><refentrytitle>ioctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
number specific to each file system, so only certain types are supported.
|
||||
Currently:
|
||||
<citerefentry project='man-pages'><refentrytitle>ext4</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
btrfs (see
|
||||
<citerefentry project='man-pages'><refentrytitle>btrfs-man5</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
|
||||
<!-- yes, that's what the man page is called. -->
|
||||
and dm-crypt partitions (see
|
||||
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
|
||||
</para>
|
||||
|
||||
<para>If the creation of a file system or swap device fails, the mount point or
|
||||
swap is failed too. If the growing of a file system fails, a warning is emitted.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.cramfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.ext4</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.fat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.hfsplus</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.minix</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.ntfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -318,6 +318,44 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>x-systemd.makefs</option></term>
|
||||
|
||||
<listitem><para>The file system or swap structure will be intialized
|
||||
on the device. If the device is not "empty", i.e. it contains any signature,
|
||||
the operation will be skipped. It is hence expected that this option
|
||||
remains set even after the device has been initalized.</para>
|
||||
|
||||
<para>Note that this option can only be used in
|
||||
<filename>/etc/fstab</filename>, and will be ignored when part of the
|
||||
<varname>Options=</varname> setting in a unit file.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd-makefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para><citerefentry project='man-pages'><refentrytitle>wipefs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
may be used to remove any signatures from a block device to force
|
||||
<option>x-systemd.makefs</option> to reinitialize the device.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>x-systemd.growfs</option></term>
|
||||
|
||||
<listitem><para>The file system will be grown to occupy the full block
|
||||
device. If the file system is already at maximum size, no action will
|
||||
be performed. It is hence expected that this option remains set even after
|
||||
the file system has been grown. Only certain file system types are supported,
|
||||
see
|
||||
<citerefentry><refentrytitle>systemd-makefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
for details.</para>
|
||||
|
||||
<para>Note that this option can only be used in
|
||||
<filename>/etc/fstab</filename>, and will be ignored when part of the
|
||||
<varname>Options=</varname> setting in a unit file.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>_netdev</option></term>
|
||||
|
||||
|
19
meson.build
19
meson.build
@ -182,6 +182,8 @@ conf.set_quoted('CATALOG_DATABASE', join_paths(catalog
|
||||
conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
|
||||
conf.set_quoted('SYSTEMD_BINARY_PATH', join_paths(rootlibexecdir, 'systemd'))
|
||||
conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlibexecdir, 'systemd-fsck'))
|
||||
conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs'))
|
||||
conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs'))
|
||||
conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown'))
|
||||
conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-sleep'))
|
||||
conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl'))
|
||||
@ -1936,6 +1938,23 @@ executable('systemd-fsck',
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
executable('systemd-growfs',
|
||||
'src/partition/growfs.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libcryptsetup],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
executable('systemd-makefs',
|
||||
'src/partition/makefs.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
executable('systemd-sleep',
|
||||
'src/sleep/sleep.c',
|
||||
include_directories : includes,
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "btrfs-util.h"
|
||||
#include "chattr-util.h"
|
||||
#include "copy.h"
|
||||
#include "device-nodes.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "io-util.h"
|
||||
@ -910,7 +911,8 @@ int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, u
|
||||
|
||||
int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
|
||||
struct btrfs_ioctl_vol_args args = {};
|
||||
_cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
|
||||
char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")];
|
||||
_cleanup_free_ char *backing = NULL;
|
||||
_cleanup_close_ int loop_fd = -1, backing_fd = -1;
|
||||
struct stat st;
|
||||
dev_t dev = 0;
|
||||
@ -930,8 +932,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
|
||||
if (r == 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
|
||||
return -ENOMEM;
|
||||
xsprintf_sys_block_path(p, "/loop/backing_file", dev);
|
||||
r = read_one_line_file(p, &backing);
|
||||
if (r == -ENOENT)
|
||||
return -ENODEV;
|
||||
@ -955,9 +956,8 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
|
||||
if (grow_only && new_size < (uint64_t) st.st_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
|
||||
return -ENOMEM;
|
||||
loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
xsprintf_sys_block_path(p, NULL, dev);
|
||||
loop_fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (loop_fd < 0)
|
||||
return -errno;
|
||||
|
||||
|
27
src/basic/crypt-util.c
Normal file
27
src/basic/crypt-util.c
Normal file
@ -0,0 +1,27 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#include "crypt-util.h"
|
||||
#include "log.h"
|
||||
|
||||
void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
|
||||
log_debug("%s", msg);
|
||||
}
|
||||
#endif
|
34
src/basic/crypt-util.h
Normal file
34
src/basic/crypt-util.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#if HAVE_LIBCRYPTSETUP
|
||||
#include <libcryptsetup.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */
|
||||
#ifndef CRYPT_LUKS
|
||||
#define CRYPT_LUKS NULL
|
||||
#endif
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free);
|
||||
|
||||
void cryptsetup_log_glue(int level, const char *msg, void *usrptr);
|
||||
#endif
|
@ -23,5 +23,18 @@
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
int encode_devnode_name(const char *str, char *str_enc, size_t len);
|
||||
int whitelisted_char_for_devnode(char c, const char *additional);
|
||||
|
||||
#define SYS_BLOCK_PATH_MAX(suffix) \
|
||||
(strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
|
||||
#define xsprintf_sys_block_path(buf, suffix, devno) \
|
||||
xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
|
||||
|
||||
#define DEV_NUM_PATH_MAX \
|
||||
(strlen("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t))
|
||||
#define xsprintf_dev_num_path(buf, type, devno) \
|
||||
xsprintf(buf, "/dev/%s/%u:%u", type, major(devno), minor(devno))
|
||||
|
@ -661,10 +661,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
|
||||
todo += m;
|
||||
|
||||
/* Just a single slash? Then we reached the end. */
|
||||
if (isempty(first) || path_equal(first, "/"))
|
||||
/* Empty? Then we reached the end. */
|
||||
if (isempty(first))
|
||||
break;
|
||||
|
||||
/* Just a single slash? Then we reached the end. */
|
||||
if (path_equal(first, "/")) {
|
||||
/* Preserve the trailing slash */
|
||||
if (!strextend(&done, "/", NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Just a dot? Then let's eat this up. */
|
||||
if (path_equal(first, "/."))
|
||||
continue;
|
||||
@ -726,7 +735,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
if (fstat(child, &st) < 0)
|
||||
return -errno;
|
||||
if ((flags & CHASE_NO_AUTOFS) &&
|
||||
fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
return -EREMOTE;
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
|
@ -61,6 +61,8 @@ basic_sources_plain = files('''
|
||||
copy.h
|
||||
cpu-set-util.c
|
||||
cpu-set-util.h
|
||||
crypt-util.c
|
||||
crypt-util.h
|
||||
def.h
|
||||
device-nodes.c
|
||||
device-nodes.h
|
||||
|
@ -1271,4 +1271,8 @@ struct fib_rule_uid_range {
|
||||
#define AF_VSOCK 40
|
||||
#endif
|
||||
|
||||
#ifndef EXT4_IOC_RESIZE_FS
|
||||
# define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
|
||||
#endif
|
||||
|
||||
#include "missing_syscall.h"
|
||||
|
@ -278,6 +278,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
if (path_equal(t, "/"))
|
||||
return 1;
|
||||
@ -302,7 +303,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd_is_mount_point(fd, basename(t), flags);
|
||||
return fd_is_mount_point(fd, last_path_component(t), flags);
|
||||
}
|
||||
|
||||
int path_get_mnt_id(const char *path, int *ret) {
|
||||
|
@ -703,6 +703,37 @@ char* dirname_malloc(const char *path) {
|
||||
return dir2;
|
||||
}
|
||||
|
||||
const char *last_path_component(const char *path) {
|
||||
/* Finds the last component of the path, preserving the
|
||||
* optional trailing slash that signifies a directory.
|
||||
* a/b/c → c
|
||||
* a/b/c/ → c/
|
||||
* / → /
|
||||
* // → /
|
||||
* /foo/a → a
|
||||
* /foo/a/ → a/
|
||||
* This is different than basename, which returns "" when
|
||||
* a trailing slash is present.
|
||||
*/
|
||||
|
||||
unsigned l, k;
|
||||
|
||||
l = k = strlen(path);
|
||||
if (l == 0) /* special case — an empty string */
|
||||
return path;
|
||||
|
||||
while (k > 0 && path[k-1] == '/')
|
||||
k--;
|
||||
|
||||
if (k == 0) /* the root directory */
|
||||
return path + l - 1;
|
||||
|
||||
while (k > 0 && path[k-1] != '/')
|
||||
k--;
|
||||
|
||||
return path + k;
|
||||
}
|
||||
|
||||
bool filename_is_valid(const char *p) {
|
||||
const char *e;
|
||||
|
||||
|
@ -130,6 +130,7 @@ char *prefix_root(const char *root, const char *path);
|
||||
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
|
||||
|
||||
char* dirname_malloc(const char *path);
|
||||
const char *last_path_component(const char *path);
|
||||
|
||||
bool filename_is_valid(const char *p) _pure_;
|
||||
bool path_is_normalized(const char *p) _pure_;
|
||||
|
@ -193,7 +193,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
|
||||
return F_TYPE_EQUAL(s->f_type, magic_value);
|
||||
}
|
||||
|
||||
int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
|
||||
int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
|
||||
struct statfs s;
|
||||
|
||||
if (fstatfs(fd, &s) < 0)
|
||||
@ -202,14 +202,14 @@ int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
|
||||
return is_fs_type(&s, magic_value);
|
||||
}
|
||||
|
||||
int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
|
||||
int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd_check_fstype(fd, magic_value);
|
||||
return fd_is_fs_type(fd, magic_value);
|
||||
}
|
||||
|
||||
bool is_temporary_fs(const struct statfs *s) {
|
||||
|
@ -57,8 +57,8 @@ int files_same(const char *filea, const char *fileb, int flags);
|
||||
typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
|
||||
|
||||
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
|
||||
int fd_check_fstype(int fd, statfs_f_type_t magic_value);
|
||||
int path_check_fstype(const char *path, statfs_f_type_t magic_value);
|
||||
int fd_is_fs_type(int fd, statfs_f_type_t magic_value);
|
||||
int path_is_fs_type(const char *path, statfs_f_type_t magic_value);
|
||||
|
||||
bool is_temporary_fs(const struct statfs *s) _pure_;
|
||||
int fd_is_temporary_fs(int fd);
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "build.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "def.h"
|
||||
#include "device-nodes.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -118,63 +119,42 @@ int socket_from_display(const char *display, char **path) {
|
||||
}
|
||||
|
||||
int block_get_whole_disk(dev_t d, dev_t *ret) {
|
||||
char *p, *s;
|
||||
char p[SYS_BLOCK_PATH_MAX("/partition")];
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
unsigned n, m;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* If it has a queue this is good enough for us */
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = access(p, F_OK);
|
||||
free(p);
|
||||
|
||||
if (r >= 0) {
|
||||
xsprintf_sys_block_path(p, "/queue", d);
|
||||
if (access(p, F_OK) >= 0) {
|
||||
*ret = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If it is a partition find the originating device */
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = access(p, F_OK);
|
||||
free(p);
|
||||
|
||||
if (r < 0)
|
||||
xsprintf_sys_block_path(p, "/partition", d);
|
||||
if (access(p, F_OK) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* Get parent dev_t */
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
xsprintf_sys_block_path(p, "/../dev", d);
|
||||
r = read_one_line_file(p, &s);
|
||||
free(p);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sscanf(s, "%u:%u", &m, &n);
|
||||
free(s);
|
||||
|
||||
if (r != 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* Only return this if it is really good enough for us. */
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
|
||||
return -ENOMEM;
|
||||
xsprintf_sys_block_path(p, "/queue", makedev(m, n));
|
||||
if (access(p, F_OK) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
r = access(p, F_OK);
|
||||
free(p);
|
||||
|
||||
if (r >= 0) {
|
||||
*ret = makedev(m, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
*ret = makedev(m, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool kexec_loaded(void) {
|
||||
@ -749,7 +729,8 @@ int get_block_device(const char *path, dev_t *dev) {
|
||||
|
||||
int get_block_device_harder(const char *path, dev_t *dev) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
_cleanup_free_ char *p = NULL, *t = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
char p[SYS_BLOCK_PATH_MAX("/slaves")];
|
||||
struct dirent *de, *found = NULL;
|
||||
const char *q;
|
||||
unsigned maj, min;
|
||||
@ -767,9 +748,7 @@ int get_block_device_harder(const char *path, dev_t *dev) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
xsprintf_sys_block_path(p, "/slaves", dt);
|
||||
d = opendir(p);
|
||||
if (!d) {
|
||||
if (errno == ENOENT)
|
||||
|
@ -19,7 +19,6 @@
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <libcryptsetup.h>
|
||||
#include <mntent.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
@ -28,6 +27,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "crypt-util.h"
|
||||
#include "device-util.h"
|
||||
#include "escape.h"
|
||||
#include "fileio.h"
|
||||
@ -39,11 +39,6 @@
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
|
||||
/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */
|
||||
#ifndef CRYPT_LUKS
|
||||
#define CRYPT_LUKS NULL
|
||||
#endif
|
||||
|
||||
/* internal helper */
|
||||
#define ANY_LUKS "LUKS"
|
||||
|
||||
@ -254,10 +249,6 @@ static int parse_options(const char *options) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_glue(int level, const char *msg, void *usrptr) {
|
||||
log_debug("%s", msg);
|
||||
}
|
||||
|
||||
static int disk_major_minor(const char *path, char **ret) {
|
||||
struct stat st;
|
||||
|
||||
@ -604,7 +595,7 @@ static int help(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct crypt_device *cd = NULL;
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
int r = -EINVAL;
|
||||
|
||||
if (argc <= 1) {
|
||||
@ -666,7 +657,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
crypt_set_log_callback(cd, log_glue, NULL);
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
status = crypt_status(cd, argv[2]);
|
||||
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
|
||||
@ -750,7 +741,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
crypt_set_log_callback(cd, log_glue, NULL);
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
r = crypt_deactivate(cd, argv[2]);
|
||||
if (r < 0) {
|
||||
@ -766,9 +757,6 @@ int main(int argc, char *argv[]) {
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (cd)
|
||||
crypt_free(cd);
|
||||
|
||||
free(arg_cipher);
|
||||
free(arg_hash);
|
||||
free(arg_header);
|
||||
|
@ -47,6 +47,14 @@
|
||||
#include "virt.h"
|
||||
#include "volatile-util.h"
|
||||
|
||||
typedef enum MountpointFlags {
|
||||
NOAUTO = 1 << 0,
|
||||
NOFAIL = 1 << 1,
|
||||
AUTOMOUNT = 1 << 2,
|
||||
MAKEFS = 1 << 3,
|
||||
GROWFS = 1 << 4,
|
||||
} MountpointFlags;
|
||||
|
||||
static const char *arg_dest = "/tmp";
|
||||
static const char *arg_dest_late = "/tmp";
|
||||
static bool arg_fstab_enabled = true;
|
||||
@ -91,8 +99,7 @@ static int write_what(FILE *f, const char *what) {
|
||||
static int add_swap(
|
||||
const char *what,
|
||||
struct mntent *me,
|
||||
bool noauto,
|
||||
bool nofail) {
|
||||
MountpointFlags flags) {
|
||||
|
||||
_cleanup_free_ char *name = NULL, *unit = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
@ -150,9 +157,19 @@ static int add_swap(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!noauto) {
|
||||
if (flags & MAKEFS) {
|
||||
r = generator_hook_up_mkswap(arg_dest, what);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (flags & GROWFS)
|
||||
/* TODO: swap devices must be wiped and recreated */
|
||||
log_warning("%s: growing swap devices is currently unsupported.", what);
|
||||
|
||||
if (!(flags & NOAUTO)) {
|
||||
r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
|
||||
nofail ? "wants" : "requires", name);
|
||||
(flags & NOFAIL) ? "wants" : "requires", name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -297,17 +314,16 @@ static int add_mount(
|
||||
const char *fstype,
|
||||
const char *opts,
|
||||
int passno,
|
||||
bool noauto,
|
||||
bool nofail,
|
||||
bool automount,
|
||||
MountpointFlags flags,
|
||||
const char *post,
|
||||
const char *source) {
|
||||
|
||||
_cleanup_free_ char
|
||||
*name = NULL, *unit = NULL,
|
||||
*name = NULL,
|
||||
*automount_name = NULL, *automount_unit = NULL,
|
||||
*filtered = NULL,
|
||||
*where_escaped = NULL;
|
||||
const char *unit;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
@ -330,23 +346,21 @@ static int add_mount(
|
||||
return 0;
|
||||
|
||||
if (path_equal(where, "/")) {
|
||||
if (noauto)
|
||||
if (flags & NOAUTO)
|
||||
log_warning("Ignoring \"noauto\" for root device");
|
||||
if (nofail)
|
||||
if (flags & NOFAIL)
|
||||
log_warning("Ignoring \"nofail\" for root device");
|
||||
if (automount)
|
||||
if (flags & AUTOMOUNT)
|
||||
log_warning("Ignoring automount option for root device");
|
||||
|
||||
noauto = nofail = automount = false;
|
||||
SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false);
|
||||
}
|
||||
|
||||
r = unit_name_from_path(where, ".mount", &name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate unit name: %m");
|
||||
|
||||
unit = strjoin(dest, "/", name);
|
||||
if (!unit)
|
||||
return log_oom();
|
||||
unit = strjoina(dest, "/", name);
|
||||
|
||||
f = fopen(unit, "wxe");
|
||||
if (!f)
|
||||
@ -363,7 +377,7 @@ static int add_mount(
|
||||
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
|
||||
source);
|
||||
|
||||
if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !automount &&
|
||||
if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & AUTOMOUNT) &&
|
||||
fstab_test_yes_no_option(opts, "bg\0" "fg\0")) {
|
||||
/* The default retry timeout that mount.nfs uses for 'bg' mounts
|
||||
* is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
|
||||
@ -374,13 +388,13 @@ static int add_mount(
|
||||
* By placing these options first, they can be over-ridden by
|
||||
* settings in /etc/fstab. */
|
||||
opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts, ",fg");
|
||||
nofail = true;
|
||||
SET_FLAG(flags, NOFAIL, true);
|
||||
}
|
||||
|
||||
if (!nofail && !automount)
|
||||
if (!(flags & NOFAIL) && !(flags & AUTOMOUNT))
|
||||
fprintf(f, "Before=%s\n", post);
|
||||
|
||||
if (!automount && opts) {
|
||||
if (!(flags & AUTOMOUNT) && opts) {
|
||||
r = write_after(f, opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -444,14 +458,26 @@ static int add_mount(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write unit file %s: %m", unit);
|
||||
|
||||
if (!noauto && !automount) {
|
||||
r = generator_add_symlink(dest, post,
|
||||
nofail ? "wants" : "requires", name);
|
||||
if (flags & MAKEFS) {
|
||||
r = generator_hook_up_mkfs(dest, what, where, fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (automount) {
|
||||
if (flags & GROWFS) {
|
||||
r = generator_hook_up_growfs(dest, where, post);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) {
|
||||
r = generator_add_symlink(dest, post,
|
||||
(flags & NOFAIL) ? "wants" : "requires", name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (flags & AUTOMOUNT) {
|
||||
r = unit_name_from_path(where, ".automount", &automount_name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate unit name: %m");
|
||||
@ -504,7 +530,7 @@ static int add_mount(
|
||||
return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
|
||||
|
||||
r = generator_add_symlink(dest, post,
|
||||
nofail ? "wants" : "requires", automount_name);
|
||||
(flags & NOFAIL) ? "wants" : "requires", automount_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -529,7 +555,7 @@ static int parse_fstab(bool initrd) {
|
||||
|
||||
while ((me = getmntent(f))) {
|
||||
_cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
|
||||
bool noauto, nofail;
|
||||
bool makefs, growfs, noauto, nofail;
|
||||
int k;
|
||||
|
||||
if (initrd && !mount_in_initrd(me))
|
||||
@ -571,14 +597,18 @@ static int parse_fstab(bool initrd) {
|
||||
}
|
||||
}
|
||||
|
||||
makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
|
||||
growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
|
||||
noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
|
||||
nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
|
||||
log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s",
|
||||
log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
|
||||
what, where, me->mnt_type,
|
||||
yes_no(makefs),
|
||||
yes_no(noauto), yes_no(nofail));
|
||||
|
||||
if (streq(me->mnt_type, "swap"))
|
||||
k = add_swap(what, me, noauto, nofail);
|
||||
k = add_swap(what, me,
|
||||
makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL);
|
||||
else {
|
||||
bool automount;
|
||||
const char *post;
|
||||
@ -600,14 +630,12 @@ static int parse_fstab(bool initrd) {
|
||||
me->mnt_type,
|
||||
me->mnt_opts,
|
||||
me->mnt_passno,
|
||||
noauto,
|
||||
nofail,
|
||||
automount,
|
||||
makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
|
||||
post,
|
||||
fstab_path);
|
||||
}
|
||||
|
||||
if (k < 0)
|
||||
if (r >= 0 && k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
@ -663,9 +691,7 @@ static int add_sysroot_mount(void) {
|
||||
arg_root_fstype,
|
||||
opts,
|
||||
is_device_path(what) ? 1 : 0, /* passno */
|
||||
false, /* noauto off */
|
||||
false, /* nofail off */
|
||||
false, /* automount off */
|
||||
0, /* makefs off, growfs off, noauto off, nofail off, automount off */
|
||||
SPECIAL_INITRD_ROOT_FS_TARGET,
|
||||
"/proc/cmdline");
|
||||
}
|
||||
@ -718,9 +744,7 @@ static int add_sysroot_usr_mount(void) {
|
||||
arg_usr_fstype,
|
||||
opts,
|
||||
is_device_path(what) ? 1 : 0, /* passno */
|
||||
false, /* noauto off */
|
||||
false, /* nofail off */
|
||||
false, /* automount off */
|
||||
0,
|
||||
SPECIAL_INITRD_FS_TARGET,
|
||||
"/proc/cmdline");
|
||||
}
|
||||
@ -759,9 +783,7 @@ static int add_volatile_var(void) {
|
||||
"tmpfs",
|
||||
"mode=0755",
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
SPECIAL_LOCAL_FS_TARGET,
|
||||
"/proc/cmdline");
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
|
||||
unsigned long extra_flags = 0;
|
||||
|
||||
top = prefix_roota(dest, "/sys");
|
||||
r = path_check_fstype(top, SYSFS_MAGIC);
|
||||
r = path_is_fs_type(top, SYSFS_MAGIC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
|
||||
/* /sys might already be mounted as sysfs by the outer child in the
|
||||
|
317
src/partition/growfs.c
Normal file
317
src/partition/growfs.c
Normal file
@ -0,0 +1,317 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include "crypt-util.h"
|
||||
#include "device-nodes.h"
|
||||
#include "dissect-image.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "log.h"
|
||||
#include "missing.h"
|
||||
#include "mount-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
const char *arg_target = NULL;
|
||||
bool arg_dry_run = false;
|
||||
|
||||
static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
|
||||
assert((uint64_t) (int) blocksize == blocksize);
|
||||
|
||||
if (arg_dry_run)
|
||||
return 0;
|
||||
|
||||
if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0)
|
||||
return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (ext4): %m",
|
||||
path, numblocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
|
||||
struct btrfs_ioctl_vol_args args = {};
|
||||
int r;
|
||||
|
||||
assert((uint64_t) (int) blocksize == blocksize);
|
||||
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
|
||||
if (numblocks * blocksize < 256*1024*1024) {
|
||||
log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
r = snprintf(args.name, sizeof(args.name), "%"PRIu64, numblocks * blocksize);
|
||||
/* The buffer is large enough for any number to fit... */
|
||||
assert((size_t) r < sizeof(args.name));
|
||||
|
||||
if (arg_dry_run)
|
||||
return 0;
|
||||
|
||||
if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0)
|
||||
return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (btrfs): %m",
|
||||
path, numblocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) {
|
||||
char devpath[DEV_NUM_PATH_MAX], main_devpath[DEV_NUM_PATH_MAX];
|
||||
_cleanup_close_ int main_devfd = -1;
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
uint64_t size;
|
||||
int r;
|
||||
|
||||
xsprintf_dev_num_path(main_devpath, "block", main_devno);
|
||||
main_devfd = open(main_devpath, O_RDONLY|O_CLOEXEC);
|
||||
if (main_devfd < 0)
|
||||
return log_error_errno(errno, "Failed to open \"%s\": %m", main_devpath);
|
||||
|
||||
if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
|
||||
return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m",
|
||||
main_devpath);
|
||||
|
||||
log_debug("%s is %"PRIu64" bytes", main_devpath, size);
|
||||
|
||||
xsprintf_dev_num_path(devpath, "block", devno);
|
||||
r = crypt_init(&cd, devpath);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "crypt_init(\"%s\") failed: %m", devpath);
|
||||
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
r = crypt_load(cd, CRYPT_LUKS, NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to load LUKS metadata for %s: %m", devpath);
|
||||
|
||||
if (arg_dry_run)
|
||||
return 0;
|
||||
|
||||
r = crypt_resize(cd, main_devpath, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath);
|
||||
|
||||
if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
|
||||
log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m",
|
||||
devpath);
|
||||
else
|
||||
log_debug("%s is now %"PRIu64" bytes", main_devpath, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
|
||||
dev_t devno;
|
||||
char devpath[DEV_NUM_PATH_MAX];
|
||||
_cleanup_free_ char *fstype = NULL;
|
||||
int r;
|
||||
|
||||
crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL);
|
||||
crypt_set_debug_level(1);
|
||||
|
||||
r = get_block_device_harder(mountpath, &devno);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine underlying block device of \"%s\": %m",
|
||||
mountpath);
|
||||
|
||||
log_debug("Underlying device %d:%d, main dev %d:%d, %s",
|
||||
major(devno), minor(devno),
|
||||
major(main_devno), minor(main_devno),
|
||||
devno == main_devno ? "same" : "different");
|
||||
if (devno == main_devno)
|
||||
return 0;
|
||||
|
||||
xsprintf_dev_num_path(devpath, "block", devno);
|
||||
r = probe_filesystem(devpath, &fstype);
|
||||
if (r == -EUCLEAN)
|
||||
return log_warning_errno(r, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to probe \"%s\": %m", devpath);
|
||||
|
||||
if (streq_ptr(fstype, "crypto_LUKS"))
|
||||
return resize_crypt_luks_device(devno, fstype, main_devno);
|
||||
|
||||
log_debug("Don't know how to resize %s of type %s, ignoring", devpath, strnull(fstype));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help(void) {
|
||||
printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
|
||||
"Grow filesystem or encrypted payload to device size.\n\n"
|
||||
"Options:\n"
|
||||
" -h --help Show this help and exit\n"
|
||||
" --version Print version string and exit\n"
|
||||
" -n --dry-run Just print what would be done\n"
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version" , no_argument, NULL, ARG_VERSION },
|
||||
{ "dry-run", no_argument, NULL, 'n' },
|
||||
{}
|
||||
};
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hn", options, NULL)) >= 0)
|
||||
switch(c) {
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
version();
|
||||
return 0;
|
||||
|
||||
case 'n':
|
||||
arg_dry_run = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unhandled option");
|
||||
}
|
||||
|
||||
if (optind + 1 != argc) {
|
||||
log_error("%s excepts exactly one argument (the mount point).",
|
||||
program_invocation_short_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_target = argv[optind];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
dev_t devno;
|
||||
_cleanup_close_ int mountfd = -1, devfd = -1;
|
||||
int blocksize;
|
||||
uint64_t size, numblocks;
|
||||
char devpath[DEV_NUM_PATH_MAX], fb[FORMAT_BYTES_MAX];
|
||||
struct statfs sfs;
|
||||
int r;
|
||||
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
if (r == 0)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
r = path_is_mount_point(arg_target, NULL, 0);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (r == 0) {
|
||||
log_error_errno(r, "\"%s\" is not a mount point: %m", arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = get_block_device(arg_target, &devno);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = maybe_resize_slave_device(arg_target, devno);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
mountfd = open(arg_target, O_RDONLY|O_CLOEXEC);
|
||||
if (mountfd < 0) {
|
||||
log_error_errno(errno, "Failed to open \"%s\": %m", arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
xsprintf_dev_num_path(devpath, "block", devno);
|
||||
devfd = open(devpath, O_RDONLY|O_CLOEXEC);
|
||||
if (devfd < 0) {
|
||||
log_error_errno(errno, "Failed to open \"%s\": %m", devpath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (ioctl(devfd, BLKBSZGET, &blocksize) != 0) {
|
||||
log_error_errno(errno, "Failed to query block size of \"%s\": %m", devpath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (ioctl(devfd, BLKGETSIZE64, &size) != 0) {
|
||||
log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (size % blocksize != 0)
|
||||
log_notice("Partition size %"PRIu64" is not a multiple of the blocksize %d,"
|
||||
" ignoring %"PRIu64" bytes", size, blocksize, size % blocksize);
|
||||
|
||||
numblocks = size / blocksize;
|
||||
|
||||
if (fstatfs(mountfd, &sfs) < 0) {
|
||||
log_error_errno(errno, "Failed to stat file system \"%s\": %m", arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
switch(sfs.f_type) {
|
||||
case EXT4_SUPER_MAGIC:
|
||||
r = resize_ext4(arg_target, mountfd, devfd, numblocks, blocksize);
|
||||
break;
|
||||
case BTRFS_SUPER_MAGIC:
|
||||
r = resize_btrfs(arg_target, mountfd, devfd, numblocks, blocksize);
|
||||
break;
|
||||
default:
|
||||
log_error("Don't know how to resize fs %llx on \"%s\"",
|
||||
(long long unsigned) sfs.f_type, arg_target);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).",
|
||||
arg_target, format_bytes(fb, sizeof fb, size), numblocks, blocksize);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
110
src/partition/makefs.c
Normal file
110
src/partition/makefs.c
Normal file
@ -0,0 +1,110 @@
|
||||
/***
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2017 Zbigniew Jędrzejewski-Szmek
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "signal-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static int makefs(const char *type, const char *device) {
|
||||
const char *mkfs;
|
||||
pid_t pid;
|
||||
|
||||
if (streq(type, "swap"))
|
||||
mkfs = "/sbin/mkswap";
|
||||
else
|
||||
mkfs = strjoina("/sbin/mkfs.", type);
|
||||
if (access(mkfs, X_OK) != 0)
|
||||
return log_error_errno(errno, "%s is not executable: %m", mkfs);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return log_error_errno(errno, "fork(): %m");
|
||||
|
||||
if (pid == 0) {
|
||||
const char *cmdline[3] = { mkfs, device, NULL };
|
||||
|
||||
/* Child */
|
||||
|
||||
(void) reset_all_signal_handlers();
|
||||
(void) reset_signal_mask();
|
||||
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
|
||||
|
||||
execv(cmdline[0], (char**) cmdline);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return wait_for_terminate_and_warn(mkfs, pid, true);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *device, *type;
|
||||
_cleanup_free_ char *detected = NULL;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
if (argc != 3) {
|
||||
log_error("This program expects two arguments.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
type = argv[1];
|
||||
device = argv[2];
|
||||
|
||||
if (stat(device, &st) < 0) {
|
||||
r = log_error_errno(errno, "Failed to stat \"%s\": %m", device);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!S_ISBLK(st.st_mode))
|
||||
log_info("%s is not a block device.", device);
|
||||
|
||||
r = probe_filesystem(device, &detected);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r,
|
||||
r == -EUCLEAN ?
|
||||
"Cannot reliably determine probe \"%s\", refusing to proceed." :
|
||||
"Failed to probe \"%s\": %m",
|
||||
device);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (detected) {
|
||||
log_info("%s is not empty (type %s), exiting", device, detected);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = makefs(type, device);
|
||||
|
||||
finish:
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
#include "bootspec.h"
|
||||
#include "conf-files.h"
|
||||
#include "def.h"
|
||||
#include "device-nodes.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -420,7 +421,7 @@ static int verify_esp(
|
||||
sd_id128_t *ret_uuid) {
|
||||
|
||||
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
char t[DEV_NUM_PATH_MAX];
|
||||
uint64_t pstart = 0, psize = 0;
|
||||
struct stat st, st2;
|
||||
const char *v, *t2;
|
||||
@ -478,10 +479,7 @@ static int verify_esp(
|
||||
if (detect_container() > 0 || geteuid() != 0)
|
||||
goto finish;
|
||||
|
||||
r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
xsprintf_dev_num_path(t, "block", st.st_dev);
|
||||
errno = 0;
|
||||
b = blkid_new_probe_from_filename(t);
|
||||
if (!b)
|
||||
|
@ -18,12 +18,6 @@
|
||||
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>
|
||||
@ -32,7 +26,9 @@
|
||||
#include "ask-password-api.h"
|
||||
#include "blkid-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"
|
||||
@ -55,25 +51,34 @@
|
||||
#include "udev-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;
|
||||
|
||||
@ -611,7 +616,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
|
||||
|
||||
if (!p->fstype && p->node) {
|
||||
r = probe_filesystem(p->node, &p->fstype);
|
||||
if (r < 0)
|
||||
if (r < 0 && r != -EUCLEAN)
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -652,7 +657,7 @@ DissectedImage* dissected_image_unref(DissectedImage *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);
|
||||
@ -663,13 +668,13 @@ static int is_loop_device(const char *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;
|
||||
}
|
||||
@ -849,7 +854,7 @@ static int decrypt_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);
|
||||
@ -876,37 +881,28 @@ static int decrypt_partition(
|
||||
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;
|
||||
cd = NULL;
|
||||
d->n_decrypted++;
|
||||
|
||||
m->decrypted_node = node;
|
||||
node = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int verity_partition(
|
||||
@ -918,7 +914,7 @@ 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);
|
||||
@ -948,30 +944,27 @@ static int verity_partition(
|
||||
|
||||
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;
|
||||
return r;
|
||||
|
||||
d->decrypted[d->n_decrypted].name = name;
|
||||
name = NULL;
|
||||
|
||||
d->decrypted[d->n_decrypted].device = cd;
|
||||
cd = NULL;
|
||||
d->n_decrypted++;
|
||||
|
||||
m->decrypted_node = node;
|
||||
node = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1033,7 +1026,7 @@ int dissected_image_decrypt(
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ struct DissectedImage {
|
||||
char **os_release;
|
||||
};
|
||||
|
||||
int probe_filesystem(const char *node, char **ret_fstype);
|
||||
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
|
||||
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
|
@ -83,8 +83,8 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
|
||||
fprintf(f,
|
||||
"# Automatically generated by %1$s\n\n"
|
||||
"[Unit]\n"
|
||||
"Documentation=man:systemd-fsck-root.service(8)\n"
|
||||
"Description=File System Check on %2$s\n"
|
||||
"Documentation=man:systemd-fsck-root.service(8)\n"
|
||||
"DefaultDependencies=no\n"
|
||||
"BindsTo=%3$s\n"
|
||||
"After=initrd-root-device.target local-fs-pre.target %3$s\n"
|
||||
@ -248,7 +248,8 @@ int generator_write_device_deps(
|
||||
|
||||
r = unit_name_from_path(node, ".device", &unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit name from path: %m");
|
||||
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
|
||||
node);
|
||||
|
||||
/* See mount_add_default_dependencies for explanation why we create such
|
||||
* dependencies. */
|
||||
@ -266,7 +267,8 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
|
||||
|
||||
r = unit_name_from_path(what, ".device", &unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit name from path: %m");
|
||||
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
|
||||
what);
|
||||
|
||||
return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
|
||||
"# Automatically generated by %s\n\n"
|
||||
@ -277,3 +279,206 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
|
||||
unit,
|
||||
unit);
|
||||
}
|
||||
|
||||
int generator_hook_up_mkswap(
|
||||
const char *dir,
|
||||
const char *what) {
|
||||
|
||||
_cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *unit_file;
|
||||
int r;
|
||||
|
||||
node = fstab_node_to_udev_node(what);
|
||||
if (!node)
|
||||
return log_oom();
|
||||
|
||||
/* Nothing to work on. */
|
||||
if (!is_device_path(node)) {
|
||||
log_error("Cannot format something that is not a device node: %s", node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
|
||||
node);
|
||||
|
||||
unit_file = strjoina(dir, "/", unit);
|
||||
log_debug("Creating %s", unit_file);
|
||||
|
||||
escaped = cescape(node);
|
||||
if (!escaped)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_from_path(what, ".swap", &where_unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
|
||||
what);
|
||||
|
||||
f = fopen(unit_file, "wxe");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to create unit file %s: %m",
|
||||
unit_file);
|
||||
|
||||
fprintf(f,
|
||||
"# Automatically generated by %s\n\n"
|
||||
"[Unit]\n"
|
||||
"Description=Make Swap on %%f\n"
|
||||
"Documentation=man:systemd-mkswap@.service(8)\n"
|
||||
"DefaultDependencies=no\n"
|
||||
"BindsTo=%%i.device\n"
|
||||
"After=%%i.device\n"
|
||||
"Before=%s\n"
|
||||
"Before=shutdown.target\n"
|
||||
"\n"
|
||||
"[Service]\n"
|
||||
"Type=oneshot\n"
|
||||
"RemainAfterExit=yes\n"
|
||||
"ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
|
||||
"TimeoutSec=0\n",
|
||||
program_invocation_short_name,
|
||||
where_unit,
|
||||
escaped);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
|
||||
|
||||
return generator_add_symlink(dir, where_unit, "requires", unit);
|
||||
}
|
||||
|
||||
int generator_hook_up_mkfs(
|
||||
const char *dir,
|
||||
const char *what,
|
||||
const char *where,
|
||||
const char *type) {
|
||||
|
||||
_cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *unit_file;
|
||||
int r;
|
||||
|
||||
node = fstab_node_to_udev_node(what);
|
||||
if (!node)
|
||||
return log_oom();
|
||||
|
||||
/* Nothing to work on. */
|
||||
if (!is_device_path(node)) {
|
||||
log_error("Cannot format something that is not a device node: %s", node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!type || streq(type, "auto")) {
|
||||
log_error("Cannot format partition %s, filesystem type is not specified", node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
|
||||
node);
|
||||
|
||||
unit_file = strjoina(dir, "/", unit);
|
||||
log_debug("Creating %s", unit_file);
|
||||
|
||||
escaped = cescape(node);
|
||||
if (!escaped)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_from_path(where, ".mount", &where_unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
|
||||
where);
|
||||
|
||||
f = fopen(unit_file, "wxe");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to create unit file %s: %m",
|
||||
unit_file);
|
||||
|
||||
fprintf(f,
|
||||
"# Automatically generated by %s\n\n"
|
||||
"[Unit]\n"
|
||||
"Description=Make File System on %%f\n"
|
||||
"Documentation=man:systemd-mkfs@.service(8)\n"
|
||||
"DefaultDependencies=no\n"
|
||||
"BindsTo=%%i.device\n"
|
||||
"After=%%i.device\n"
|
||||
/* fsck might or might not be used, so let's be safe and order
|
||||
* ourselves before both systemd-fsck@.service and the mount unit. */
|
||||
"Before=systemd-fsck@%%i.service\n"
|
||||
"Before=%s\n"
|
||||
"Before=shutdown.target\n"
|
||||
"\n"
|
||||
"[Service]\n"
|
||||
"Type=oneshot\n"
|
||||
"RemainAfterExit=yes\n"
|
||||
"ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
|
||||
"TimeoutSec=0\n",
|
||||
program_invocation_short_name,
|
||||
where_unit,
|
||||
type,
|
||||
escaped);
|
||||
// XXX: what about local-fs-pre.target?
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
|
||||
|
||||
return generator_add_symlink(dir, where_unit, "requires", unit);
|
||||
}
|
||||
|
||||
int generator_hook_up_growfs(
|
||||
const char *dir,
|
||||
const char *where,
|
||||
const char *target) {
|
||||
|
||||
_cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *unit_file;
|
||||
int r;
|
||||
|
||||
escaped = cescape(where);
|
||||
if (!escaped)
|
||||
return log_oom();
|
||||
|
||||
r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
|
||||
where);
|
||||
|
||||
r = unit_name_from_path(where, ".mount", &where_unit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
|
||||
where);
|
||||
|
||||
unit_file = strjoina(dir, "/", unit);
|
||||
log_debug("Creating %s", unit_file);
|
||||
|
||||
f = fopen(unit_file, "wxe");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to create unit file %s: %m",
|
||||
unit_file);
|
||||
|
||||
fprintf(f,
|
||||
"# Automatically generated by %s\n\n"
|
||||
"[Unit]\n"
|
||||
"Description=Grow File System on %%f\n"
|
||||
"Documentation=man:systemd-growfs@.service(8)\n"
|
||||
"DefaultDependencies=no\n"
|
||||
"BindsTo=%%i.mount\n"
|
||||
"After=%%i.mount\n"
|
||||
"Before=shutdown.target\n"
|
||||
"Before=%s\n"
|
||||
"\n"
|
||||
"[Service]\n"
|
||||
"Type=oneshot\n"
|
||||
"RemainAfterExit=yes\n"
|
||||
"ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
|
||||
"TimeoutSec=0\n",
|
||||
program_invocation_short_name,
|
||||
target,
|
||||
escaped);
|
||||
|
||||
return generator_add_symlink(dir, where_unit, "wants", unit);
|
||||
}
|
||||
|
@ -39,11 +39,24 @@ int generator_write_timeouts(
|
||||
char **filtered);
|
||||
|
||||
int generator_write_device_deps(
|
||||
const char *dir,
|
||||
const char *what,
|
||||
const char *where,
|
||||
const char *opts);
|
||||
const char *dir,
|
||||
const char *what,
|
||||
const char *where,
|
||||
const char *opts);
|
||||
|
||||
int generator_write_initrd_root_device_deps(
|
||||
const char *dir,
|
||||
const char *what);
|
||||
|
||||
int generator_hook_up_mkswap(
|
||||
const char *dir,
|
||||
const char *what);
|
||||
int generator_hook_up_mkfs(
|
||||
const char *dir,
|
||||
const char *what,
|
||||
const char *where,
|
||||
const char *type);
|
||||
int generator_hook_up_growfs(
|
||||
const char *dir,
|
||||
const char *where,
|
||||
const char *target);
|
||||
|
@ -35,7 +35,7 @@
|
||||
static void test_chase_symlinks(void) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
char temp[] = "/tmp/test-chase.XXXXXX";
|
||||
const char *top, *p, *q;
|
||||
const char *top, *p, *pslash, *q, *qslash;
|
||||
int r;
|
||||
|
||||
assert_se(mkdtemp(temp));
|
||||
@ -66,93 +66,114 @@ static void test_chase_symlinks(void) {
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/usr"));
|
||||
|
||||
result = mfree(result);
|
||||
|
||||
pslash = strjoina(p, "/");
|
||||
r = chase_symlinks(pslash, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/usr/"));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
r = chase_symlinks(pslash, temp, 0, &result);
|
||||
assert_se(r == -ENOENT);
|
||||
|
||||
q = strjoina(temp, "/usr");
|
||||
|
||||
r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(path_equal(result, q));
|
||||
result = mfree(result);
|
||||
|
||||
qslash = strjoina(q, "/");
|
||||
|
||||
r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(path_equal(result, qslash));
|
||||
result = mfree(result);
|
||||
|
||||
assert_se(mkdir(q, 0700) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(pslash, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, qslash));
|
||||
result = mfree(result);
|
||||
|
||||
p = strjoina(temp, "/slash");
|
||||
assert_se(symlink("/", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/"));
|
||||
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, temp));
|
||||
result = mfree(result);
|
||||
|
||||
/* Paths that would "escape" outside of the "root" */
|
||||
|
||||
p = strjoina(temp, "/6dots");
|
||||
assert_se(symlink("../../..", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, temp));
|
||||
result = mfree(result);
|
||||
|
||||
p = strjoina(temp, "/6dotsusr");
|
||||
assert_se(symlink("../../../usr", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, q));
|
||||
result = mfree(result);
|
||||
|
||||
p = strjoina(temp, "/top/8dotsusr");
|
||||
assert_se(symlink("../../../../usr", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, q));
|
||||
result = mfree(result);
|
||||
|
||||
/* Paths that contain repeated slashes */
|
||||
|
||||
p = strjoina(temp, "/slashslash");
|
||||
assert_se(symlink("///usr///", p) >= 0);
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/usr"));
|
||||
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, temp, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, q));
|
||||
result = mfree(result);
|
||||
|
||||
/* Paths using . */
|
||||
|
||||
result = mfree(result);
|
||||
r = chase_symlinks("/etc/./.././", NULL, 0, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(path_equal(result, "/"));
|
||||
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
|
||||
assert_se(r > 0 && path_equal(result, "/etc"));
|
||||
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
|
||||
assert_se(r == -ENOTDIR);
|
||||
result = mfree(result);
|
||||
|
||||
/* Path that loops back to self */
|
||||
|
||||
result = mfree(result);
|
||||
p = strjoina(temp, "/recursive-symlink");
|
||||
assert_se(symlink("recursive-symlink", p) >= 0);
|
||||
r = chase_symlinks(p, NULL, 0, &result);
|
||||
|
@ -26,8 +26,10 @@
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "log.h"
|
||||
#include "log.h"
|
||||
#include "mount-util.h"
|
||||
#include "path-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
|
||||
@ -100,6 +102,165 @@ static void test_mnt_id(void) {
|
||||
hashmap_free_free(h);
|
||||
}
|
||||
|
||||
static void test_path_is_mount_point(void) {
|
||||
int fd;
|
||||
char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
|
||||
_cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
|
||||
_cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
|
||||
_cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
|
||||
|
||||
assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/", NULL, 0) > 0);
|
||||
assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("//", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
|
||||
assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
|
||||
|
||||
assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
|
||||
assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
|
||||
|
||||
/* we'll create a hierarchy of different kinds of dir/file/link
|
||||
* layouts:
|
||||
*
|
||||
* <tmp>/file1, <tmp>/file2
|
||||
* <tmp>/link1 -> file1, <tmp>/link2 -> file2
|
||||
* <tmp>/dir1/
|
||||
* <tmp>/dir1/file
|
||||
* <tmp>/dirlink1 -> dir1
|
||||
* <tmp>/dirlink1file -> dirlink1/file
|
||||
* <tmp>/dir2/
|
||||
* <tmp>/dir2/file
|
||||
*/
|
||||
|
||||
/* file mountpoints */
|
||||
assert_se(mkdtemp(tmp_dir) != NULL);
|
||||
file1 = path_join(NULL, tmp_dir, "file1");
|
||||
assert_se(file1);
|
||||
file2 = path_join(NULL, tmp_dir, "file2");
|
||||
assert_se(file2);
|
||||
fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
link1 = path_join(NULL, tmp_dir, "link1");
|
||||
assert_se(link1);
|
||||
assert_se(symlink("file1", link1) == 0);
|
||||
link2 = path_join(NULL, tmp_dir, "link2");
|
||||
assert_se(link1);
|
||||
assert_se(symlink("file2", link2) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(file1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, 0) == 0);
|
||||
|
||||
/* directory mountpoints */
|
||||
dir1 = path_join(NULL, tmp_dir, "dir1");
|
||||
assert_se(dir1);
|
||||
assert_se(mkdir(dir1, 0755) == 0);
|
||||
dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
|
||||
assert_se(dirlink1);
|
||||
assert_se(symlink("dir1", dirlink1) == 0);
|
||||
dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
|
||||
assert_se(dirlink1file);
|
||||
assert_se(symlink("dirlink1/file", dirlink1file) == 0);
|
||||
dir2 = path_join(NULL, tmp_dir, "dir2");
|
||||
assert_se(dir2);
|
||||
assert_se(mkdir(dir2, 0755) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
|
||||
|
||||
/* file in subdirectory mountpoints */
|
||||
dir1file = path_join(NULL, dir1, "file");
|
||||
assert_se(dir1file);
|
||||
fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
|
||||
assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
|
||||
|
||||
/* these tests will only work as root */
|
||||
if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
|
||||
int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
|
||||
const char *file2d;
|
||||
|
||||
/* files */
|
||||
/* capture results in vars, to avoid dangling mounts on failure */
|
||||
log_info("%s: %s", __func__, file2);
|
||||
rf = path_is_mount_point(file2, NULL, 0);
|
||||
rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
file2d = strjoina(file2, "/");
|
||||
log_info("%s: %s", __func__, file2d);
|
||||
rdf = path_is_mount_point(file2d, NULL, 0);
|
||||
rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
log_info("%s: %s", __func__, link2);
|
||||
rlf = path_is_mount_point(link2, NULL, 0);
|
||||
rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(file2) == 0);
|
||||
|
||||
assert_se(rf == 1);
|
||||
assert_se(rt == 1);
|
||||
assert_se(rdf == -ENOTDIR);
|
||||
assert_se(rdt == -ENOTDIR);
|
||||
assert_se(rlf == 0);
|
||||
assert_se(rlt == 1);
|
||||
|
||||
/* dirs */
|
||||
dir2file = path_join(NULL, dir2, "file");
|
||||
assert_se(dir2file);
|
||||
fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
|
||||
assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
|
||||
|
||||
log_info("%s: %s", __func__, dir1);
|
||||
rf = path_is_mount_point(dir1, NULL, 0);
|
||||
rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
|
||||
log_info("%s: %s", __func__, dirlink1);
|
||||
rlf = path_is_mount_point(dirlink1, NULL, 0);
|
||||
rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
|
||||
log_info("%s: %s", __func__, dirlink1file);
|
||||
/* its parent is a mount point, but not /file itself */
|
||||
rl1f = path_is_mount_point(dirlink1file, NULL, 0);
|
||||
rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(dir1) == 0);
|
||||
|
||||
assert_se(rf == 1);
|
||||
assert_se(rt == 1);
|
||||
assert_se(rlf == 0);
|
||||
assert_se(rlt == 1);
|
||||
assert_se(rl1f == 0);
|
||||
assert_se(rl1t == 0);
|
||||
|
||||
} else
|
||||
printf("Skipping bind mount file test: %m\n");
|
||||
|
||||
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
@ -113,6 +274,7 @@ int main(int argc, char *argv[]) {
|
||||
test_mount_propagation_flags(" ", -EINVAL, 0);
|
||||
|
||||
test_mnt_id();
|
||||
test_path_is_mount_point();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
***/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@ -376,143 +375,6 @@ static void test_prefix_root(void) {
|
||||
test_prefix_root_one("/foo///", "//bar", "/foo/bar");
|
||||
}
|
||||
|
||||
static void test_path_is_mount_point(void) {
|
||||
int fd;
|
||||
char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
|
||||
_cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
|
||||
_cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
|
||||
_cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
|
||||
|
||||
assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
|
||||
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
|
||||
|
||||
assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
|
||||
assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
|
||||
|
||||
/* we'll create a hierarchy of different kinds of dir/file/link
|
||||
* layouts:
|
||||
*
|
||||
* <tmp>/file1, <tmp>/file2
|
||||
* <tmp>/link1 -> file1, <tmp>/link2 -> file2
|
||||
* <tmp>/dir1/
|
||||
* <tmp>/dir1/file
|
||||
* <tmp>/dirlink1 -> dir1
|
||||
* <tmp>/dirlink1file -> dirlink1/file
|
||||
* <tmp>/dir2/
|
||||
* <tmp>/dir2/file
|
||||
*/
|
||||
|
||||
/* file mountpoints */
|
||||
assert_se(mkdtemp(tmp_dir) != NULL);
|
||||
file1 = path_join(NULL, tmp_dir, "file1");
|
||||
assert_se(file1);
|
||||
file2 = path_join(NULL, tmp_dir, "file2");
|
||||
assert_se(file2);
|
||||
fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
link1 = path_join(NULL, tmp_dir, "link1");
|
||||
assert_se(link1);
|
||||
assert_se(symlink("file1", link1) == 0);
|
||||
link2 = path_join(NULL, tmp_dir, "link2");
|
||||
assert_se(link1);
|
||||
assert_se(symlink("file2", link2) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(file1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(link1, NULL, 0) == 0);
|
||||
|
||||
/* directory mountpoints */
|
||||
dir1 = path_join(NULL, tmp_dir, "dir1");
|
||||
assert_se(dir1);
|
||||
assert_se(mkdir(dir1, 0755) == 0);
|
||||
dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
|
||||
assert_se(dirlink1);
|
||||
assert_se(symlink("dir1", dirlink1) == 0);
|
||||
dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
|
||||
assert_se(dirlink1file);
|
||||
assert_se(symlink("dirlink1/file", dirlink1file) == 0);
|
||||
dir2 = path_join(NULL, tmp_dir, "dir2");
|
||||
assert_se(dir2);
|
||||
assert_se(mkdir(dir2, 0755) == 0);
|
||||
|
||||
assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
|
||||
|
||||
/* file in subdirectory mountpoints */
|
||||
dir1file = path_join(NULL, dir1, "file");
|
||||
assert_se(dir1file);
|
||||
fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
|
||||
assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
|
||||
assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
|
||||
|
||||
/* these tests will only work as root */
|
||||
if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
|
||||
int rt, rf, rlt, rlf, rl1t, rl1f;
|
||||
|
||||
/* files */
|
||||
/* capture results in vars, to avoid dangling mounts on failure */
|
||||
rf = path_is_mount_point(file2, NULL, 0);
|
||||
rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(link2, NULL, 0);
|
||||
rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(file2) == 0);
|
||||
|
||||
assert_se(rf == 1);
|
||||
assert_se(rt == 1);
|
||||
assert_se(rlf == 0);
|
||||
assert_se(rlt == 1);
|
||||
|
||||
/* dirs */
|
||||
dir2file = path_join(NULL, dir2, "file");
|
||||
assert_se(dir2file);
|
||||
fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
|
||||
assert_se(fd > 0);
|
||||
close(fd);
|
||||
|
||||
assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
|
||||
|
||||
rf = path_is_mount_point(dir1, NULL, 0);
|
||||
rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
|
||||
rlf = path_is_mount_point(dirlink1, NULL, 0);
|
||||
rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
|
||||
/* its parent is a mount point, but not /file itself */
|
||||
rl1f = path_is_mount_point(dirlink1file, NULL, 0);
|
||||
rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
|
||||
|
||||
assert_se(umount(dir1) == 0);
|
||||
|
||||
assert_se(rf == 1);
|
||||
assert_se(rt == 1);
|
||||
assert_se(rlf == 0);
|
||||
assert_se(rlt == 1);
|
||||
assert_se(rl1f == 0);
|
||||
assert_se(rl1t == 0);
|
||||
|
||||
} else
|
||||
printf("Skipping bind mount file test: %m\n");
|
||||
|
||||
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||
}
|
||||
|
||||
static void test_file_in_same_dir(void) {
|
||||
char *t;
|
||||
|
||||
@ -537,6 +399,21 @@ static void test_file_in_same_dir(void) {
|
||||
free(t);
|
||||
}
|
||||
|
||||
static void test_last_path_component(void) {
|
||||
assert_se(streq(last_path_component("a/b/c"), "c"));
|
||||
assert_se(streq(last_path_component("a/b/c/"), "c/"));
|
||||
assert_se(streq(last_path_component("/"), "/"));
|
||||
assert_se(streq(last_path_component("//"), "/"));
|
||||
assert_se(streq(last_path_component("///"), "/"));
|
||||
assert_se(streq(last_path_component("."), "."));
|
||||
assert_se(streq(last_path_component("./."), "."));
|
||||
assert_se(streq(last_path_component("././"), "./"));
|
||||
assert_se(streq(last_path_component("././/"), ".//"));
|
||||
assert_se(streq(last_path_component("/foo/a"), "a"));
|
||||
assert_se(streq(last_path_component("/foo/a/"), "a/"));
|
||||
assert_se(streq(last_path_component(""), ""));
|
||||
}
|
||||
|
||||
static void test_filename_is_valid(void) {
|
||||
char foo[FILENAME_MAX+2];
|
||||
int i;
|
||||
@ -621,8 +498,8 @@ int main(int argc, char **argv) {
|
||||
test_strv_resolve();
|
||||
test_path_startswith();
|
||||
test_prefix_root();
|
||||
test_path_is_mount_point();
|
||||
test_file_in_same_dir();
|
||||
test_last_path_component();
|
||||
test_filename_is_valid();
|
||||
test_hidden_or_backup_file();
|
||||
test_skip_dev_prefix();
|
||||
|
@ -72,16 +72,16 @@ static void test_path_is_os_tree(void) {
|
||||
assert_se(path_is_os_tree("/idontexist") == -ENOENT);
|
||||
}
|
||||
|
||||
static void test_path_check_fstype(void) {
|
||||
static void test_path_is_fs_type(void) {
|
||||
/* run might not be a mount point in build chroots */
|
||||
if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) {
|
||||
assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
|
||||
assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
|
||||
assert_se(path_is_fs_type("/run", TMPFS_MAGIC) > 0);
|
||||
assert_se(path_is_fs_type("/run", BTRFS_SUPER_MAGIC) == 0);
|
||||
}
|
||||
assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0);
|
||||
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
|
||||
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
|
||||
assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT);
|
||||
assert_se(path_is_fs_type("/proc", PROC_SUPER_MAGIC) > 0);
|
||||
assert_se(path_is_fs_type("/proc", BTRFS_SUPER_MAGIC) == 0);
|
||||
assert_se(path_is_fs_type("/proc", BTRFS_SUPER_MAGIC) == 0);
|
||||
assert_se(path_is_fs_type("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT);
|
||||
}
|
||||
|
||||
static void test_path_is_temporary_fs(void) {
|
||||
@ -96,7 +96,7 @@ int main(int argc, char *argv[]) {
|
||||
test_files_same();
|
||||
test_is_symlink();
|
||||
test_path_is_os_tree();
|
||||
test_path_check_fstype();
|
||||
test_path_is_fs_type();
|
||||
test_path_is_temporary_fs();
|
||||
|
||||
return 0;
|
||||
|
@ -320,6 +320,19 @@ static void test_delete_trailing_chars(void) {
|
||||
assert_se(s == input3);
|
||||
}
|
||||
|
||||
static void test_delete_trailing_slashes(void) {
|
||||
char s1[] = "foobar//",
|
||||
s2[] = "foobar/",
|
||||
s3[] = "foobar",
|
||||
s4[] = "";
|
||||
|
||||
assert_se(streq(delete_trailing_chars(s1, "_"), "foobar//"));
|
||||
assert_se(streq(delete_trailing_chars(s1, "/"), "foobar"));
|
||||
assert_se(streq(delete_trailing_chars(s2, "/"), "foobar"));
|
||||
assert_se(streq(delete_trailing_chars(s3, "/"), "foobar"));
|
||||
assert_se(streq(delete_trailing_chars(s4, "/"), ""));
|
||||
}
|
||||
|
||||
static void test_skip_leading_chars(void) {
|
||||
char input1[] = " \n \r k \n \r ",
|
||||
input2[] = "kkkkthiskkkiskkkaktestkkk",
|
||||
@ -399,6 +412,7 @@ int main(int argc, char *argv[]) {
|
||||
test_endswith_no_case();
|
||||
test_delete_chars();
|
||||
test_delete_trailing_chars();
|
||||
test_delete_trailing_slashes();
|
||||
test_skip_leading_chars();
|
||||
test_in_charset();
|
||||
test_split_pair();
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "device-nodes.h"
|
||||
#include "dirent-util.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
@ -337,7 +338,7 @@ out:
|
||||
void udev_node_add(struct udev_device *dev, bool apply,
|
||||
mode_t mode, uid_t uid, gid_t gid,
|
||||
struct udev_list *seclabel_list) {
|
||||
char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)];
|
||||
char filename[DEV_NUM_PATH_MAX];
|
||||
struct udev_list_entry *list_entry;
|
||||
|
||||
log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT,
|
||||
@ -347,10 +348,9 @@ void udev_node_add(struct udev_device *dev, bool apply,
|
||||
return;
|
||||
|
||||
/* always add /dev/{block,char}/$major:$minor */
|
||||
xsprintf(filename, "/dev/%s/%u:%u",
|
||||
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
|
||||
major(udev_device_get_devnum(dev)),
|
||||
minor(udev_device_get_devnum(dev)));
|
||||
xsprintf_dev_num_path(filename,
|
||||
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
|
||||
udev_device_get_devnum(dev));
|
||||
node_symlink(dev, udev_device_get_devnode(dev), filename);
|
||||
|
||||
/* create/update symlinks, add symlinks to name index */
|
||||
@ -360,16 +360,15 @@ void udev_node_add(struct udev_device *dev, bool apply,
|
||||
|
||||
void udev_node_remove(struct udev_device *dev) {
|
||||
struct udev_list_entry *list_entry;
|
||||
char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)];
|
||||
char filename[DEV_NUM_PATH_MAX];
|
||||
|
||||
/* remove/update symlinks, remove symlinks from name index */
|
||||
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
|
||||
link_update(dev, udev_list_entry_get_name(list_entry), false);
|
||||
|
||||
/* remove /dev/{block,char}/$major:$minor */
|
||||
xsprintf(filename, "/dev/%s/%u:%u",
|
||||
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
|
||||
major(udev_device_get_devnum(dev)),
|
||||
minor(udev_device_get_devnum(dev)));
|
||||
xsprintf_dev_num_path(filename,
|
||||
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
|
||||
udev_device_get_devnum(dev));
|
||||
unlink(filename);
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <libcryptsetup.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "crypt-util.h"
|
||||
#include "log.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "string-util.h"
|
||||
@ -41,12 +41,8 @@ static int help(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_glue(int level, const char *msg, void *usrptr) {
|
||||
log_debug("%s", msg);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct crypt_device *cd = NULL;
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
int r;
|
||||
|
||||
if (argc <= 1) {
|
||||
@ -89,7 +85,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
crypt_set_log_callback(cd, log_glue, NULL);
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
status = crypt_status(cd, argv[2]);
|
||||
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
|
||||
@ -127,7 +123,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
crypt_set_log_callback(cd, log_glue, NULL);
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
r = crypt_deactivate(cd, argv[2]);
|
||||
if (r < 0) {
|
||||
@ -144,9 +140,6 @@ int main(int argc, char *argv[]) {
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (cd)
|
||||
crypt_free(cd);
|
||||
|
||||
free(arg_root_hash);
|
||||
free(arg_data_what);
|
||||
free(arg_hash_what);
|
||||
|
Loading…
Reference in New Issue
Block a user