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