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