Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <fcntl.h>
5 : #include <unistd.h>
6 :
7 : #include "fd-util.h"
8 : #include "fs-util.h"
9 : #include "hexdecoct.h"
10 : #include "id128-util.h"
11 : #include "io-util.h"
12 : #include "stdio-util.h"
13 :
14 3 : char *id128_to_uuid_string(sd_id128_t id, char s[37]) {
15 3 : unsigned n, k = 0;
16 :
17 3 : assert(s);
18 :
19 : /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
20 :
21 51 : for (n = 0; n < 16; n++) {
22 :
23 48 : if (IN_SET(n, 4, 6, 8, 10))
24 12 : s[k++] = '-';
25 :
26 48 : s[k++] = hexchar(id.bytes[n] >> 4);
27 48 : s[k++] = hexchar(id.bytes[n] & 0xF);
28 : }
29 :
30 3 : assert(k == 36);
31 :
32 3 : s[k] = 0;
33 :
34 3 : return s;
35 : }
36 :
37 1771 : bool id128_is_valid(const char *s) {
38 : size_t i, l;
39 :
40 1771 : assert(s);
41 :
42 1771 : l = strlen(s);
43 1771 : if (l == 32) {
44 :
45 : /* Plain formatted 128bit hex string */
46 :
47 44220 : for (i = 0; i < l; i++) {
48 42880 : char c = s[i];
49 :
50 42880 : if (!(c >= '0' && c <= '9') &&
51 14941 : !(c >= 'a' && c <= 'z') &&
52 0 : !(c >= 'A' && c <= 'Z'))
53 0 : return false;
54 : }
55 :
56 431 : } else if (l == 36) {
57 :
58 : /* Formatted UUID */
59 :
60 37 : for (i = 0; i < l; i++) {
61 36 : char c = s[i];
62 :
63 36 : if (IN_SET(i, 8, 13, 18, 23)) {
64 4 : if (c != '-')
65 0 : return false;
66 : } else {
67 32 : if (!(c >= '0' && c <= '9') &&
68 6 : !(c >= 'a' && c <= 'z') &&
69 0 : !(c >= 'A' && c <= 'Z'))
70 0 : return false;
71 : }
72 : }
73 :
74 : } else
75 430 : return false;
76 :
77 1341 : return true;
78 : }
79 :
80 51 : int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
81 : char buffer[36 + 2];
82 : ssize_t l;
83 :
84 51 : assert(fd >= 0);
85 51 : assert(f < _ID128_FORMAT_MAX);
86 :
87 : /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
88 : * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
89 : * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
90 : * accept". */
91 :
92 51 : l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
93 51 : if (l < 0)
94 0 : return (int) l;
95 51 : if (l == 0) /* empty? */
96 0 : return -ENOMEDIUM;
97 :
98 51 : switch (l) {
99 :
100 22 : case 33: /* plain UUID with trailing newline */
101 22 : if (buffer[32] != '\n')
102 0 : return -EINVAL;
103 :
104 : _fallthrough_;
105 : case 32: /* plain UUID without trailing newline */
106 24 : if (f == ID128_UUID)
107 2 : return -EINVAL;
108 :
109 22 : buffer[32] = 0;
110 22 : break;
111 :
112 25 : case 37: /* RFC UUID with trailing newline */
113 25 : if (buffer[36] != '\n')
114 0 : return -EINVAL;
115 :
116 : _fallthrough_;
117 : case 36: /* RFC UUID without trailing newline */
118 27 : if (f == ID128_PLAIN)
119 2 : return -EINVAL;
120 :
121 25 : buffer[36] = 0;
122 25 : break;
123 :
124 0 : default:
125 0 : return -EINVAL;
126 : }
127 :
128 47 : return sd_id128_from_string(buffer, ret);
129 : }
130 :
131 40 : int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
132 40 : _cleanup_close_ int fd = -1;
133 :
134 40 : fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
135 40 : if (fd < 0)
136 0 : return -errno;
137 :
138 40 : return id128_read_fd(fd, f, ret);
139 : }
140 :
141 2 : int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
142 : char buffer[36 + 2];
143 : size_t sz;
144 : int r;
145 :
146 2 : assert(fd >= 0);
147 2 : assert(f < _ID128_FORMAT_MAX);
148 :
149 2 : if (f != ID128_UUID) {
150 1 : sd_id128_to_string(id, buffer);
151 1 : buffer[32] = '\n';
152 1 : sz = 33;
153 : } else {
154 1 : id128_to_uuid_string(id, buffer);
155 1 : buffer[36] = '\n';
156 1 : sz = 37;
157 : }
158 :
159 2 : r = loop_write(fd, buffer, sz, false);
160 2 : if (r < 0)
161 0 : return r;
162 :
163 2 : if (do_sync) {
164 0 : if (fsync(fd) < 0)
165 0 : return -errno;
166 :
167 0 : r = fsync_directory_of_file(fd);
168 0 : if (r < 0)
169 0 : return r;
170 : }
171 :
172 2 : return 0;
173 : }
174 :
175 0 : int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
176 0 : _cleanup_close_ int fd = -1;
177 :
178 0 : fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
179 0 : if (fd < 0)
180 0 : return -errno;
181 :
182 0 : return id128_write_fd(fd, f, id, do_sync);
183 : }
184 :
185 5100 : void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
186 5100 : siphash24_compress(p, sizeof(sd_id128_t), state);
187 5100 : }
188 :
189 2553 : int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
190 2553 : return memcmp(a, b, 16);
191 : }
192 :
193 : DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
|