]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-id128/id128-util.c
sd-id128: introduce ID128_REFUSE_NULL flag
[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"
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 16bool 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 44int 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 116int 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 129int 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 162int 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
175void 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 179int 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
183sd_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 196DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
3e61656f 197DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
b4be4ff8
LP
198
199int 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}