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