]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/devnum-util.c
Merge pull request #28301 from berrange/cvm-lockdown
[thirdparty/systemd.git] / src / basic / devnum-util.c
CommitLineData
7176f06c
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <string.h>
4#include <sys/stat.h>
5
f461a28d 6#include "chase.h"
7176f06c
LP
7#include "devnum-util.h"
8#include "parse-util.h"
9#include "path-util.h"
10#include "string-util.h"
11
12int parse_devnum(const char *s, dev_t *ret) {
13 const char *major;
14 unsigned x, y;
15 size_t n;
16 int r;
17
18 n = strspn(s, DIGITS);
19 if (n == 0)
20 return -EINVAL;
02b9047e
LP
21 if (n > DECIMAL_STR_MAX(dev_t))
22 return -EINVAL;
7176f06c
LP
23 if (s[n] != ':')
24 return -EINVAL;
25
26 major = strndupa_safe(s, n);
27 r = safe_atou(major, &x);
28 if (r < 0)
29 return r;
30
31 r = safe_atou(s + n + 1, &y);
32 if (r < 0)
33 return r;
34
35 if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
36 return -ERANGE;
37
38 *ret = makedev(x, y);
39 return 0;
40}
41
42int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
43 const char *t;
44
45 /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
46
47 if (S_ISCHR(mode))
48 t = "char";
49 else if (S_ISBLK(mode))
50 t = "block";
51 else
52 return -ENODEV;
53
ec61371f 54 if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
7176f06c
LP
55 return -ENOMEM;
56
57 return 0;
58}
59
4492b5d8
YW
60int device_path_make_inaccessible(mode_t mode, char **ret) {
61 char *s;
62
63 assert(ret);
64
65 if (S_ISCHR(mode))
66 s = strdup("/run/systemd/inaccessible/chr");
67 else if (S_ISBLK(mode))
68 s = strdup("/run/systemd/inaccessible/blk");
69 else
70 return -ENODEV;
71 if (!s)
72 return -ENOMEM;
73
74 *ret = s;
75 return 0;
76}
77
7176f06c
LP
78int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
79 _cleanup_free_ char *p = NULL;
80 int r;
81
82 /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
83
84 assert(ret);
85
d80e2a1e 86 if (devnum_is_zero(devnum))
7176f06c
LP
87 /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
88 * /dev/block/ and /dev/char/, hence we handle them specially here. */
4492b5d8 89 return device_path_make_inaccessible(mode, ret);
7176f06c
LP
90
91 r = device_path_make_major_minor(mode, devnum, &p);
92 if (r < 0)
93 return r;
94
f461a28d 95 return chase(p, NULL, 0, ret, NULL);
7176f06c
LP
96}
97
98int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) {
99 mode_t mode;
100 dev_t devnum;
101 int r;
102
103 /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
104 * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
105 * path cannot be parsed like this. */
106
107 if (path_equal(path, "/run/systemd/inaccessible/chr")) {
108 mode = S_IFCHR;
109 devnum = makedev(0, 0);
110 } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
111 mode = S_IFBLK;
112 devnum = makedev(0, 0);
113 } else {
114 const char *w;
115
116 w = path_startswith(path, "/dev/block/");
117 if (w)
118 mode = S_IFBLK;
119 else {
120 w = path_startswith(path, "/dev/char/");
121 if (!w)
122 return -ENODEV;
123
124 mode = S_IFCHR;
125 }
126
127 r = parse_devnum(w, &devnum);
128 if (r < 0)
129 return r;
130 }
131
132 if (ret_mode)
133 *ret_mode = mode;
134 if (ret_devnum)
135 *ret_devnum = devnum;
136
137 return 0;
138}