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