]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-id128/id128-util.c
Merge pull request #18863 from keszybz/cmdline-escaping
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / id128-util.c
CommitLineData
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"
8ac2f74f 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"
910fd145 14
b5ea030d 15char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
910fd145
LP
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
38bool 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
945c2931 64 if (IN_SET(i, 8, 13, 18, 23)) {
910fd145
LP
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
81int 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
65548c58
LP
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". */
910fd145 92
65548c58 93 l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
910fd145
LP
94 if (l < 0)
95 return (int) l;
96 if (l == 0) /* empty? */
97 return -ENOMEDIUM;
98
65548c58 99 switch (l) {
910fd145 100
80851148
HS
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
65548c58 106 case 33: /* plain UUID with trailing newline */
910fd145
LP
107 if (buffer[32] != '\n')
108 return -EINVAL;
109
4831981d 110 _fallthrough_;
65548c58
LP
111 case 32: /* plain UUID without trailing newline */
112 if (f == ID128_UUID)
113 return -EINVAL;
114
910fd145 115 buffer[32] = 0;
65548c58 116 break;
910fd145 117
65548c58
LP
118 case 37: /* RFC UUID with trailing newline */
119 if (buffer[36] != '\n')
910fd145
LP
120 return -EINVAL;
121
4831981d 122 _fallthrough_;
65548c58 123 case 36: /* RFC UUID without trailing newline */
80851148 124 if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
910fd145
LP
125 return -EINVAL;
126
127 buffer[36] = 0;
65548c58
LP
128 break;
129
130 default:
910fd145 131 return -EINVAL;
65548c58 132 }
910fd145
LP
133
134 return sd_id128_from_string(buffer, ret);
135}
136
137int 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
15b1248a 147int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
910fd145
LP
148 char buffer[36 + 2];
149 size_t sz;
15b1248a 150 int r;
910fd145
LP
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
15b1248a
LP
165 r = loop_write(fd, buffer, sz, false);
166 if (r < 0)
167 return r;
168
169 if (do_sync) {
170 if (fsync(fd) < 0)
171 return -errno;
8ac2f74f
LP
172
173 r = fsync_directory_of_file(fd);
174 if (r < 0)
175 return r;
15b1248a
LP
176 }
177
8ac2f74f 178 return 0;
910fd145
LP
179}
180
15b1248a 181int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
910fd145
LP
182 _cleanup_close_ int fd = -1;
183
da2d1421 184 fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
910fd145
LP
185 if (fd < 0)
186 return -errno;
187
15b1248a 188 return id128_write_fd(fd, f, id, do_sync);
910fd145 189}
4b58153d 190
7a08d314
YW
191void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
192 siphash24_compress(p, sizeof(sd_id128_t), state);
4b58153d
LP
193}
194
7a08d314 195int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
4b58153d
LP
196 return memcmp(a, b, 16);
197}
198
1293a168
LP
199sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
200 /* Stolen from generate_random_uuid() of drivers/char/random.c
201 * in the kernel sources */
202
203 /* Set UUID version to 4 --- truly random generation */
204 id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
205
206 /* Set the UUID variant to DCE */
207 id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
208
209 return id;
210}
211
7a08d314 212DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
b4be4ff8
LP
213
214int id128_get_product(sd_id128_t *ret) {
215 sd_id128_t uuid;
216 int r;
217
218 assert(ret);
219
220 /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
221 * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
222
223 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
224 if (r == -ENOENT)
225 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
226 if (r < 0)
227 return r;
228
229 if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
230 return -EADDRNOTAVAIL; /* Recognizable error */
231
232 *ret = uuid;
233 return 0;
234}