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