chiark / gitweb /
Prep v238: Uncomment now needed headers and unmask now needed functions in src/libelo...
[elogind.git] / src / libelogind / sd-id128 / id128-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2016 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include "fd-util.h"
26 #include "fs-util.h"
27 #include "hexdecoct.h"
28 #include "id128-util.h"
29 #include "io-util.h"
30 #include "stdio-util.h"
31
32 char *id128_to_uuid_string(sd_id128_t id, char s[37]) {
33         unsigned n, k = 0;
34
35         assert(s);
36
37         /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
38
39         for (n = 0; n < 16; n++) {
40
41                 if (IN_SET(n, 4, 6, 8, 10))
42                         s[k++] = '-';
43
44                 s[k++] = hexchar(id.bytes[n] >> 4);
45                 s[k++] = hexchar(id.bytes[n] & 0xF);
46         }
47
48         assert(k == 36);
49
50         s[k] = 0;
51
52         return s;
53 }
54
55 bool id128_is_valid(const char *s) {
56         size_t i, l;
57
58         assert(s);
59
60         l = strlen(s);
61         if (l == 32) {
62
63                 /* Plain formatted 128bit hex string */
64
65                 for (i = 0; i < l; i++) {
66                         char c = s[i];
67
68                         if (!(c >= '0' && c <= '9') &&
69                             !(c >= 'a' && c <= 'z') &&
70                             !(c >= 'A' && c <= 'Z'))
71                                 return false;
72                 }
73
74         } else if (l == 36) {
75
76                 /* Formatted UUID */
77
78                 for (i = 0; i < l; i++) {
79                         char c = s[i];
80
81                         if (IN_SET(i, 8, 13, 18, 23)) {
82                                 if (c != '-')
83                                         return false;
84                         } else {
85                                 if (!(c >= '0' && c <= '9') &&
86                                     !(c >= 'a' && c <= 'z') &&
87                                     !(c >= 'A' && c <= 'Z'))
88                                         return false;
89                         }
90                 }
91
92         } else
93                 return false;
94
95         return true;
96 }
97
98 int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
99         char buffer[36 + 2];
100         ssize_t l;
101
102         assert(fd >= 0);
103         assert(f < _ID128_FORMAT_MAX);
104
105         /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
106          * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
107          * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
108          * accept". */
109
110         l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
111         if (l < 0)
112                 return (int) l;
113         if (l == 0) /* empty? */
114                 return -ENOMEDIUM;
115
116         switch (l) {
117
118         case 33: /* plain UUID with trailing newline */
119                 if (buffer[32] != '\n')
120                         return -EINVAL;
121
122                 _fallthrough_;
123         case 32: /* plain UUID without trailing newline */
124                 if (f == ID128_UUID)
125                         return -EINVAL;
126
127                 buffer[32] = 0;
128                 break;
129
130         case 37: /* RFC UUID with trailing newline */
131                 if (buffer[36] != '\n')
132                         return -EINVAL;
133
134                 _fallthrough_;
135         case 36: /* RFC UUID without trailing newline */
136                 if (f == ID128_PLAIN)
137                         return -EINVAL;
138
139                 buffer[36] = 0;
140                 break;
141
142         default:
143                 return -EINVAL;
144         }
145
146         return sd_id128_from_string(buffer, ret);
147 }
148
149 int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
150         _cleanup_close_ int fd = -1;
151
152         fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
153         if (fd < 0)
154                 return -errno;
155
156         return id128_read_fd(fd, f, ret);
157 }
158
159 int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
160         char buffer[36 + 2];
161         size_t sz;
162         int r;
163
164         assert(fd >= 0);
165         assert(f < _ID128_FORMAT_MAX);
166
167         if (f != ID128_UUID) {
168                 sd_id128_to_string(id, buffer);
169                 buffer[32] = '\n';
170                 sz = 33;
171         } else {
172                 id128_to_uuid_string(id, buffer);
173                 buffer[36] = '\n';
174                 sz = 37;
175         }
176
177         r = loop_write(fd, buffer, sz, false);
178         if (r < 0)
179                 return r;
180
181         if (do_sync) {
182                 if (fsync(fd) < 0)
183                         return -errno;
184
185                 r = fsync_directory_of_file(fd);
186                 if (r < 0)
187                         return r;
188         }
189
190         return 0;
191 }
192
193 #if 0 /// UNNEEDED by elogind
194 int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
195         _cleanup_close_ int fd = -1;
196
197         fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
198         if (fd < 0)
199                 return -errno;
200
201         return id128_write_fd(fd, f, id, do_sync);
202 }
203
204 void id128_hash_func(const void *p, struct siphash *state) {
205         siphash24_compress(p, 16, state);
206 }
207
208 int id128_compare_func(const void *a, const void *b) {
209         return memcmp(a, b, 16);
210 }
211
212 const struct hash_ops id128_hash_ops = {
213         .hash = id128_hash_func,
214         .compare = id128_compare_func,
215 };
216 #endif // 0