]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
910fd145 | 2 | |
dccca82b | 3 | #include <errno.h> |
910fd145 | 4 | #include <fcntl.h> |
15b1248a | 5 | #include <unistd.h> |
910fd145 LP |
6 | |
7 | #include "fd-util.h" | |
0f44b766 | 8 | #include "fs-util.h" |
910fd145 LP |
9 | #include "hexdecoct.h" |
10 | #include "id128-util.h" | |
11 | #include "io-util.h" | |
12 | #include "stdio-util.h" | |
80851148 | 13 | #include "string-util.h" |
bf819d3a | 14 | #include "sync-util.h" |
910fd145 | 15 | |
7c9de5d8 ZJS |
16 | int id128_from_string_nonzero(const char *s, sd_id128_t *ret) { |
17 | sd_id128_t t; | |
18 | int r; | |
19 | ||
20 | assert(ret); | |
21 | ||
22 | r = sd_id128_from_string(ASSERT_PTR(s), &t); | |
23 | if (r < 0) | |
24 | return r; | |
25 | ||
26 | if (sd_id128_is_null(t)) | |
27 | return -ENXIO; | |
28 | ||
29 | *ret = t; | |
30 | return 0; | |
31 | } | |
32 | ||
910fd145 | 33 | bool id128_is_valid(const char *s) { |
28bf2de2 | 34 | size_t l; |
910fd145 LP |
35 | |
36 | assert(s); | |
37 | ||
38 | l = strlen(s); | |
910fd145 | 39 | |
28bf2de2 | 40 | if (l == SD_ID128_STRING_MAX - 1) |
da890466 | 41 | /* Plain formatted 128-bit hex string */ |
28bf2de2 | 42 | return in_charset(s, HEXDIGITS); |
910fd145 | 43 | |
28bf2de2 | 44 | if (l == SD_ID128_UUID_STRING_MAX - 1) { |
910fd145 | 45 | /* Formatted UUID */ |
28bf2de2 | 46 | for (size_t i = 0; i < l; i++) { |
910fd145 LP |
47 | char c = s[i]; |
48 | ||
945c2931 | 49 | if (IN_SET(i, 8, 13, 18, 23)) { |
910fd145 LP |
50 | if (c != '-') |
51 | return false; | |
28bf2de2 YW |
52 | } else if (!ascii_ishex(c)) |
53 | return false; | |
910fd145 | 54 | } |
28bf2de2 YW |
55 | return true; |
56 | } | |
910fd145 | 57 | |
28bf2de2 | 58 | return false; |
910fd145 LP |
59 | } |
60 | ||
762b78de | 61 | int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { |
28bf2de2 | 62 | char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ |
52673ef8 | 63 | sd_id128_t id; |
910fd145 | 64 | ssize_t l; |
e8a66254 | 65 | int r; |
910fd145 LP |
66 | |
67 | assert(fd >= 0); | |
910fd145 | 68 | |
da890466 | 69 | /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both |
65548c58 LP |
70 | * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they |
71 | * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you | |
057bf780 YW |
72 | * accept". |
73 | * | |
74 | * This returns the following: | |
75 | * -ENOMEDIUM: an empty string, | |
76 | * -ENOPKG: "uninitialized" or "uninitialized\n", | |
e8a66254 | 77 | * -EUCLEAN: other invalid strings. */ |
910fd145 | 78 | |
65548c58 | 79 | l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ |
910fd145 LP |
80 | if (l < 0) |
81 | return (int) l; | |
82 | if (l == 0) /* empty? */ | |
83 | return -ENOMEDIUM; | |
84 | ||
65548c58 | 85 | switch (l) { |
910fd145 | 86 | |
057bf780 YW |
87 | case STRLEN("uninitialized"): |
88 | case STRLEN("uninitialized\n"): | |
89 | return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL; | |
80851148 | 90 | |
28bf2de2 YW |
91 | case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */ |
92 | if (buffer[SD_ID128_STRING_MAX-1] != '\n') | |
e8a66254 | 93 | return -EUCLEAN; |
910fd145 | 94 | |
4831981d | 95 | _fallthrough_; |
28bf2de2 | 96 | case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */ |
057bf780 | 97 | if (!FLAGS_SET(f, ID128_FORMAT_PLAIN)) |
e8a66254 | 98 | return -EUCLEAN; |
65548c58 | 99 | |
28bf2de2 | 100 | buffer[SD_ID128_STRING_MAX-1] = 0; |
65548c58 | 101 | break; |
910fd145 | 102 | |
28bf2de2 YW |
103 | case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */ |
104 | if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n') | |
e8a66254 | 105 | return -EUCLEAN; |
910fd145 | 106 | |
4831981d | 107 | _fallthrough_; |
28bf2de2 | 108 | case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */ |
057bf780 | 109 | if (!FLAGS_SET(f, ID128_FORMAT_UUID)) |
e8a66254 | 110 | return -EUCLEAN; |
910fd145 | 111 | |
28bf2de2 | 112 | buffer[SD_ID128_UUID_STRING_MAX-1] = 0; |
65548c58 LP |
113 | break; |
114 | ||
115 | default: | |
e8a66254 | 116 | return -EUCLEAN; |
65548c58 | 117 | } |
910fd145 | 118 | |
52673ef8 YW |
119 | r = sd_id128_from_string(buffer, &id); |
120 | if (r == -EINVAL) | |
121 | return -EUCLEAN; | |
122 | if (r < 0) | |
123 | return r; | |
124 | ||
125 | if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) | |
126 | return -ENOMEDIUM; | |
127 | ||
128 | if (ret) | |
129 | *ret = id; | |
130 | return 0; | |
910fd145 LP |
131 | } |
132 | ||
0f44b766 | 133 | int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) { |
254d1313 | 134 | _cleanup_close_ int fd = -EBADF; |
910fd145 | 135 | |
0f44b766 | 136 | assert(dir_fd >= 0 || dir_fd == AT_FDCWD); |
169d91b7 YW |
137 | assert(path); |
138 | ||
420d2e31 | 139 | fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0); |
910fd145 | 140 | if (fd < 0) |
0f44b766 | 141 | return fd; |
910fd145 LP |
142 | |
143 | return id128_read_fd(fd, f, ret); | |
144 | } | |
145 | ||
762b78de | 146 | int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) { |
28bf2de2 | 147 | char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ |
910fd145 | 148 | size_t sz; |
15b1248a | 149 | int r; |
910fd145 LP |
150 | |
151 | assert(fd >= 0); | |
057bf780 | 152 | assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); |
910fd145 | 153 | |
52673ef8 YW |
154 | if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id)) |
155 | return -ENOMEDIUM; | |
156 | ||
057bf780 | 157 | if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { |
b7416360 | 158 | assert_se(sd_id128_to_string(id, buffer)); |
b7416360 | 159 | sz = SD_ID128_STRING_MAX; |
910fd145 | 160 | } else { |
b7416360 | 161 | assert_se(sd_id128_to_uuid_string(id, buffer)); |
b7416360 | 162 | sz = SD_ID128_UUID_STRING_MAX; |
910fd145 LP |
163 | } |
164 | ||
28bf2de2 | 165 | buffer[sz - 1] = '\n'; |
e22c60a9 | 166 | r = loop_write(fd, buffer, sz); |
15b1248a LP |
167 | if (r < 0) |
168 | return r; | |
169 | ||
b40c8ebd | 170 | if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) { |
bf819d3a | 171 | r = fsync_full(fd); |
8ac2f74f LP |
172 | if (r < 0) |
173 | return r; | |
15b1248a LP |
174 | } |
175 | ||
8ac2f74f | 176 | return 0; |
910fd145 LP |
177 | } |
178 | ||
c227c46b | 179 | int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) { |
254d1313 | 180 | _cleanup_close_ int fd = -EBADF; |
910fd145 | 181 | |
c227c46b | 182 | assert(dir_fd >= 0 || dir_fd == AT_FDCWD); |
169d91b7 YW |
183 | assert(path); |
184 | ||
420d2e31 | 185 | fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444); |
910fd145 | 186 | if (fd < 0) |
c227c46b | 187 | return fd; |
910fd145 | 188 | |
b40c8ebd | 189 | return id128_write_fd(fd, f, id); |
910fd145 | 190 | } |
4b58153d | 191 | |
7a08d314 YW |
192 | void id128_hash_func(const sd_id128_t *p, struct siphash *state) { |
193 | siphash24_compress(p, sizeof(sd_id128_t), state); | |
4b58153d LP |
194 | } |
195 | ||
7a08d314 | 196 | int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) { |
4b58153d LP |
197 | return memcmp(a, b, 16); |
198 | } | |
199 | ||
1293a168 LP |
200 | sd_id128_t id128_make_v4_uuid(sd_id128_t id) { |
201 | /* Stolen from generate_random_uuid() of drivers/char/random.c | |
202 | * in the kernel sources */ | |
203 | ||
204 | /* Set UUID version to 4 --- truly random generation */ | |
205 | id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; | |
206 | ||
207 | /* Set the UUID variant to DCE */ | |
208 | id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; | |
209 | ||
210 | return id; | |
211 | } | |
212 | ||
7a08d314 | 213 | DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); |
3e61656f | 214 | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free); |
b4be4ff8 LP |
215 | |
216 | int id128_get_product(sd_id128_t *ret) { | |
217 | sd_id128_t uuid; | |
218 | int r; | |
219 | ||
220 | assert(ret); | |
221 | ||
222 | /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is | |
223 | * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ | |
224 | ||
17f9d6d8 | 225 | r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid); |
b4be4ff8 | 226 | if (r == -ENOENT) |
17f9d6d8 | 227 | r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid); |
b4be4ff8 LP |
228 | if (r < 0) |
229 | return r; | |
230 | ||
231 | if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) | |
232 | return -EADDRNOTAVAIL; /* Recognizable error */ | |
233 | ||
234 | *ret = uuid; | |
235 | return 0; | |
236 | } |