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