1
0
mirror of https://github.com/systemd/systemd synced 2026-04-25 08:25:12 +02:00

Compare commits

...

26 Commits

Author SHA1 Message Date
Lennart Poettering
4bcf5c11c8
Merge pull request #23011 from mrc0mmand/TEST-64-md
test: add MD-related tests to TEST-64
2022-04-13 22:26:50 +02:00
Lennart Poettering
1fde09b3b4
Merge pull request #22759 from msekletar/issue-18077-long-sysfs-paths-hashing
Create "hashed" unit names from long paths
2022-04-13 22:21:38 +02:00
Yu Watanabe
bd1f75d5fb
Merge pull request #23070 from poettering/devnum-split
basic: some devnum handling tweaks and refactorings
2022-04-14 03:45:39 +09:00
Yu Watanabe
f4e357d77f
Merge pull request #22969 from poettering/udevadm-tree
udevadm: add new "udevadm info --tree" command
2022-04-14 03:45:14 +09:00
Michal Sekletar
b26f4f0028 tests: reflect that we can now handle devices with very long sysfs paths 2022-04-13 17:46:52 +02:00
Frantisek Sumsal
3c9af05cae test: add MD-related tests to TEST-64 2022-04-14 00:12:02 +09:00
Yu Watanabe
9a78ee002f udev: set ID_IGNORE_DISKSEQ for md devices 2022-04-14 00:12:02 +09:00
Yu Watanabe
0224c31ea3 udev: do not create disk/by-diskseq symlink when ID_IGNORE_DISKSEQ property is set 2022-04-14 00:11:57 +09:00
Yu Watanabe
03b894fc09 sd-device: skip diskseq verification when ID_IGNORE_DISKSEQ property is set
Some drivers do not announce the diskseq change.
E.g. for md devices, the kernel increments the diskseq *after*
emitting a 'change' uevent when backing block devices are added to
a md device, and udevd does not receive no uevent which contains
the new diskseq.
2022-04-14 00:10:28 +09:00
Lennart Poettering
02b9047edf devnum-util: catch potential stack overruns early 2022-04-13 16:26:31 +02:00
Lennart Poettering
ec61371fe6 devnum-util: define helper macros for formatting devnum major/minor pairs
And port some parts over.
2022-04-13 16:26:31 +02:00
Lennart Poettering
7176f06c9e basic: split out dev_t related calls into new devno-util.[ch]
No actual code changes, just splitting out of some dev_t handling
related calls from stat-util.[ch], they are quite a number already, and
deserve their own module now I think.

Also, try to settle on the name "devnum" as the name for the concept,
instead of "devno" or "dev" or "devid". "devnum" is the name exported in
udev APIs, hence probably best to stick to that. (this just renames a
few symbols to "devum", local variables are left untouched, to make the
patch not too invasive)

No actual code changes.
2022-04-13 16:26:31 +02:00
Yu Watanabe
17d97d4c90 udev: create disk/by-diskseq symlink only when the device has diskseq
Follow-up for 0d08db7f89ee665a9dcb6dd66c1f9e203192e8ec.
2022-04-13 23:09:47 +09:00
Lennart Poettering
2f048ad0fe update TODO 2022-04-13 14:41:05 +02:00
Lennart Poettering
7b6d168376 udevadm: use xopendirat() where appropriate
And while we are at it, let's use more appropriate open flags.

O_NONBLOCk is pointless in combination with O_NOFOLLOW.

O_NOFOLLOW makes a ton of sense otoh, since the inode is supposed to be
a dir, we just checked.

THe other flags are implied by xopendirat()
2022-04-13 14:41:05 +02:00
Lennart Poettering
9117d94b9a udevadm: add new --tree mode to "udevadm info"
sysfs is a tree, hence let's a mode that allows showing it as such.
2022-04-13 14:41:05 +02:00
Lennart Poettering
00dfbf35fd sd-device: include parent devices in enumeration 2022-04-13 14:41:05 +02:00
Lennart Poettering
0b859c9773 sd-device: properly support some corner case syspath 2022-04-13 14:41:05 +02:00
Lennart Poettering
be247835c7 sd-device: add some comments 2022-04-13 14:41:05 +02:00
Lennart Poettering
29a5428f1c sd-device: filter regular files when enumerating
Currently if enumerating with debug logging you'll likely see something
like this:

sd-device: the syspath "/sys/class/devcoredump/disabled" is not a directory.
sd-device: the syspath "/sys/class/firmware/timeout" is not a directory.
sd-device: the syspath "/sys/class/zram-control/hot_remove" is not a directory.
sd-device: the syspath "/sys/class/zram-control/hot_add" is not a directory.
sd-device: the syspath "/sys/class/drm/version" is not a directory.

This is because these sysfs classes place regular files in these
directories, which we so far didn't expect.

Let's filter them early, and only bother with enumerated inodes if they
actually are dirs or symlinks, i.e. can be kobject dirs. Regular file
definitely never are kobject dirs...
2022-04-13 14:41:04 +02:00
Lennart Poettering
e7c7d79d52 sd-device: split out checking of matches from enumerator_scan_dir_and_add_devices()
No change in behaviour, just some splitting out of code.
2022-04-13 14:41:04 +02:00
Lennart Poettering
5cf0ee31ec sd-device: generate e better error code when trying to allocate sd_device for non-dir
Currently, for sysfs paths outside of /sys/devices/ we do better
checking if something is a suitable path: we check if it's actually a
directory, and if not return ENODEV.

Let's make the codepath for nodes *inside* of /sys/device/ similar:
let's also return ENODEV if the path supplied is not a directory.

Previously, we'd return ENOTDIR in that case, which is quite confusing I
think.
2022-04-13 14:41:03 +02:00
Lennart Poettering
a7910612a5 sd-device: don't accept non-sysfs paths
There are some file systems mounted below /sys/ that are not actually
sysfs, i.e. are not arranged in a sysfs/kobject style. Let's refuse
those early. (Example, /sys/fs/cgroup/ and similar.)

(Also, let's add an env var for this, so that it can be turned off for
test cases.)
2022-04-13 14:40:13 +02:00
Lennart Poettering
1793bb6112 sd-device: use chase_symlinks() O_PATH fd
chase_symlinks() can return its last O_PATH fd to us. Let's use that and
make the access() check a bit tighter by going via faccessat() on the
O_PATH fd.

This doesn't really change too much, but is nice in context of the next
commit, which uses the O_PATH fd in one other way.
2022-04-13 14:40:09 +02:00
Michal Sekletar
2ef0101e0b tests: add test case for long unit names 2022-04-08 15:18:29 +02:00
Michal Sekletar
1d0727e76f core: shorten long unit names that are based on paths and append path hash at the end
Fixes #18077
2022-04-08 15:18:24 +02:00
61 changed files with 1051 additions and 447 deletions

3
TODO
View File

@ -164,9 +164,6 @@ Features:
sd_device object, so that data passed into sd_device_new_from_devnum() can
also be queried.
* udevadm: a new "tree" verb that shows tree of devices as syspath hierarchy,
along with their properties. uninitialized devices should be greyed out.
* bootctl: show whether UEFI audit mode is available
* sd-event: optionally, if per-event source rate limit is hit, downgrade

View File

@ -199,7 +199,7 @@ All tools:
or whenever they change if it wants to integrate with `systemd-logind`'s
APIs.
`systemd-udevd`:
`systemd-udevd` and sd-device library:
* `$NET_NAMING_SCHEME=` — if set, takes a network naming scheme (i.e. one of
"v238", "v239", "v240"…, or the special value "latest") as parameter. If
@ -211,6 +211,10 @@ All tools:
prefixed with `:` in which case the kernel command line option takes
precedence, if it is specified as well.
* `$SYSTEMD_DEVICE_VERIFY_SYSFS` — if set to "0", disables verification that
devices sysfs path are actually backed by sysfs. Relaxing this verification
is useful for testing purposes.
`nss-systemd`:
* `$SYSTEMD_NSS_BYPASS_SYNTHETIC=1` — if set, `nss-systemd` won't synthesize

View File

@ -160,6 +160,15 @@
along the chain, up to the root of sysfs that can be used in udev rules.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<term><option>--tree</option></term>
<listitem>
<para>Display a sysfs tree. This recursively iterates through the sysfs hierarchy and displays it
in a tree structure. If a path is specified only the subtree below that directory is
shown. This will show both device and subsystem items.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-x</option></term>
<term><option>--export</option></term>

View File

@ -4,6 +4,10 @@
ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}"
ACTION=="remove", GOTO="default_end"
# The md driver increments diskseq *after* emitting 'change' uevent.
# Drop the line below if it is fixed on the kernel side.
SUBSYSTEM=="block", KERNEL=="md*", ENV{ID_IGNORE_DISKSEQ}="1"
SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}"
# select "system RTC" or just use the first one

View File

@ -124,6 +124,6 @@ ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/
# by-diskseq link (if an app is told to open a path like this, they may parse
# the diskseq number from the path, then issue BLKGETDISKSEQ to verify they really got
# the right device, to access specific disks in a race-free fashion)
ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"
ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"
LABEL="persistent_storage_end"

135
src/basic/devnum-util.c Normal file
View File

@ -0,0 +1,135 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <string.h>
#include <sys/stat.h>
#include "chase-symlinks.h"
#include "devnum-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
int parse_devnum(const char *s, dev_t *ret) {
const char *major;
unsigned x, y;
size_t n;
int r;
n = strspn(s, DIGITS);
if (n == 0)
return -EINVAL;
if (n > DECIMAL_STR_MAX(dev_t))
return -EINVAL;
if (s[n] != ':')
return -EINVAL;
major = strndupa_safe(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
r = safe_atou(s + n + 1, &y);
if (r < 0)
return r;
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
return -ERANGE;
*ret = makedev(x, y);
return 0;
}
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
const char *t;
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
if (S_ISCHR(mode))
t = "char";
else if (S_ISBLK(mode))
t = "block";
else
return -ENODEV;
if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
return -ENOMEM;
return 0;
}
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
assert(ret);
if (major(devnum) == 0 && minor(devnum) == 0) {
char *s;
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
* /dev/block/ and /dev/char/, hence we handle them specially here. */
if (S_ISCHR(mode))
s = strdup("/run/systemd/inaccessible/chr");
else if (S_ISBLK(mode))
s = strdup("/run/systemd/inaccessible/blk");
else
return -ENODEV;
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
r = device_path_make_major_minor(mode, devnum, &p);
if (r < 0)
return r;
return chase_symlinks(p, NULL, 0, ret, NULL);
}
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) {
mode_t mode;
dev_t devnum;
int r;
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
* path cannot be parsed like this. */
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
mode = S_IFCHR;
devnum = makedev(0, 0);
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
mode = S_IFBLK;
devnum = makedev(0, 0);
} else {
const char *w;
w = path_startswith(path, "/dev/block/");
if (w)
mode = S_IFBLK;
else {
w = path_startswith(path, "/dev/char/");
if (!w)
return -ENODEV;
mode = S_IFCHR;
}
r = parse_devnum(w, &devnum);
if (r < 0)
return r;
}
if (ret_mode)
*ret_mode = mode;
if (ret_devnum)
*ret_devnum = devnum;
return 0;
}

51
src/basic/devnum-util.h Normal file
View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <sys/types.h>
#include "stdio-util.h"
int parse_devnum(const char *s, dev_t *ret);
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
* such a test would be pointless in such a case.) */
#define DEVICE_MAJOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 12); \
\
})
#define DEVICE_MINOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 20); \
})
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
static inline bool devnum_set_and_equal(dev_t a, dev_t b) {
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
* know" and we'll return false */
return a == b && a != 0;
}
/* Maximum string length for a major:minor string. (Note that DECIMAL_STR_MAX includes space for a trailing NUL) */
#define DEVNUM_STR_MAX (DECIMAL_STR_MAX(dev_t)-1+1+DECIMAL_STR_MAX(dev_t))
#define DEVNUM_FORMAT_STR "%u:%u"
#define DEVNUM_FORMAT_VAL(d) major(d), minor(d)
static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
return ASSERT_PTR(snprintf_ok(buf, DEVNUM_STR_MAX, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d)));
}
#define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {})

View File

@ -40,6 +40,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_TREE_RIGHT] = "`-",
[SPECIAL_GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_TREE_TOP] = ",-",
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
@ -81,6 +82,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_TREE_TOP] = u8"┌─",
/* Single glyphs in both cases */
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"",
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"",
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"",

View File

@ -12,6 +12,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_RIGHT,
SPECIAL_GLYPH_TREE_SPACE,
SPECIAL_GLYPH_TREE_TOP,
SPECIAL_GLYPH_VERTICAL_DOTTED,
SPECIAL_GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_WHITE_CIRCLE,

View File

@ -32,6 +32,8 @@ basic_sources = files(
'conf-files.c',
'conf-files.h',
'def.h',
'devnum-util.c',
'devnum-util.h',
'dirent-util.c',
'dirent-util.h',
'dns-def.h',

View File

@ -692,34 +692,6 @@ int parse_ip_prefix_length(const char *s, int *ret) {
return 0;
}
int parse_dev(const char *s, dev_t *ret) {
const char *major;
unsigned x, y;
size_t n;
int r;
n = strspn(s, DIGITS);
if (n == 0)
return -EINVAL;
if (s[n] != ':')
return -EINVAL;
major = strndupa_safe(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
r = safe_atou(s + n + 1, &y);
if (r < 0)
return r;
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
return -ERANGE;
*ret = makedev(x, y);
return 0;
}
int parse_oom_score_adjust(const char *s, int *ret) {
int r, v;

View File

@ -12,7 +12,6 @@
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s);

View File

@ -314,101 +314,6 @@ int fd_verify_directory(int fd) {
return stat_verify_directory(&st);
}
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
const char *t;
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
if (S_ISCHR(mode))
t = "char";
else if (S_ISBLK(mode))
t = "block";
else
return -ENODEV;
if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
return -ENOMEM;
return 0;
}
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
assert(ret);
if (major(devno) == 0 && minor(devno) == 0) {
char *s;
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
* /dev/block/ and /dev/char/, hence we handle them specially here. */
if (S_ISCHR(mode))
s = strdup("/run/systemd/inaccessible/chr");
else if (S_ISBLK(mode))
s = strdup("/run/systemd/inaccessible/blk");
else
return -ENODEV;
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
r = device_path_make_major_minor(mode, devno, &p);
if (r < 0)
return r;
return chase_symlinks(p, NULL, 0, ret, NULL);
}
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
mode_t mode;
dev_t devno;
int r;
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
* path cannot be parsed like this. */
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
mode = S_IFCHR;
devno = makedev(0, 0);
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
mode = S_IFBLK;
devno = makedev(0, 0);
} else {
const char *w;
w = path_startswith(path, "/dev/block/");
if (w)
mode = S_IFBLK;
else {
w = path_startswith(path, "/dev/char/");
if (!w)
return -ENODEV;
mode = S_IFCHR;
}
r = parse_dev(w, &devno);
if (r < 0)
return r;
}
if (ret_mode)
*ret_mode = mode;
if (ret_devno)
*ret_devno = devno;
return 0;
}
int proc_mounted(void) {
int r;

View File

@ -71,29 +71,6 @@ int fd_verify_regular(int fd);
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
* such a test would be pointless in such a case.) */
#define DEVICE_MAJOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 12); \
\
})
#define DEVICE_MINOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 20); \
})
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
int proc_mounted(void);
bool stat_inode_same(const struct stat *a, const struct stat *b);
@ -119,9 +96,3 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
struct new_statx nsx; \
} var
#endif
static inline bool devid_set_and_equal(dev_t a, dev_t b) {
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
* know" and we'll return false */
return a == b && a != 0;
}

View File

@ -10,17 +10,18 @@
#include "string-util-fundamental.h"
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
#define NEWLINE "\n\r"
#define QUOTES "\"\'"
#define COMMENTS "#;"
#define GLOB_CHARS "*?["
#define DIGITS "0123456789"
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define WHITESPACE " \t\n\r"
#define NEWLINE "\n\r"
#define QUOTES "\"\'"
#define COMMENTS "#;"
#define GLOB_CHARS "*?["
#define DIGITS "0123456789"
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
static inline char* strstr_ptr(const char *haystack, const char *needle) {
if (!haystack || !needle)

View File

@ -22,6 +22,7 @@
#include "alloc-util.h"
#include "def.h"
#include "devnum-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"

View File

@ -5,12 +5,17 @@
#include <stdint.h>
#include <stdlib.h>
#include "sd-id128.h"
#include "alloc-util.h"
#include "glob-util.h"
#include "hexdecoct.h"
#include "memory-util.h"
#include "path-util.h"
#include "random-util.h"
#include "sparse-endian.h"
#include "special.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
@ -31,6 +36,9 @@
VALID_CHARS_WITH_AT \
"[]!-*?"
#define LONG_UNIT_NAME_HASH_KEY SD_ID128_MAKE(ec,f2,37,fb,58,32,4a,32,84,9f,06,9b,0d,21,eb,9a)
#define UNIT_NAME_HASH_LENGTH_CHARS 16
bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
const char *e, *i, *at;
@ -507,6 +515,68 @@ int unit_name_template(const char *f, char **ret) {
return 0;
}
bool unit_name_is_hashed(const char *name) {
char *s;
if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
return false;
assert_se(s = strrchr(name, '.'));
if (s - name < UNIT_NAME_HASH_LENGTH_CHARS + 1)
return false;
s -= UNIT_NAME_HASH_LENGTH_CHARS;
if (s[-1] != '_')
return false;
for (size_t i = 0; i < UNIT_NAME_HASH_LENGTH_CHARS; i++)
if (!strchr(LOWERCASE_HEXDIGITS, s[i]))
return false;
return true;
}
int unit_name_hash_long(const char *name, char **ret) {
_cleanup_free_ char *n = NULL, *hash = NULL;
char *suffix;
le64_t h;
size_t len;
if (strlen(name) < UNIT_NAME_MAX)
return -EMSGSIZE;
suffix = strrchr(name, '.');
if (!suffix)
return -EINVAL;
if (unit_type_from_string(suffix+1) < 0)
return -EINVAL;
h = htole64(siphash24_string(name, LONG_UNIT_NAME_HASH_KEY.bytes));
hash = hexmem(&h, sizeof(h));
if (!hash)
return -ENOMEM;
assert_se(strlen(hash) == UNIT_NAME_HASH_LENGTH_CHARS);
len = UNIT_NAME_MAX - 1 - strlen(suffix+1) - UNIT_NAME_HASH_LENGTH_CHARS - 2;
assert(len > 0 && len < UNIT_NAME_MAX);
n = strndup(name, len);
if (!n)
return -ENOMEM;
if (!strextend(&n, "_", hash, suffix))
return -ENOMEM;
assert_se(unit_name_is_valid(n, UNIT_NAME_PLAIN));
*ret = TAKE_PTR(n);
return 0;
}
int unit_name_from_path(const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *p = NULL, *s = NULL;
int r;
@ -526,8 +596,17 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
if (!s)
return -ENOMEM;
if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */
return -ENAMETOOLONG;
if (strlen(s) >= UNIT_NAME_MAX) {
_cleanup_free_ char *n = NULL;
log_debug("Unit name \"%s\" too long, falling back to hashed unit name.", s);
r = unit_name_hash_long(s, &n);
if (r < 0)
return r;
free_and_replace(s, n);
}
/* Refuse if this for some other reason didn't result in a valid name */
if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
@ -581,6 +660,9 @@ int unit_name_to_path(const char *name, char **ret) {
if (r < 0)
return r;
if (unit_name_is_hashed(name))
return -ENAMETOOLONG;
return unit_name_path_unescape(prefix, ret);
}

View File

@ -44,6 +44,9 @@ int unit_name_replace_instance(const char *f, const char *i, char **ret);
int unit_name_template(const char *f, char **ret);
int unit_name_hash_long(const char *name, char **ret);
bool unit_name_is_hashed(const char *name);
int unit_name_from_path(const char *path, const char *suffix, char **ret);
int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret);
int unit_name_to_path(const char *name, char **ret);

View File

@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "bootspec.h"
#include "devnum-util.h"
#include "efi-api.h"
#include "efi-loader.h"
#include "efivars.h"
@ -16,7 +17,6 @@
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
#include "stat-util.h"
#include "sync-util.h"
#include "terminal-util.h"
#include "util.h"
@ -121,7 +121,7 @@ static int acquire_path(void) {
"Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
"Alternatively, use --path= to specify path to mount point.");
if (esp_path && xbootldr_path && !devid_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
if (esp_path && xbootldr_path && !devnum_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
a = strv_new(esp_path, xbootldr_path);
else if (esp_path)
a = strv_new(esp_path);

View File

@ -16,6 +16,7 @@
#include "blkid-util.h"
#include "bootspec.h"
#include "copy.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "efi-api.h"
#include "efi-loader.h"
@ -595,7 +596,7 @@ static int boot_config_load_and_select(
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
* find the same entries twice. */
bool same = esp_path && xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
if (r < 0)

View File

@ -5,12 +5,12 @@
#include "bpf-devices.h"
#include "bpf-program.h"
#include "devnum-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"

View File

@ -16,6 +16,7 @@
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "devnum-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "in-addr-prefix-util.h"
@ -31,7 +32,6 @@
#include "procfs-util.h"
#include "restrict-ifaces.h"
#include "special.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@ -1084,7 +1084,7 @@ static int set_bfq_weight(Unit *u, const char *controller, dev_t dev, uint64_t i
bfq_weight = BFQ_WEIGHT(io_weight);
if (major(dev) > 0)
xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), bfq_weight);
xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), bfq_weight);
else
xsprintf(buf, "%" PRIu64 "\n", bfq_weight);
@ -1117,7 +1117,7 @@ static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_
r1 = set_bfq_weight(u, "io", dev, io_weight);
xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight);
xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), io_weight);
r2 = cg_set_attribute("io", u->cgroup_path, "io.weight", buf);
/* Look at the configured device, when both fail, prefer io.weight errno. */
@ -1138,7 +1138,7 @@ static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint
if (r < 0)
return;
xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight);
xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), blkio_weight);
(void) set_attribute_and_warn(u, "blkio", "blkio.weight_device", buf);
}
@ -1152,9 +1152,9 @@ static void cgroup_apply_io_device_latency(Unit *u, const char *dev_path, usec_t
return;
if (target != USEC_INFINITY)
xsprintf(buf, "%u:%u target=%" PRIu64 "\n", major(dev), minor(dev), target);
xsprintf(buf, DEVNUM_FORMAT_STR " target=%" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), target);
else
xsprintf(buf, "%u:%u target=max\n", major(dev), minor(dev));
xsprintf(buf, DEVNUM_FORMAT_STR " target=max\n", DEVNUM_FORMAT_VAL(dev));
(void) set_attribute_and_warn(u, "io", "io.latency", buf);
}
@ -1173,7 +1173,7 @@ static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t
else
xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0");
xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev),
xsprintf(buf, DEVNUM_FORMAT_STR " rbps=%s wbps=%s riops=%s wiops=%s\n", DEVNUM_FORMAT_VAL(dev),
limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX],
limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]);
(void) set_attribute_and_warn(u, "io", "io.max", buf);
@ -1186,10 +1186,10 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6
if (lookup_block_device(dev_path, &dev) < 0)
return;
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps);
sprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), rbps);
(void) set_attribute_and_warn(u, "blkio", "blkio.throttle.read_bps_device", buf);
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps);
sprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), wbps);
(void) set_attribute_and_warn(u, "blkio", "blkio.throttle.write_bps_device", buf);
}

View File

@ -622,6 +622,9 @@ static int mount_add_extras(Mount *m) {
if (!m->where) {
r = unit_name_to_path(u->id, &m->where);
if (r == -ENAMETOOLONG)
log_unit_error_errno(u, r, "Failed to derive mount point path from unit name, because unit name is hashed. "
"Set \"Where=\" in the unit file explicitly.");
if (r < 0)
return r;
}

View File

@ -12,6 +12,7 @@
#include "base-filesystem.h"
#include "chase-symlinks.h"
#include "dev-setup.h"
#include "devnum-util.h"
#include "env-util.h"
#include "escape.h"
#include "extension-release.h"
@ -885,10 +886,10 @@ add_symlink:
return 0;
/* Create symlinks like /dev/char/1:9 → ../urandom */
if (asprintf(&sl, "%s/dev/%s/%u:%u",
if (asprintf(&sl, "%s/dev/%s/" DEVNUM_FORMAT_STR,
temporary_mount,
S_ISCHR(st.st_mode) ? "char" : "block",
major(st.st_rdev), minor(st.st_rdev)) < 0)
DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
(void) mkdir_parents(sl, 0755);

View File

@ -12,6 +12,7 @@
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "dropin.h"
@ -57,8 +58,8 @@ static int open_parent_block_device(dev_t devnum, int *ret_fd) {
if (sd_device_get_devname(d, &name) < 0) {
r = sd_device_get_syspath(d, &name);
if (r < 0) {
log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m",
major(devnum), minor(devnum));
log_device_debug_errno(d, r, "Device " DEVNUM_FORMAT_STR " does not have a name, ignoring: %m",
DEVNUM_FORMAT_VAL(devnum));
return 0;
}
}

View File

@ -5,6 +5,7 @@
#include <sys/stat.h>
#include "alloc-util.h"
#include "devnum-util.h"
#include "fileio.h"
#include "log.h"
#include "util.h"
@ -12,7 +13,6 @@
int main(int argc, char *argv[]) {
struct stat st;
const char *device;
_cleanup_free_ char *major_minor = NULL;
int r;
if (argc != 2) {
@ -40,14 +40,9 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) {
log_oom();
return EXIT_FAILURE;
}
r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_DISABLE_BUFFER);
r = write_string_file("/sys/power/resume", FORMAT_DEVNUM(st.st_rdev), WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor);
log_error_errno(r, "Failed to write '" DEVNUM_FORMAT_STR "' to /sys/power/resume: %m", DEVNUM_FORMAT_VAL(st.st_rdev));
return EXIT_FAILURE;
}
@ -58,6 +53,6 @@ int main(int argc, char *argv[]) {
* no hibernation image).
*/
log_info("Could not resume from '%s' (%s).", device, major_minor);
log_info("Could not resume from '%s' (" DEVNUM_FORMAT_STR ").", device, DEVNUM_FORMAT_VAL(st.st_rdev));
return EXIT_SUCCESS;
}

View File

@ -527,7 +527,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
previous_devno = st.st_dev;
r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
r = quotactl_devnum(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "No UID quota support on %s, ignoring.", where);

View File

@ -19,6 +19,7 @@
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
#include "devnum-util.h"
#include "dm-util.h"
#include "env-util.h"
#include "errno-util.h"
@ -46,7 +47,6 @@
#include "process-util.h"
#include "random-util.h"
#include "resize-fs.h"
#include "stat-util.h"
#include "strv.h"
#include "sync-util.h"
#include "tmpfile-util.h"
@ -1301,7 +1301,7 @@ int home_setup_luks(
return log_error_errno(r, "Failed to stat block device %s: %m", n);
assert(S_ISBLK(st.st_mode));
if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
if (access(sysfs, F_OK) < 0) {
@ -1312,7 +1312,7 @@ int home_setup_luks(
} else {
_cleanup_free_ char *buffer = NULL;
if (asprintf(&sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev)) < 0)
if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/start", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
r = read_one_line_file(sysfs, &buffer);
@ -2205,7 +2205,7 @@ int home_create_luks(
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing.");
if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
if (access(sysfs, F_OK) < 0) {
if (errno != ENOENT)

View File

@ -54,7 +54,7 @@ int home_update_quota_classic(UserRecord *h, const char *path) {
if (devno == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system %s not backed by a block device.", path);
r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
r = quotactl_devnum(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r))
return log_error_errno(r, "No UID quota support on %s.", path);
@ -74,7 +74,7 @@ int home_update_quota_classic(UserRecord *h, const char *path) {
req.dqb_valid = QIF_BLIMITS;
req.dqb_bsoftlimit = req.dqb_bhardlimit = h->disk_size / QIF_DQBLKSIZE;
r = quotactl_devno(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
r = quotactl_devnum(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
if (r < 0) {
if (r == -ESRCH)
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "UID quota not available on %s.", path);

View File

@ -547,73 +547,34 @@ static int match_initialized(sd_device_enumerator *enumerator, sd_device *device
return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0);
}
static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
_cleanup_closedir_ DIR *dir = NULL;
char *path;
int k, r = 0;
static int test_matches(
sd_device_enumerator *enumerator,
sd_device *device) {
int r;
assert(enumerator);
assert(basedir);
assert(device);
path = strjoina("/sys/", basedir, "/");
/* Checks all matches, except for the sysname match (which the caller should check beforehand) */
if (subdir1)
path = strjoina(path, subdir1, "/");
r = match_initialized(enumerator, device);
if (r <= 0)
return r;
if (subdir2)
path = strjoina(path, subdir2, "/");
if (!device_match_parent(device, enumerator->match_parent, NULL))
return false;
dir = opendir(path);
if (!dir)
/* this is necessarily racey, so ignore missing directories */
return (errno == ENOENT && (subdir1 || subdir2)) ? 0 : -errno;
if (!match_tag(enumerator, device))
return false;
FOREACH_DIRENT_ALL(de, dir, return -errno) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char syspath[strlen(path) + 1 + strlen(de->d_name) + 1];
if (!match_property(enumerator, device))
return false;
if (de->d_name[0] == '.')
continue;
if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
return false;
if (!match_sysname(enumerator, de->d_name))
continue;
(void) sprintf(syspath, "%s%s", path, de->d_name);
k = sd_device_new_from_syspath(&device, syspath);
if (k < 0) {
if (k != -ENODEV)
/* this is necessarily racey, so ignore missing devices */
r = k;
continue;
}
k = match_initialized(enumerator, device);
if (k <= 0) {
if (k < 0)
r = k;
continue;
}
if (!device_match_parent(device, enumerator->match_parent, NULL))
continue;
if (!match_tag(enumerator, device))
continue;
if (!match_property(enumerator, device))
continue;
if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
continue;
k = device_enumerator_add_device(enumerator, device);
if (k < 0)
r = k;
}
return r;
return true;
}
static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
@ -638,6 +599,128 @@ static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsys
return false;
}
static bool relevant_sysfs_subdir(const struct dirent *de) {
assert(de);
if (de->d_name[0] == '.')
return false;
/* Also filter out regular files and such, i.e. stuff that definitely isn't a kobject path. (Note
* that we rely on the fact that sysfs fills in d_type here, i.e. doesn't do DT_UNKNOWN) */
return IN_SET(de->d_type, DT_DIR, DT_LNK);
}
static int enumerator_scan_dir_and_add_devices(
sd_device_enumerator *enumerator,
const char *basedir,
const char *subdir1,
const char *subdir2) {
_cleanup_closedir_ DIR *dir = NULL;
char *path;
int k, r = 0;
assert(enumerator);
assert(basedir);
path = strjoina("/sys/", basedir, "/");
if (subdir1)
path = strjoina(path, subdir1, "/");
if (subdir2)
path = strjoina(path, subdir2, "/");
dir = opendir(path);
if (!dir)
/* this is necessarily racey, so ignore missing directories */
return (errno == ENOENT && (subdir1 || subdir2)) ? 0 : -errno;
FOREACH_DIRENT_ALL(de, dir, return -errno) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char syspath[strlen(path) + 1 + strlen(de->d_name) + 1];
sd_device *upwards;
if (!relevant_sysfs_subdir(de))
continue;
if (!match_sysname(enumerator, de->d_name))
continue;
(void) sprintf(syspath, "%s%s", path, de->d_name);
k = sd_device_new_from_syspath(&device, syspath);
if (k < 0) {
if (k != -ENODEV)
/* this is necessarily racey, so ignore missing devices */
r = k;
continue;
}
k = test_matches(enumerator, device);
if (k <= 0) {
if (k < 0)
r = k;
continue;
}
k = device_enumerator_add_device(enumerator, device);
if (k < 0)
r = k;
/* Also include all potentially matching parent devices in the enumeration. These are things
* like root busses e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not
* linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */
upwards = device;
for (;;) {
const char *ss, *usn;
k = sd_device_get_parent(upwards, &upwards);
if (k == -ENOENT) /* Reached the top? */
break;
if (k < 0) {
r = k;
break;
}
k = sd_device_get_subsystem(upwards, &ss);
if (k == -ENOENT) /* Has no subsystem? */
continue;
if (k < 0) {
r = k;
break;
}
if (!match_subsystem(enumerator, ss))
continue;
k = sd_device_get_sysname(upwards, &usn);
if (k < 0) {
r = k;
break;
}
if (!match_sysname(enumerator, usn))
continue;
k = test_matches(enumerator, upwards);
if (k < 0)
break;
if (k == 0)
continue;
k = device_enumerator_add_device(enumerator, upwards);
if (k < 0)
r = k;
else if (k == 0) /* Exists already? Then no need to go further up. */
break;
}
}
return r;
}
static int enumerator_scan_dir(
sd_device_enumerator *enumerator,
const char *basedir,
@ -659,7 +742,7 @@ static int enumerator_scan_dir(
FOREACH_DIRENT_ALL(de, dir, return -errno) {
int k;
if (de->d_name[0] == '.')
if (!relevant_sysfs_subdir(de))
continue;
if (!match_subsystem(enumerator, subsystem ? : de->d_name))

View File

@ -12,7 +12,9 @@
#include "device-internal.h"
#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@ -20,12 +22,12 @@
#include "hashmap.h"
#include "id128-util.h"
#include "macro.h"
#include "missing_magic.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
@ -150,7 +152,9 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
_syspath);
if (verify) {
r = chase_symlinks(_syspath, NULL, 0, &syspath, NULL);
_cleanup_close_ int fd = -1;
r = chase_symlinks(_syspath, NULL, 0, &syspath, &fd);
if (r == -ENOENT)
/* the device does not exist (any more?) */
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
@ -181,28 +185,50 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
path_simplify(syspath);
}
if (path_startswith(syspath, "/sys/devices/")) {
char *path;
if (path_startswith(syspath, "/sys/devices/")) {
/* For proper devices, stricter rules apply: they must have a 'uevent' file,
* otherwise we won't allow them */
/* all 'devices' require an 'uevent' file */
path = strjoina(syspath, "/uevent");
if (access(path, F_OK) < 0) {
if (faccessat(fd, "uevent", F_OK, 0) < 0) {
if (errno == ENOENT)
/* This is not a valid device.
* Note, this condition is quite often satisfied when
* enumerating devices or finding a parent device.
/* This is not a valid device. Note, this condition is quite often
* satisfied when enumerating devices or finding a parent device.
* Hence, use log_trace_errno() here. */
return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the uevent file \"%s\" does not exist.", path);
"sd-device: the uevent file \"%s/uevent\" does not exist.", syspath);
if (errno == ENOTDIR)
/* Not actually a directory. */
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the syspath \"%s\" is not a directory.", syspath);
return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
return log_debug_errno(errno, "sd-device: cannot find uevent file for %s: %m", syspath);
}
} else {
/* everything else just needs to be a directory */
if (!is_dir(syspath, false))
struct stat st;
/* For everything else lax rules apply: they just need to be a directory */
if (fstat(fd, &st) < 0)
return log_debug_errno(errno, "sd-device: failed to check if syspath \"%s\" is a directory: %m", syspath);
if (!S_ISDIR(st.st_mode))
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the syspath \"%s\" is not a directory.", syspath);
}
/* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
* things are not arranged as kobjects in kernel, and hence don't necessarily have
* kobject/attribute structure. */
r = getenv_bool_secure("SYSTEMD_DEVICE_VERIFY_SYSFS");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
if (r != 0) {
r = fd_is_fs_type(fd, SYSFS_MAGIC);
if (r < 0)
return log_debug_errno(r, "sd-device: failed to check if syspath \"%s\" is backed by sysfs.", syspath);
if (r == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
}
} else {
syspath = strdup(_syspath);
if (!syspath)
@ -235,7 +261,7 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
if (r < 0)
return r;
r = device_set_syspath(device, syspath, true);
r = device_set_syspath(device, syspath, /* verify= */ true);
if (r < 0)
return r;
@ -413,7 +439,10 @@ _public_ int sd_device_new_from_subsystem_sysname(
const char *subsys = memdupa_suffix0(sysname, sep - sysname);
sep++;
r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
else
r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
if (r < 0)
return r;
if (r > 0)
@ -786,7 +815,7 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
if (isempty(id))
return -EINVAL;
r = parse_dev(id + 1, &devt);
r = parse_devnum(id + 1, &devt);
if (r < 0)
return r;
@ -914,10 +943,13 @@ int device_set_drivers_subsystem(sd_device *device) {
return r;
drivers = strstr(devpath, "/drivers/");
if (!drivers)
drivers = endswith(devpath, "/drivers");
if (!drivers)
return -EINVAL;
r = path_find_last_component(devpath, false, &drivers, &p);
/* Find the path component immediately before the "/drivers/" string */
r = path_find_last_component(devpath, /* accept_dot_dot= */ false, &drivers, &p);
if (r < 0)
return r;
if (r == 0)
@ -959,11 +991,11 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
if (subsystem)
r = device_set_subsystem(device, subsystem);
/* use implicit names */
else if (path_startswith(device->devpath, "/module/"))
else if (!isempty(path_startswith(device->devpath, "/module/")))
r = device_set_subsystem(device, "module");
else if (strstr(syspath, "/drivers/"))
else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers"))
r = device_set_drivers_subsystem(device);
else if (PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/"))
else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
r = device_set_subsystem(device, "subsystem");
else {
device->subsystem_set = true;
@ -2281,7 +2313,7 @@ _public_ int sd_device_trigger_with_uuid(
_public_ int sd_device_open(sd_device *device, int flags) {
_cleanup_close_ int fd = -1, fd2 = -1;
const char *devname, *subsystem = NULL;
const char *devname, *subsystem = NULL, *val = NULL;
uint64_t q, diskseq = 0;
struct stat st;
dev_t devnum;
@ -2306,10 +2338,6 @@ _public_ int sd_device_open(sd_device *device, int flags) {
if (r < 0 && r != -ENOENT)
return r;
r = sd_device_get_diskseq(device, &diskseq);
if (r < 0 && r != -ENOENT)
return r;
fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0)
return -errno;
@ -2327,6 +2355,16 @@ _public_ int sd_device_open(sd_device *device, int flags) {
if (FLAGS_SET(flags, O_PATH))
return TAKE_FD(fd);
r = sd_device_get_property_value(device, "ID_IGNORE_DISKSEQ", &val);
if (r < 0 && r != -ENOENT)
return r;
if (!val || parse_boolean(val) <= 0) {
r = sd_device_get_diskseq(device, &diskseq);
if (r < 0 && r != -ENOENT)
return r;
}
fd2 = open(FORMAT_PROC_FD_PATH(fd), flags);
if (fd2 < 0)
return -errno;

View File

@ -8,6 +8,7 @@
#include "bus-label.h"
#include "bus-polkit.h"
#include "bus-util.h"
#include "devnum-util.h"
#include "fd-util.h"
#include "logind-brightness.h"
#include "logind-dbus.h"
@ -21,7 +22,6 @@
#include "missing_capability.h"
#include "path-util.h"
#include "signal-util.h"
#include "stat-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"

View File

@ -15,6 +15,7 @@
#include "audit-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "devnum-util.h"
#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
@ -377,7 +378,7 @@ static int session_load_devices(Session *s, const char *devices) {
break;
}
k = parse_dev(word, &dev);
k = parse_devnum(word, &dev);
if (k < 0) {
r = k;
continue;

View File

@ -8,6 +8,7 @@
#include "bus-util.h"
#include "cap-list.h"
#include "cpu-set-util.h"
#include "devnum-util.h"
#include "env-util.h"
#include "format-util.h"
#include "fs-util.h"
@ -20,7 +21,6 @@
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"

View File

@ -14,6 +14,7 @@
#include "btrfs-util.h"
#include "cryptsetup-util.h"
#include "device-nodes.h"
#include "devnum-util.h"
#include "dissect-image.h"
#include "escape.h"
#include "fd-util.h"

View File

@ -24,6 +24,7 @@
#include "conf-parser.h"
#include "cryptsetup-util.h"
#include "def.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "efivars.h"
#include "errno-util.h"
@ -55,7 +56,6 @@
#include "resize-fs.h"
#include "sort-util.h"
#include "specifier.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@ -3885,7 +3885,7 @@ static int resolve_copy_blocks_auto(
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", q);
r = parse_dev(t, &sl);
r = parse_devnum(t, &sl);
if (r < 0) {
log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
continue;

View File

@ -6,12 +6,12 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing_magic.h"
#include "parse-util.h"
#include "stat-util.h"
int block_get_whole_disk(dev_t d, dev_t *ret) {
char p[SYS_BLOCK_PATH_MAX("/partition")];
@ -44,7 +44,7 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
if (r < 0)
return r;
r = parse_dev(s, &devt);
r = parse_devnum(s, &devt);
if (r < 0)
return r;
@ -170,7 +170,7 @@ int block_get_originating(dev_t dt, dev_t *ret) {
if (r < 0)
return r;
r = parse_dev(t, &devt);
r = parse_devnum(t, &devt);
if (r < 0)
return -EINVAL;

View File

@ -5,6 +5,7 @@
#include "bootspec.h"
#include "bootspec-fundamental.h"
#include "conf-files.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "efi-loader.h"
#include "env-file.h"
@ -15,7 +16,6 @@
#include "pe-header.h"
#include "recurse-dir.h"
#include "sort-util.h"
#include "stat-util.h"
#include "strv.h"
#include "unaligned.h"
@ -918,7 +918,7 @@ int boot_config_load_auto(
return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
/* If both paths actually refer to the same inode, suppress the xbootldr path */
if (esp_where && xbootldr_where && devid_set_and_equal(esp_devid, xbootldr_devid))
if (esp_where && xbootldr_where && devnum_set_and_equal(esp_devid, xbootldr_devid))
xbootldr_where = mfree(xbootldr_where);
return boot_config_load(config, esp_where, xbootldr_where);

View File

@ -1,11 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/magic.h>
#include <sys/vfs.h>
#include "sd-device.h"
#include "alloc-util.h"
#include "blkid-util.h"
#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "find-esp.h"

View File

@ -18,6 +18,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "device-util.h"
#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
@ -876,7 +877,7 @@ static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) {
r = read_one_line_file(sysfs, &buffer);
if (r < 0)
return r;
r = parse_dev(buffer, &devno);
r = parse_devnum(buffer, &devno);
if (r < 0)
return r;

View File

@ -1,20 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/quota.h>
#include <sys/stat.h>
#include "alloc-util.h"
#include "blockdev-util.h"
#include "devnum-util.h"
#include "quota-util.h"
#include "stat-util.h"
int quotactl_devno(int cmd, dev_t devno, int id, void *addr) {
int quotactl_devnum(int cmd, dev_t devnum, int id, void *addr) {
_cleanup_free_ char *devnode = NULL;
int r;
/* Like quotactl() but takes a dev_t instead of a path to a device node, and fixes caddr_t → void*,
* like we should, today */
r = device_path_make_major_minor(S_IFBLK, devno, &devnode);
r = device_path_make_major_minor(S_IFBLK, devnum, &devnode);
if (r < 0)
return r;
@ -37,5 +38,5 @@ int quotactl_path(int cmd, const char *path, int id, void *addr) {
if (devno == 0) /* Doesn't have a block device */
return -ENODEV;
return quotactl_devno(cmd, devno, id, addr);
return quotactl_devnum(cmd, devno, id, addr);
}

View File

@ -15,5 +15,5 @@ static inline int QCMD_FIXED(uint32_t cmd, uint32_t type) {
return (int) QCMD(cmd, type);
}
int quotactl_devno(int cmd, dev_t devno, int id, void *addr);
int quotactl_devnum(int cmd, dev_t devnum, int id, void *addr);
int quotactl_path(int cmd, const char *path, int id, void *addr);

View File

@ -20,13 +20,13 @@
#include "btrfs-util.h"
#include "conf-parser.h"
#include "def.h"
#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
#include "sleep-config.h"
#include "stat-util.h"
@ -274,7 +274,7 @@ static int read_resume_files(dev_t *ret_resume, uint64_t *ret_resume_offset) {
if (r < 0)
return log_debug_errno(r, "Error reading /sys/power/resume: %m");
r = parse_dev(resume_str, &resume);
r = parse_devnum(resume_str, &resume);
if (r < 0)
return log_debug_errno(r, "Error parsing /sys/power/resume device: %s: %m", resume_str);

View File

@ -8,6 +8,7 @@
#include "capability-util.h"
#include "chase-symlinks.h"
#include "devnum-util.h"
#include "discover-image.h"
#include "dissect-image.h"
#include "env-util.h"
@ -31,7 +32,6 @@
#include "pretty-print.h"
#include "process-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "terminal-util.h"
#include "user-util.h"
#include "verbs.h"
@ -84,7 +84,7 @@ static int is_our_mount_point(const char *p) {
if (r < 0)
return log_error_errno(r, "Failed to determine whether hierarchy '%s' contains '.systemd-sysext/dev': %m", p);
r = parse_dev(buf, &dev);
r = parse_devnum(buf, &dev);
if (r < 0)
return log_error_errno(r, "Failed to parse device major/minor stored in '.systemd-sysext/dev' file on '%s': %m", p);

View File

@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "chase-symlinks.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "env-util.h"
#include "fd-util.h"
@ -18,7 +19,6 @@
#include "macro.h"
#include "process-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "string-table.h"
#include "sysupdate-cache.h"
#include "sysupdate-instance.h"

View File

@ -244,6 +244,8 @@ tests += [
[files('test-stat-util.c')],
[files('test-devnum-util.c')],
[files('test-os-util.c')],
[files('test-libcrypt-util.c'),

124
src/test/test-devnum-util.c Normal file
View File

@ -0,0 +1,124 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/stat.h>
#include "devnum-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "tests.h"
TEST(parse_devnum) {
dev_t dev;
assert_se(parse_devnum("", &dev) == -EINVAL);
assert_se(parse_devnum("junk", &dev) == -EINVAL);
assert_se(parse_devnum("0", &dev) == -EINVAL);
assert_se(parse_devnum("5", &dev) == -EINVAL);
assert_se(parse_devnum("5:", &dev) == -EINVAL);
assert_se(parse_devnum(":5", &dev) == -EINVAL);
assert_se(parse_devnum("-1:-1", &dev) == -EINVAL);
#if SIZEOF_DEV_T < 8
assert_se(parse_devnum("4294967295:4294967295", &dev) == -EINVAL);
#endif
assert_se(parse_devnum("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11);
assert_se(parse_devnum("0:0", &dev) >= 0 && major(dev) == 0 && minor(dev) == 0);
}
TEST(device_major_minor_valid) {
/* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
assert_cc(sizeof(dev_t) == sizeof(uint64_t));
assert_se(DEVICE_MAJOR_VALID(0U));
assert_se(DEVICE_MINOR_VALID(0U));
assert_se(DEVICE_MAJOR_VALID(1U));
assert_se(DEVICE_MINOR_VALID(1U));
assert_se(!DEVICE_MAJOR_VALID(-1U));
assert_se(!DEVICE_MINOR_VALID(-1U));
assert_se(DEVICE_MAJOR_VALID(1U << 10));
assert_se(DEVICE_MINOR_VALID(1U << 10));
assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
assert_se(!DEVICE_MINOR_VALID((1U << 20)));
assert_se(!DEVICE_MAJOR_VALID(1U << 25));
assert_se(!DEVICE_MINOR_VALID(1U << 25));
assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
assert_se(DEVICE_MAJOR_VALID(major(0)));
assert_se(DEVICE_MINOR_VALID(minor(0)));
}
static void test_device_path_make_canonical_one(const char *path) {
_cleanup_free_ char *resolved = NULL, *raw = NULL;
struct stat st;
dev_t devno;
mode_t mode;
int r;
log_debug("> %s", path);
if (stat(path, &st) < 0) {
assert_se(errno == ENOENT);
log_notice("Path %s not found, skipping test", path);
return;
}
r = device_path_make_canonical(st.st_mode, st.st_rdev, &resolved);
if (r == -ENOENT) {
/* maybe /dev/char/x:y and /dev/block/x:y are missing in this test environment, because we
* run in a container or so? */
log_notice("Device %s cannot be resolved, skipping test", path);
return;
}
assert_se(r >= 0);
assert_se(path_equal(path, resolved));
assert_se(device_path_make_major_minor(st.st_mode, st.st_rdev, &raw) >= 0);
assert_se(device_path_parse_major_minor(raw, &mode, &devno) >= 0);
assert_se(st.st_rdev == devno);
assert_se((st.st_mode & S_IFMT) == (mode & S_IFMT));
}
TEST(device_path_make_canonical) {
test_device_path_make_canonical_one("/dev/null");
test_device_path_make_canonical_one("/dev/zero");
test_device_path_make_canonical_one("/dev/full");
test_device_path_make_canonical_one("/dev/random");
test_device_path_make_canonical_one("/dev/urandom");
test_device_path_make_canonical_one("/dev/tty");
if (is_device_node("/run/systemd/inaccessible/blk") > 0) {
test_device_path_make_canonical_one("/run/systemd/inaccessible/chr");
test_device_path_make_canonical_one("/run/systemd/inaccessible/blk");
}
}
static void test_devnum_format_str_one(dev_t devnum, const char *s) {
dev_t x;
assert_se(streq(FORMAT_DEVNUM(devnum), s));
assert_se(parse_devnum(s, &x) >= 0);
assert_se(x == devnum);
}
TEST(devnum_format_str) {
test_devnum_format_str_one(makedev(0, 0), "0:0");
test_devnum_format_str_one(makedev(1, 2), "1:2");
test_devnum_format_str_one(makedev(99, 100), "99:100");
test_devnum_format_str_one(makedev(4095, 1048575), "4095:1048575");
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -92,6 +92,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_TREE_RIGHT);
dump_glyph(SPECIAL_GLYPH_TREE_SPACE);
dump_glyph(SPECIAL_GLYPH_TREE_TOP);
dump_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED);
dump_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET);
dump_glyph(SPECIAL_GLYPH_BLACK_CIRCLE);
dump_glyph(SPECIAL_GLYPH_WHITE_CIRCLE);

View File

@ -812,23 +812,6 @@ TEST(parse_nice) {
assert_se(parse_nice("+20", &n) == -ERANGE);
}
TEST(parse_dev) {
dev_t dev;
assert_se(parse_dev("", &dev) == -EINVAL);
assert_se(parse_dev("junk", &dev) == -EINVAL);
assert_se(parse_dev("0", &dev) == -EINVAL);
assert_se(parse_dev("5", &dev) == -EINVAL);
assert_se(parse_dev("5:", &dev) == -EINVAL);
assert_se(parse_dev(":5", &dev) == -EINVAL);
assert_se(parse_dev("-1:-1", &dev) == -EINVAL);
#if SIZEOF_DEV_T < 8
assert_se(parse_dev("4294967295:4294967295", &dev) == -EINVAL);
#endif
assert_se(parse_dev("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11);
assert_se(parse_dev("0:0", &dev) >= 0 && major(dev) == 0 && minor(dev) == 0);
}
TEST(parse_errno) {
assert_se(parse_errno("EILSEQ") == EILSEQ);
assert_se(parse_errno("EINVAL") == EINVAL);

View File

@ -149,88 +149,6 @@ TEST(fd_is_ns) {
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 1, -EUCLEAN));
}
TEST(device_major_minor_valid) {
/* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
assert_cc(sizeof(dev_t) == sizeof(uint64_t));
assert_se(DEVICE_MAJOR_VALID(0U));
assert_se(DEVICE_MINOR_VALID(0U));
assert_se(DEVICE_MAJOR_VALID(1U));
assert_se(DEVICE_MINOR_VALID(1U));
assert_se(!DEVICE_MAJOR_VALID(-1U));
assert_se(!DEVICE_MINOR_VALID(-1U));
assert_se(DEVICE_MAJOR_VALID(1U << 10));
assert_se(DEVICE_MINOR_VALID(1U << 10));
assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
assert_se(!DEVICE_MINOR_VALID((1U << 20)));
assert_se(!DEVICE_MAJOR_VALID(1U << 25));
assert_se(!DEVICE_MINOR_VALID(1U << 25));
assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
assert_se(DEVICE_MAJOR_VALID(major(0)));
assert_se(DEVICE_MINOR_VALID(minor(0)));
}
static void test_device_path_make_canonical_one(const char *path) {
_cleanup_free_ char *resolved = NULL, *raw = NULL;
struct stat st;
dev_t devno;
mode_t mode;
int r;
log_debug("> %s", path);
if (stat(path, &st) < 0) {
assert_se(errno == ENOENT);
log_notice("Path %s not found, skipping test", path);
return;
}
r = device_path_make_canonical(st.st_mode, st.st_rdev, &resolved);
if (r == -ENOENT) {
/* maybe /dev/char/x:y and /dev/block/x:y are missing in this test environment, because we
* run in a container or so? */
log_notice("Device %s cannot be resolved, skipping test", path);
return;
}
assert_se(r >= 0);
assert_se(path_equal(path, resolved));
assert_se(device_path_make_major_minor(st.st_mode, st.st_rdev, &raw) >= 0);
assert_se(device_path_parse_major_minor(raw, &mode, &devno) >= 0);
assert_se(st.st_rdev == devno);
assert_se((st.st_mode & S_IFMT) == (mode & S_IFMT));
}
TEST(device_path_make_canonical) {
test_device_path_make_canonical_one("/dev/null");
test_device_path_make_canonical_one("/dev/zero");
test_device_path_make_canonical_one("/dev/full");
test_device_path_make_canonical_one("/dev/random");
test_device_path_make_canonical_one("/dev/urandom");
test_device_path_make_canonical_one("/dev/tty");
if (is_device_node("/run/systemd/inaccessible/blk") > 0) {
test_device_path_make_canonical_one("/run/systemd/inaccessible/chr");
test_device_path_make_canonical_one("/run/systemd/inaccessible/blk");
}
}
TEST(dir_is_empty) {
_cleanup_(rm_rf_physical_and_freep) char *empty_dir = NULL;
_cleanup_free_ char *j = NULL, *jj = NULL;

View File

@ -107,6 +107,7 @@ TEST(unit_name_replace_instance) {
static void test_unit_name_from_path_one(const char *path, const char *suffix, const char *expected, int ret) {
_cleanup_free_ char *t = NULL;
int r;
assert_se(unit_name_from_path(path, suffix, &t) == ret);
puts(strna(t));
@ -114,12 +115,31 @@ static void test_unit_name_from_path_one(const char *path, const char *suffix, c
if (t) {
_cleanup_free_ char *k = NULL;
assert_se(unit_name_to_path(t, &k) == 0);
/* We don't support converting hashed unit names back to paths */
r = unit_name_to_path(t, &k);
if (r == -ENAMETOOLONG)
return;
assert(r == 0);
puts(strna(k));
assert_se(path_equal(k, empty_to_root(path)));
}
}
TEST(unit_name_is_hashed) {
assert_se(!unit_name_is_hashed(""));
assert_se(!unit_name_is_hashed("foo@bar.service"));
assert_se(!unit_name_is_hashed("foo@.service"));
assert_se(unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9ed33c2ec55.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736D9ED33C2EC55.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!7736d9ed33c2ec55.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9gd33c2ec55.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_2103e1466b87f7f7@waldo.mount"));
assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_2103e1466b87f7f7@.mount"));
}
TEST(unit_name_from_path) {
test_unit_name_from_path_one("/waldo", ".mount", "waldo.mount", 0);
test_unit_name_from_path_one("/waldo/quuix", ".mount", "waldo-quuix.mount", 0);
@ -129,7 +149,8 @@ TEST(unit_name_from_path) {
test_unit_name_from_path_one("///", ".mount", "-.mount", 0);
test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL);
test_unit_name_from_path_one("/foo/./bar", ".mount", "foo-bar.mount", 0);
test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -ENAMETOOLONG);
test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount",
"waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9ed33c2ec55.mount", 0);
}
static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
@ -157,7 +178,6 @@ TEST(unit_name_from_path_instance) {
test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL);
test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -ENAMETOOLONG);
}
static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) {

View File

@ -26,6 +26,7 @@
#include "conf-files.h"
#include "copy.h"
#include "def.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "env-util.h"
@ -58,7 +59,6 @@
#include "set.h"
#include "sort-util.h"
#include "specifier.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@ -3084,7 +3084,7 @@ static int parse_line(
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
}
r = parse_dev(i.argument, &i.major_minor);
r = parse_devnum(i.argument, &i.major_minor);
if (r < 0) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, r, "Can't parse device file major/minor '%s'.", i.argument);

View File

@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"

View File

@ -18,6 +18,9 @@
#include "dirent-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "glyph-util.h"
#include "pager.h"
#include "sort-util.h"
#include "static-destruct.h"
#include "string-table.h"
@ -31,6 +34,7 @@ typedef enum ActionType {
ACTION_QUERY,
ACTION_ATTRIBUTE_WALK,
ACTION_DEVICE_ID_FILE,
ACTION_TREE,
} ActionType;
typedef enum QueryType {
@ -48,6 +52,9 @@ static bool arg_value = false;
static const char *arg_export_prefix = NULL;
static usec_t arg_wait_for_initialization_timeout = 0;
/* Put a limit on --tree descent level to not exhaust our stack */
#define TREE_DEPTH_MAX 64
static bool skip_attribute(const char *name) {
assert(name);
@ -171,7 +178,7 @@ static int print_device_chain(sd_device *device) {
return 0;
}
static int print_record(sd_device *device) {
static int print_record(sd_device *device, const char *prefix) {
const char *str, *val, *subsys;
dev_t devnum;
uint64_t q;
@ -179,6 +186,8 @@ static int print_record(sd_device *device) {
assert(device);
prefix = strempty(prefix);
/* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
*
* We don't show action/seqnum here because that only makes sense for records synthesized from
@ -197,52 +206,54 @@ static int print_record(sd_device *device) {
* no color for regular properties */
assert_se(sd_device_get_devpath(device, &str) >= 0);
printf("P: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_sysname(device, &str) >= 0)
printf("M: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_sysnum(device, &str) >= 0)
printf("R: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_subsystem(device, &subsys) >= 0)
printf("U: %s%s%s\n", ansi_highlight_green(), subsys, ansi_normal());
printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
if (sd_device_get_devtype(device, &str) >= 0)
printf("T: %s%s%s\n", ansi_highlight_green(), str, ansi_normal());
printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
if (sd_device_get_devnum(device, &devnum) >= 0)
printf("D: %s%c %u:%u%s\n",
printf("%sD: %s%c %u:%u%s\n",
prefix,
ansi_highlight_cyan(),
streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
ansi_normal());
if (sd_device_get_ifindex(device, &ifi) >= 0)
printf("I: %s%i%s\n", ansi_highlight_cyan(), ifi, ansi_normal());
printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
if (sd_device_get_devname(device, &str) >= 0) {
assert_se(val = path_startswith(str, "/dev/"));
printf("N: %s%s%s\n", ansi_highlight_cyan(), val, ansi_normal());
printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
if (device_get_devlink_priority(device, &i) >= 0)
printf("L: %s%i%s\n", ansi_highlight_cyan(), i, ansi_normal());
printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
FOREACH_DEVICE_DEVLINK(device, str) {
assert_se(val = path_startswith(str, "/dev/"));
printf("S: %s%s%s\n", ansi_highlight_cyan(), val, ansi_normal());
printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
}
}
if (sd_device_get_diskseq(device, &q) >= 0)
printf("Q: %s%" PRIu64 "%s\n", ansi_highlight_magenta(), q, ansi_normal());
printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
if (sd_device_get_driver(device, &str) >= 0)
printf("V: %s%s%s\n", ansi_highlight_yellow4(), str, ansi_normal());
printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
FOREACH_DEVICE_PROPERTY(device, str, val)
printf("E: %s=%s\n", str, val);
printf("%sE: %s=%s\n", prefix, str, val);
puts("");
if (isempty(prefix))
puts("");
return 0;
}
@ -284,7 +295,7 @@ static int export_devices(void) {
return log_error_errno(r, "Failed to scan devices: %m");
FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
(void) print_record(d);
(void) print_record(d, NULL);
return 0;
}
@ -305,11 +316,13 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
if ((stats.st_mode & mask) != 0)
continue;
if (S_ISDIR(stats.st_mode)) {
_cleanup_closedir_ DIR *dir2 = NULL;
_cleanup_closedir_ DIR *subdir = NULL;
dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
if (dir2)
cleanup_dir(dir2, mask, depth-1);
subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
if (!subdir)
log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
else
cleanup_dir(subdir, mask, depth-1);
(void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
} else
@ -352,11 +365,13 @@ static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if (S_ISDIR(stats.st_mode)) {
_cleanup_closedir_ DIR *dir2 = NULL;
_cleanup_closedir_ DIR *subdir = NULL;
dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
if (dir2)
cleanup_dir_after_db_cleanup(dir2, datadir);
subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
if (!subdir)
log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
else
cleanup_dir_after_db_cleanup(subdir, datadir);
(void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
} else
@ -449,7 +464,7 @@ static int query_device(QueryType query, sd_device* device) {
}
case QUERY_ALL:
return print_record(device);
return print_record(device, NULL);
default:
assert_not_reached();
@ -474,6 +489,7 @@ static int help(void) {
" -r --root Prepend dev directory to path names\n"
" -a --attribute-walk Print all key matches walking along the chain\n"
" of parent devices\n"
" -t --tree Show tree of devices\n"
" -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
" -x --export Export key/value pairs\n"
" -P --export-prefix Export the key name with a prefix\n"
@ -486,6 +502,156 @@ static int help(void) {
return 0;
}
static int draw_tree(
sd_device *parent,
sd_device *const array[], size_t n,
const char *prefix,
unsigned level);
static int output_tree_device(
sd_device *device,
const char *str,
const char *prefix,
bool more,
sd_device *const array[], size_t n,
unsigned level) {
_cleanup_free_ char *subprefix = NULL, *subsubprefix = NULL;
assert(device);
assert(str);
prefix = strempty(prefix);
printf("%s%s%s\n", prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), str);
subprefix = strjoin(prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
if (!subprefix)
return log_oom();
subsubprefix = strjoin(subprefix, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED), " ");
if (!subsubprefix)
return log_oom();
(void) print_record(device, subsubprefix);
return draw_tree(device, array, n, subprefix, level + 1);
}
static int draw_tree(
sd_device *parent,
sd_device *const array[], size_t n,
const char *prefix,
unsigned level) {
const char *parent_path;
size_t i = 0;
int r;
if (n == 0)
return 0;
assert(array);
if (parent) {
r = sd_device_get_devpath(parent, &parent_path);
if (r < 0)
return log_error_errno(r, "Failed to get sysfs path of parent device: %m");
} else
parent_path = NULL;
if (level > TREE_DEPTH_MAX) {
log_warning("Eliding tree below '%s', too deep.", strna(parent_path));
return 0;
}
while (i < n) {
sd_device *device = array[i];
const char *device_path, *str;
bool more = false;
size_t j;
r = sd_device_get_devpath(device, &device_path);
if (r < 0)
return log_error_errno(r, "Failed to get sysfs path of enumerated device: %m");
/* Scan through the subsequent devices looking children of the device we are looking at. */
for (j = i + 1; j < n; j++) {
sd_device *next = array[j];
const char *next_path;
r = sd_device_get_devpath(next, &next_path);
if (r < 0)
return log_error_errno(r, "Failed to get sysfs of child device: %m");
if (!path_startswith(next_path, device_path)) {
more = !parent_path || path_startswith(next_path, parent_path);
break;
}
}
/* Determine the string to display for this node. If we are at the top of the tree, the full
* device path so far, otherwise just the part suffixing the parent's device path. */
str = parent ? ASSERT_PTR(path_startswith(device_path, parent_path)) : device_path;
r = output_tree_device(device, str, prefix, more, array + i + 1, j - i - 1, level);
if (r < 0)
return r;
i = j;
}
return 0;
}
static int print_tree(sd_device* below) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
const char *below_path;
sd_device **array;
size_t n = 0;
int r;
if (below) {
r = sd_device_get_devpath(below, &below_path);
if (r < 0)
return log_error_errno(r, "Failed to get sysfs path of device: %m");
} else
below_path = NULL;
r = sd_device_enumerator_new(&e);
if (r < 0)
return log_error_errno(r, "Failed to allocate device enumerator: %m");
if (below) {
r = sd_device_enumerator_add_match_parent(e, below);
if (r < 0)
return log_error_errno(r, "Failed to install parent enumerator match: %m");
}
r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
r = device_enumerator_scan_devices_and_subsystems(e);
if (r < 0)
return log_error_errno(r, "Failed to scan for devices and subsystems: %m");
assert_se(array = device_enumerator_get_devices(e, &n));
if (n == 0) {
log_info("No items.");
return 0;
}
r = draw_tree(NULL, array, n, NULL, 0);
if (r < 0)
return r;
printf("\n%zu items shown.\n", n);
return 0;
}
int info_main(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **devices = NULL;
_cleanup_free_ char *name = NULL;
@ -498,6 +664,7 @@ int info_main(int argc, char *argv[], void *userdata) {
static const struct option options[] = {
{ "attribute-walk", no_argument, NULL, 'a' },
{ "tree", no_argument, NULL, 't' },
{ "cleanup-db", no_argument, NULL, 'c' },
{ "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' },
@ -518,7 +685,7 @@ int info_main(int argc, char *argv[], void *userdata) {
ActionType action = ACTION_QUERY;
QueryType query = QUERY_ALL;
while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
switch (c) {
case ARG_PROPERTY:
/* Make sure that if the empty property list was specified, we won't show any
@ -578,6 +745,9 @@ int info_main(int argc, char *argv[], void *userdata) {
case 'a':
action = ACTION_ATTRIBUTE_WALK;
break;
case 't':
action = ACTION_TREE;
break;
case 'e':
return export_devices();
case 'c':
@ -620,17 +790,23 @@ int info_main(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to build argument list: %m");
if (strv_isempty(devices))
if (action != ACTION_TREE && strv_isempty(devices))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A device name or path is required");
if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Only one device may be specified with -a/--attribute-walk");
"Only one device may be specified with -a/--attribute-walk and -t/--tree");
if (arg_export && arg_value)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"-x/--export or -P/--export-prefix cannot be used with --value");
if (strv_isempty(devices)) {
assert(action == ACTION_TREE);
pager_open(0);
return print_tree(NULL);
}
ret = 0;
STRV_FOREACH(p, devices) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
@ -666,6 +842,8 @@ int info_main(int argc, char *argv[], void *userdata) {
r = query_device(query, device);
else if (action == ACTION_ATTRIBUTE_WALK)
r = print_device_chain(device);
else if (action == ACTION_TREE)
r = print_tree(device);
else
assert_not_reached();
if (r < 0)

View File

@ -7,6 +7,7 @@
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "devnum-util.h"
#include "fd-util.h"
#include "fdset.h"
#include "main-func.h"
@ -16,7 +17,6 @@
#include "process-util.h"
#include "signal-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "strv.h"
#include "time-util.h"
#include "udevadm.h"

View File

@ -5,13 +5,13 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "chase-symlinks.h"
#include "devnum-util.h"
#include "escape.h"
#include "main-func.h"
#include "mkdir.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "volatile-util.h"

View File

@ -42,6 +42,9 @@ _host_has_feature() {(
lvm)
command -v lvm || return $?
;;
mdadm)
command -v mdadm || return $?
;;
multipath)
command -v multipath && command -v multipathd || return $?
;;
@ -64,6 +67,7 @@ test_append_files() {(
[btrfs]=install_btrfs
[iscsi]=install_iscsi
[lvm]=install_lvm
[mdadm]=install_mdadm
[multipath]=install_multipath
)
@ -444,6 +448,32 @@ EOF
rm -f "${testdisk:?}"
}
testcase_mdadm_basic() {
if ! _host_has_feature "mdadm"; then
echo "Missing mdadm tools/modules, skipping the test..."
return 77
fi
local qemu_opts=("-device ahci,id=ahci0")
local diskpath i size
for i in {0..4}; do
diskpath="${TESTDIR:?}/mdadmbasic${i}.img"
dd if=/dev/zero of="$diskpath" bs=1M count=64
qemu_opts+=(
"-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefmdadm$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
test_run_one "${1:?}" || return $?
rm -f "${TESTDIR:?}"/mdadmbasic*.img
}
# Allow overriding which tests should be run from the "outside", useful for manual
# testing (make -C test/... TESTCASES="testcase1 testcase2")
if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then

View File

@ -1055,6 +1055,29 @@ install_iscsi() {
fi
}
install_mdadm() {
local unit
local mdadm_units=(
system/mdadm-grow-continue@.service
system/mdadm-last-resort@.service
system/mdadm-last-resort@.timer
system/mdmon@.service
system/mdmonitor-oneshot.service
system/mdmonitor-oneshot.timer
system/mdmonitor.service
system-shutdown/mdadm.shutdown
)
image_install mdadm mdmon
inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules
# Fedora/CentOS/RHEL ships this rule file
[[ -f /lib/udev/rules.d/65-md-incremental.rules ]] && inst_rules 65-md-incremental.rules
for unit in "${mdadm_units[@]}"; do
image_install "${ROOTLIBDIR:?}/$unit"
done
}
install_compiled_systemd() {
dinfo "Install compiled systemd"

View File

@ -33,6 +33,10 @@ BEGIN {
}
}
# Relax sd-device's sysfs verification, since we want to provide a fake sysfs
# here that actually is a tmpfs.
$ENV{"SYSTEMD_DEVICE_VERIFY_SYSFS"}="0";
my $udev_bin = "./test-udev";
my $valgrind = 0;
my $gdb = 0;

View File

@ -8,6 +8,25 @@ systemd-analyze log-target journal
NUM_DIRS=20
# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
TS="$(date '+%H:%M:%S')"
mkdir -p "$LONGPATH"
mount -t tmpfs tmpfs "$LONGPATH"
systemctl daemon-reload
# check that unit is active(mounted)
systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
# check that relevant part of journal doesn't contain any errors related to unit
[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
# check that we can successfully stop the mount unit
systemctl stop "$LONGPATH"
rm -rf "$LONGPATH"
# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
for ((i = 0; i < NUM_DIRS; i++)); do

View File

@ -515,6 +515,7 @@ testcase_long_sysfs_path() {
echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
systemctl daemon-reload
mount "$mpoint"
systemctl status "$mpoint"
test -e "$mpoint/test"
umount "$mpoint"
@ -525,15 +526,48 @@ testcase_long_sysfs_path() {
udevadm settle
logfile="$(mktemp)"
journalctl -b -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name"
[[ "$(journalctl -b -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]]
# Make sure we don't unnecessarily spam the log
journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service | tee "$logfile"
{ journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile"
[[ "$(wc -l <"$logfile")" -lt 10 ]]
: >/etc/fstab
rm -fr "${logfile:?}" "${mpoint:?}"
}
testcase_mdadm_basic() {
local raid_name uuid
local expected_symlinks=()
local devices=(
/dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4}
)
ls -l "${devices[@]}"
echo "Mirror raid"
raid_name="mdmirror"
uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001"
expected_symlinks=(
"/dev/md/$raid_name"
"/dev/disk/by-id/md-name-H:mdmirror"
"/dev/disk/by-id/md-uuid-$uuid"
"/dev/disk/by-label/mdadm_mirror" # ext4 partition
)
# Create a simple RAID 1 with an ext4 filesystem
echo y | mdadm --create "${expected_symlinks[0]}" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
udevadm wait --settle --timeout=30 "${expected_symlinks[0]}"
mkfs.ext4 -L mdadm_mirror "/dev/md/$raid_name"
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
# Disassemble the array
mdadm -v --stop "${expected_symlinks[0]}"
udevadm settle
helper_check_device_symlinks
# Reassemble it and check if all requires symlinks exist
mdadm --assemble "${expected_symlinks[0]}" --name "$raid_name" -v
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
helper_check_device_symlinks
}
: >/failed
udevadm settle