]>
Commit | Line | Data |
---|---|---|
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 | ||
12 | int 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 | ||
42 | int 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 |
60 | int 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 |
78 | int 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 | ||
98 | int 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 | } |