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