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