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