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