]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-id128/id128-util.c
Merge pull request #20937 from poettering/sync-split
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / id128-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <unistd.h>
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"
12 #include "string-util.h"
13 #include "sync-util.h"
14
15 char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
16 unsigned n, k = 0;
17
18 assert(s);
19
20 /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
21
22 for (n = 0; n < 16; n++) {
23
24 if (IN_SET(n, 4, 6, 8, 10))
25 s[k++] = '-';
26
27 s[k++] = hexchar(id.bytes[n] >> 4);
28 s[k++] = hexchar(id.bytes[n] & 0xF);
29 }
30
31 assert(k == 36);
32
33 s[k] = 0;
34
35 return s;
36 }
37
38 bool id128_is_valid(const char *s) {
39 size_t i, l;
40
41 assert(s);
42
43 l = strlen(s);
44 if (l == 32) {
45
46 /* Plain formatted 128bit hex string */
47
48 for (i = 0; i < l; i++) {
49 char c = s[i];
50
51 if (!(c >= '0' && c <= '9') &&
52 !(c >= 'a' && c <= 'z') &&
53 !(c >= 'A' && c <= 'Z'))
54 return false;
55 }
56
57 } else if (l == 36) {
58
59 /* Formatted UUID */
60
61 for (i = 0; i < l; i++) {
62 char c = s[i];
63
64 if (IN_SET(i, 8, 13, 18, 23)) {
65 if (c != '-')
66 return false;
67 } else {
68 if (!(c >= '0' && c <= '9') &&
69 !(c >= 'a' && c <= 'z') &&
70 !(c >= 'A' && c <= 'Z'))
71 return false;
72 }
73 }
74
75 } else
76 return false;
77
78 return true;
79 }
80
81 int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
82 char buffer[36 + 2];
83 ssize_t l;
84
85 assert(fd >= 0);
86 assert(f < _ID128_FORMAT_MAX);
87
88 /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
89 * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
90 * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
91 * accept". */
92
93 l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
94 if (l < 0)
95 return (int) l;
96 if (l == 0) /* empty? */
97 return -ENOMEDIUM;
98
99 switch (l) {
100
101 case 13:
102 case 14:
103 /* Treat an "uninitialized" id file like an empty one */
104 return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
105
106 case 33: /* plain UUID with trailing newline */
107 if (buffer[32] != '\n')
108 return -EINVAL;
109
110 _fallthrough_;
111 case 32: /* plain UUID without trailing newline */
112 if (f == ID128_UUID)
113 return -EINVAL;
114
115 buffer[32] = 0;
116 break;
117
118 case 37: /* RFC UUID with trailing newline */
119 if (buffer[36] != '\n')
120 return -EINVAL;
121
122 _fallthrough_;
123 case 36: /* RFC UUID without trailing newline */
124 if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
125 return -EINVAL;
126
127 buffer[36] = 0;
128 break;
129
130 default:
131 return -EINVAL;
132 }
133
134 return sd_id128_from_string(buffer, ret);
135 }
136
137 int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
138 _cleanup_close_ int fd = -1;
139
140 fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
141 if (fd < 0)
142 return -errno;
143
144 return id128_read_fd(fd, f, ret);
145 }
146
147 int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
148 char buffer[36 + 2];
149 size_t sz;
150 int r;
151
152 assert(fd >= 0);
153 assert(f < _ID128_FORMAT_MAX);
154
155 if (f != ID128_UUID) {
156 sd_id128_to_string(id, buffer);
157 buffer[32] = '\n';
158 sz = 33;
159 } else {
160 id128_to_uuid_string(id, buffer);
161 buffer[36] = '\n';
162 sz = 37;
163 }
164
165 r = loop_write(fd, buffer, sz, false);
166 if (r < 0)
167 return r;
168
169 if (do_sync) {
170 r = fsync_full(fd);
171 if (r < 0)
172 return r;
173 }
174
175 return 0;
176 }
177
178 int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
179 _cleanup_close_ int fd = -1;
180
181 fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
182 if (fd < 0)
183 return -errno;
184
185 return id128_write_fd(fd, f, id, do_sync);
186 }
187
188 void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
189 siphash24_compress(p, sizeof(sd_id128_t), state);
190 }
191
192 int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
193 return memcmp(a, b, 16);
194 }
195
196 sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
197 /* Stolen from generate_random_uuid() of drivers/char/random.c
198 * in the kernel sources */
199
200 /* Set UUID version to 4 --- truly random generation */
201 id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
202
203 /* Set the UUID variant to DCE */
204 id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
205
206 return id;
207 }
208
209 DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
210
211 int id128_get_product(sd_id128_t *ret) {
212 sd_id128_t uuid;
213 int r;
214
215 assert(ret);
216
217 /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
218 * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
219
220 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
221 if (r == -ENOENT)
222 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
223 if (r < 0)
224 return r;
225
226 if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
227 return -EADDRNOTAVAIL; /* Recognizable error */
228
229 *ret = uuid;
230 return 0;
231 }