/src/cryptsetup/lib/utils_io.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * utils - miscellaneous I/O utilities for cryptsetup |
3 | | * |
4 | | * Copyright (C) 2004 Jana Saout <jana@saout.de> |
5 | | * Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org> |
6 | | * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved. |
7 | | * Copyright (C) 2009-2024 Milan Broz |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU General Public License |
11 | | * as published by the Free Software Foundation; either version 2 |
12 | | * of the License, or (at your option) any later version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU General Public License |
20 | | * along with this program; if not, write to the Free Software |
21 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
22 | | */ |
23 | | |
24 | | #include <errno.h> |
25 | | #include <limits.h> |
26 | | #include <string.h> |
27 | | #include <stdlib.h> |
28 | | #include <stdint.h> |
29 | | #include <unistd.h> |
30 | | |
31 | | #include "utils_io.h" |
32 | | |
33 | | /* coverity[ -taint_source : arg-1 ] */ |
34 | | static ssize_t _read_buffer(int fd, void *buf, size_t length, volatile int *quit) |
35 | 119k | { |
36 | 119k | ssize_t r, read_size = 0; |
37 | | |
38 | 119k | if (fd < 0 || !buf || length > SSIZE_MAX) |
39 | 0 | return -EINVAL; |
40 | | |
41 | 119k | do { |
42 | 119k | r = read(fd, buf, length - read_size); |
43 | 119k | if (r == -1 && errno != EINTR) |
44 | 0 | return r; |
45 | 119k | if (r > 0) { |
46 | | /* coverity[overflow:FALSE] */ |
47 | 89.2k | read_size += r; |
48 | 89.2k | buf = (uint8_t*)buf + r; |
49 | 89.2k | } |
50 | 119k | if (r == 0 || (quit && *quit)) |
51 | 29.7k | return read_size; |
52 | 119k | } while ((size_t)read_size != length); |
53 | | |
54 | 89.2k | return (ssize_t)length; |
55 | 119k | } |
56 | | |
57 | | ssize_t read_buffer(int fd, void *buf, size_t length) |
58 | 119k | { |
59 | 119k | return _read_buffer(fd, buf, length, NULL); |
60 | 119k | } |
61 | | |
62 | | ssize_t read_buffer_intr(int fd, void *buf, size_t length, volatile int *quit) |
63 | 0 | { |
64 | 0 | return _read_buffer(fd, buf, length, quit); |
65 | 0 | } |
66 | | |
67 | | static ssize_t _write_buffer(int fd, const void *buf, size_t length, volatile int *quit) |
68 | 31.5k | { |
69 | 31.5k | ssize_t w, write_size = 0; |
70 | | |
71 | 31.5k | if (fd < 0 || !buf || !length || length > SSIZE_MAX) |
72 | 0 | return -EINVAL; |
73 | | |
74 | 31.5k | do { |
75 | 31.5k | w = write(fd, buf, length - (size_t)write_size); |
76 | 31.5k | if (w < 0 && errno != EINTR) |
77 | 0 | return w; |
78 | 31.5k | if (w > 0) { |
79 | | /* coverity[overflow:FALSE] */ |
80 | 31.5k | write_size += w; |
81 | 31.5k | buf = (const uint8_t*)buf + w; |
82 | 31.5k | } |
83 | 31.5k | if (w == 0 || (quit && *quit)) |
84 | 0 | return write_size; |
85 | 31.5k | } while ((size_t)write_size != length); |
86 | | |
87 | 31.5k | return write_size; |
88 | 31.5k | } |
89 | | |
90 | | ssize_t write_buffer(int fd, const void *buf, size_t length) |
91 | 31.5k | { |
92 | 31.5k | return _write_buffer(fd, buf, length, NULL); |
93 | 31.5k | } |
94 | | |
95 | | ssize_t write_buffer_intr(int fd, const void *buf, size_t length, volatile int *quit) |
96 | 0 | { |
97 | 0 | return _write_buffer(fd, buf, length, quit); |
98 | 0 | } |
99 | | |
100 | | ssize_t write_blockwise(int fd, size_t bsize, size_t alignment, |
101 | | void *orig_buf, size_t length) |
102 | 6.02k | { |
103 | 6.02k | void *hangover_buf = NULL, *buf = NULL; |
104 | 6.02k | size_t hangover, solid; |
105 | 6.02k | ssize_t r, ret = -1; |
106 | | |
107 | 6.02k | if (fd == -1 || !orig_buf || !bsize || !alignment) |
108 | 0 | return -1; |
109 | | |
110 | 6.02k | hangover = length % bsize; |
111 | 6.02k | solid = length - hangover; |
112 | | |
113 | 6.02k | if ((size_t)orig_buf & (alignment - 1)) { |
114 | 4.45k | if (posix_memalign(&buf, alignment, length)) |
115 | 0 | return -1; |
116 | 4.45k | memcpy(buf, orig_buf, length); |
117 | 4.45k | } else |
118 | 1.57k | buf = orig_buf; |
119 | | |
120 | 6.02k | if (solid) { |
121 | 6.02k | r = write_buffer(fd, buf, solid); |
122 | 6.02k | if (r < 0 || r != (ssize_t)solid) |
123 | 0 | goto out; |
124 | 6.02k | } |
125 | | |
126 | 6.02k | if (hangover) { |
127 | 0 | if (posix_memalign(&hangover_buf, alignment, bsize)) |
128 | 0 | goto out; |
129 | 0 | memset(hangover_buf, 0, bsize); |
130 | |
|
131 | 0 | r = read_buffer(fd, hangover_buf, bsize); |
132 | 0 | if (r < 0) |
133 | 0 | goto out; |
134 | | |
135 | 0 | if (lseek(fd, -(off_t)r, SEEK_CUR) < 0) |
136 | 0 | goto out; |
137 | | |
138 | 0 | memcpy(hangover_buf, (char*)buf + solid, hangover); |
139 | |
|
140 | 0 | r = write_buffer(fd, hangover_buf, bsize); |
141 | 0 | if (r < 0 || r < (ssize_t)hangover) |
142 | 0 | goto out; |
143 | 0 | } |
144 | 6.02k | ret = length; |
145 | 6.02k | out: |
146 | 6.02k | free(hangover_buf); |
147 | 6.02k | if (buf != orig_buf) |
148 | 4.45k | free(buf); |
149 | 6.02k | return ret; |
150 | 6.02k | } |
151 | | |
152 | | ssize_t read_blockwise(int fd, size_t bsize, size_t alignment, |
153 | | void *orig_buf, size_t length) |
154 | 86.8k | { |
155 | 86.8k | void *hangover_buf = NULL, *buf = NULL; |
156 | 86.8k | size_t hangover, solid; |
157 | 86.8k | ssize_t r, ret = -1; |
158 | | |
159 | 86.8k | if (fd == -1 || !orig_buf || !bsize || !alignment) |
160 | 0 | return -1; |
161 | | |
162 | 86.8k | hangover = length % bsize; |
163 | 86.8k | solid = length - hangover; |
164 | | |
165 | 86.8k | if ((size_t)orig_buf & (alignment - 1)) { |
166 | 79.5k | if (posix_memalign(&buf, alignment, length)) |
167 | 0 | return -1; |
168 | 79.5k | } else |
169 | 7.31k | buf = orig_buf; |
170 | | |
171 | 86.8k | r = read_buffer(fd, buf, solid); |
172 | 86.8k | if (r < 0 || r != (ssize_t)solid) |
173 | 1 | goto out; |
174 | | |
175 | 86.8k | if (hangover) { |
176 | 29.5k | if (posix_memalign(&hangover_buf, alignment, bsize)) |
177 | 0 | goto out; |
178 | 29.5k | r = read_buffer(fd, hangover_buf, bsize); |
179 | 29.5k | if (r < 0 || r < (ssize_t)hangover) |
180 | 6 | goto out; |
181 | | |
182 | 29.5k | memcpy((char *)buf + solid, hangover_buf, hangover); |
183 | 29.5k | } |
184 | 86.8k | ret = length; |
185 | 86.8k | out: |
186 | 86.8k | free(hangover_buf); |
187 | 86.8k | if (buf != orig_buf) { |
188 | 79.5k | if (ret != -1) |
189 | 79.5k | memcpy(orig_buf, buf, length); |
190 | 79.5k | free(buf); |
191 | 79.5k | } |
192 | 86.8k | return ret; |
193 | 86.8k | } |
194 | | |
195 | | /* |
196 | | * Combines llseek with blockwise write. write_blockwise can already deal with short writes |
197 | | * but we also need a function to deal with short writes at the start. But this information |
198 | | * is implicitly included in the read/write offset, which can not be set to non-aligned |
199 | | * boundaries. Hence, we combine llseek with write. |
200 | | */ |
201 | | ssize_t write_lseek_blockwise(int fd, size_t bsize, size_t alignment, |
202 | | void *buf, size_t length, off_t offset) |
203 | 6.02k | { |
204 | 6.02k | void *frontPadBuf = NULL; |
205 | 6.02k | size_t frontHang, innerCount = 0; |
206 | 6.02k | ssize_t r, ret = -1; |
207 | | |
208 | 6.02k | if (fd == -1 || !buf || !bsize || !alignment) |
209 | 0 | return -1; |
210 | | |
211 | 6.02k | if (offset < 0) |
212 | 0 | offset = lseek(fd, offset, SEEK_END); |
213 | | |
214 | 6.02k | if (offset < 0) |
215 | 0 | return -1; |
216 | | |
217 | 6.02k | frontHang = offset % bsize; |
218 | | |
219 | 6.02k | if (lseek(fd, offset - frontHang, SEEK_SET) < 0) |
220 | 0 | return -1; |
221 | | |
222 | 6.02k | if (frontHang && length) { |
223 | 0 | if (posix_memalign(&frontPadBuf, alignment, bsize)) |
224 | 0 | return -1; |
225 | | |
226 | 0 | innerCount = bsize - frontHang; |
227 | 0 | if (innerCount > length) |
228 | 0 | innerCount = length; |
229 | |
|
230 | 0 | r = read_buffer(fd, frontPadBuf, bsize); |
231 | 0 | if (r < 0 || r < (ssize_t)(frontHang + innerCount)) |
232 | 0 | goto out; |
233 | | |
234 | 0 | memcpy((char*)frontPadBuf + frontHang, buf, innerCount); |
235 | |
|
236 | 0 | if (lseek(fd, offset - frontHang, SEEK_SET) < 0) |
237 | 0 | goto out; |
238 | | |
239 | 0 | r = write_buffer(fd, frontPadBuf, bsize); |
240 | 0 | if (r < 0 || r != (ssize_t)bsize) |
241 | 0 | goto out; |
242 | | |
243 | 0 | buf = (char*)buf + innerCount; |
244 | 0 | length -= innerCount; |
245 | 0 | } |
246 | | |
247 | 6.02k | ret = length ? write_blockwise(fd, bsize, alignment, buf, length) : 0; |
248 | 6.02k | if (ret >= 0) |
249 | 6.02k | ret += innerCount; |
250 | 6.02k | out: |
251 | 6.02k | free(frontPadBuf); |
252 | 6.02k | return ret; |
253 | 6.02k | } |
254 | | |
255 | | ssize_t read_lseek_blockwise(int fd, size_t bsize, size_t alignment, |
256 | | void *buf, size_t length, off_t offset) |
257 | 74.1k | { |
258 | 74.1k | void *frontPadBuf = NULL; |
259 | 74.1k | size_t frontHang, innerCount = 0; |
260 | 74.1k | ssize_t r, ret = -1; |
261 | | |
262 | 74.1k | if (fd == -1 || !buf || bsize <= 0) |
263 | 0 | return -1; |
264 | | |
265 | 74.1k | if (offset < 0) |
266 | 85 | offset = lseek(fd, offset, SEEK_END); |
267 | | |
268 | 74.1k | if (offset < 0) |
269 | 45 | return -1; |
270 | | |
271 | 74.0k | frontHang = offset % bsize; |
272 | | |
273 | 74.0k | if (lseek(fd, offset - frontHang, SEEK_SET) < 0) |
274 | 56 | return -1; |
275 | | |
276 | 74.0k | if (frontHang && length) { |
277 | 2.61k | if (posix_memalign(&frontPadBuf, alignment, bsize)) |
278 | 0 | return -1; |
279 | | |
280 | 2.61k | innerCount = bsize - frontHang; |
281 | 2.61k | if (innerCount > length) |
282 | 1.78k | innerCount = length; |
283 | | |
284 | 2.61k | r = read_buffer(fd, frontPadBuf, bsize); |
285 | 2.61k | if (r < 0 || r < (ssize_t)(frontHang + innerCount)) |
286 | 38 | goto out; |
287 | | |
288 | 2.57k | memcpy(buf, (char*)frontPadBuf + frontHang, innerCount); |
289 | | |
290 | 2.57k | buf = (char*)buf + innerCount; |
291 | 2.57k | length -= innerCount; |
292 | 2.57k | } |
293 | | |
294 | 73.9k | ret = read_blockwise(fd, bsize, alignment, buf, length); |
295 | 73.9k | if (ret >= 0) |
296 | 73.9k | ret += innerCount; |
297 | 74.0k | out: |
298 | 74.0k | free(frontPadBuf); |
299 | 74.0k | return ret; |
300 | 73.9k | } |