]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/pread.c
xfsprogs: Release v6.10.1
[thirdparty/xfsprogs-dev.git] / io / pread.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2003-2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #define _BSD_SOURCE
8 #define _DEFAULT_SOURCE
9 #include <sys/uio.h>
10 #include "command.h"
11 #include "input.h"
12 #include <ctype.h>
13 #include "init.h"
14 #include "io.h"
15
16 static cmdinfo_t pread_cmd;
17
18 static void
19 pread_help(void)
20 {
21 printf(_(
22 "\n"
23 " reads a range of bytes in a specified block size from the given offset\n"
24 "\n"
25 " Example:\n"
26 " 'pread -v 512 20' - dumps 20 bytes read from 512 bytes into the file\n"
27 "\n"
28 " Reads a segment of the currently open file, optionally dumping it to the\n"
29 " standard output stream (with -v option) for subsequent inspection.\n"
30 " The reads are performed in sequential blocks starting at offset, with the\n"
31 " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n"
32 " unless a different pattern is requested.\n"
33 " -q -- quiet mode, do not write anything to standard output.\n"
34 " -B -- read backwards through the range from offset (backwards N bytes)\n"
35 " -F -- read forwards through the range of bytes from offset (default)\n"
36 " -v -- be verbose, dump out buffers (used when reading forwards)\n"
37 " -R -- read at random offsets in the range of bytes\n"
38 " -Z N -- zeed the random number generator (used when reading randomly)\n"
39 " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n"
40 " -V N -- use vectored IO with N iovecs of blocksize each (preadv)\n"
41 "\n"
42 " When in \"random\" mode, the number of read operations will equal the\n"
43 " number required to do a complete forward/backward scan of the range.\n"
44 " Note that the offset within the range is chosen at random each time\n"
45 " (an offset may be read more than once when operating in this mode).\n"
46 "\n"));
47 }
48
49 void *io_buffer;
50 size_t highwater;
51 size_t io_buffersize;
52 int vectors;
53 struct iovec *iov;
54
55 static int
56 alloc_iovec(
57 size_t bsize,
58 int uflag,
59 unsigned int seed)
60 {
61 int i;
62
63 iov = calloc(vectors, sizeof(struct iovec));
64 if (!iov)
65 return -1;
66
67 io_buffersize = 0;
68 for (i = 0; i < vectors; i++) {
69 iov[i].iov_base = memalign(pagesize, bsize);
70 if (!iov[i].iov_base) {
71 perror("memalign");
72 goto unwind;
73 }
74 iov[i].iov_len = bsize;
75 if (!uflag)
76 memset(iov[i].iov_base, seed, bsize);
77 }
78 io_buffersize = bsize * vectors;
79 return 0;
80 unwind:
81 for( ; i >= 0; i--)
82 free(iov[i].iov_base);
83 free(iov);
84 iov = NULL;
85 return -1;
86 }
87
88 int
89 alloc_buffer(
90 size_t bsize,
91 int uflag,
92 unsigned int seed)
93 {
94 if (vectors)
95 return alloc_iovec(bsize, uflag, seed);
96
97 if (bsize > highwater) {
98 if (io_buffer)
99 free(io_buffer);
100 io_buffer = memalign(pagesize, bsize);
101 if (!io_buffer) {
102 perror("memalign");
103 highwater = io_buffersize = 0;
104 return -1;
105 }
106 highwater = bsize;
107 }
108 io_buffersize = bsize;
109 if (!uflag)
110 memset(io_buffer, seed, io_buffersize);
111 return 0;
112 }
113
114 static void
115 __dump_buffer(
116 void *buf,
117 off_t offset,
118 ssize_t len)
119 {
120 int i, j;
121 char *p;
122
123 for (i = 0, p = (char *)buf; i < len; i += 16) {
124 char *s = p;
125
126 printf("%08llx: ", (unsigned long long)offset + i);
127 for (j = 0; j < 16 && i + j < len; j++, p++)
128 printf("%02x ", *p);
129 printf(" ");
130 for (j = 0; j < 16 && i + j < len; j++, s++) {
131 if (isalnum((int)*s))
132 printf("%c", *s);
133 else
134 printf(".");
135 }
136 printf("\n");
137 }
138 }
139
140 void
141 dump_buffer(
142 off_t offset,
143 ssize_t len)
144 {
145 int i, l;
146
147 if (!vectors) {
148 __dump_buffer(io_buffer, offset, len);
149 return;
150 }
151
152 for (i = 0; len > 0 && i < vectors; i++) {
153 l = min(len, iov[i].iov_len);
154
155 __dump_buffer(iov[i].iov_base, offset, l);
156 len -= l;
157 offset += l;
158 }
159 }
160
161 static ssize_t
162 do_preadv(
163 int fd,
164 off_t offset,
165 long long count)
166 {
167 int vecs = 0;
168 ssize_t oldlen = 0;
169 ssize_t bytes = 0;
170
171 /* trim the iovec if necessary */
172 if (count < io_buffersize) {
173 size_t len = 0;
174 while (len + iov[vecs].iov_len < count) {
175 len += iov[vecs].iov_len;
176 vecs++;
177 }
178 oldlen = iov[vecs].iov_len;
179 iov[vecs].iov_len = count - len;
180 vecs++;
181 } else {
182 vecs = vectors;
183 }
184 bytes = preadv(fd, iov, vectors, offset);
185
186 /* restore trimmed iov */
187 if (oldlen)
188 iov[vecs - 1].iov_len = oldlen;
189
190 return bytes;
191 }
192
193 static ssize_t
194 do_pread(
195 int fd,
196 off_t offset,
197 long long count,
198 size_t buffer_size)
199 {
200 if (!vectors)
201 return pread(fd, io_buffer, min(count, buffer_size), offset);
202
203 return do_preadv(fd, offset, count);
204 }
205
206 static int
207 read_random(
208 int fd,
209 off_t offset,
210 long long count,
211 long long *total,
212 unsigned int seed,
213 int eof)
214 {
215 off_t end, off, range;
216 ssize_t bytes;
217 int ops = 0;
218
219 srandom(seed);
220 end = lseek(fd, 0, SEEK_END);
221 offset = (eof || offset > end) ? end : offset;
222 if ((bytes = (offset % io_buffersize)))
223 offset -= bytes;
224 offset = max(0, offset);
225 if ((bytes = (count % io_buffersize)))
226 count += bytes;
227 count = max(io_buffersize, count);
228 range = count - io_buffersize;
229
230 *total = 0;
231 while (count > 0) {
232 if (range)
233 off = ((offset + (random() % range)) / io_buffersize) *
234 io_buffersize;
235 else
236 off = offset;
237 bytes = do_pread(fd, off, io_buffersize, io_buffersize);
238 if (bytes == 0)
239 break;
240 if (bytes < 0) {
241 perror("pread");
242 return -1;
243 }
244 ops++;
245 *total += bytes;
246 if (bytes < io_buffersize)
247 break;
248 count -= bytes;
249 }
250 return ops;
251 }
252
253 static int
254 read_backward(
255 int fd,
256 off_t *offset,
257 long long *count,
258 long long *total,
259 int eof)
260 {
261 off_t end, off = *offset;
262 ssize_t bytes = 0, bytes_requested;
263 long long cnt = *count;
264 int ops = 0;
265
266 end = lseek(fd, 0, SEEK_END);
267 off = eof ? end : min(end, lseek(fd, off, SEEK_SET));
268 if ((end = off - cnt) < 0) {
269 cnt += end; /* subtraction, end is negative */
270 end = 0;
271 }
272 *total = 0;
273 *count = cnt;
274 *offset = off;
275
276 /* Do initial unaligned read if needed */
277 if ((bytes_requested = (off % io_buffersize))) {
278 off -= bytes_requested;
279 bytes = do_pread(fd, off, bytes_requested, io_buffersize);
280 if (bytes == 0)
281 return ops;
282 if (bytes < 0) {
283 perror("pread");
284 return -1;
285 }
286 ops++;
287 *total += bytes;
288 if (bytes < bytes_requested)
289 return ops;
290 cnt -= bytes;
291 }
292
293 /* Iterate backward through the rest of the range */
294 while (cnt > end) {
295 bytes_requested = min(cnt, io_buffersize);
296 off -= bytes_requested;
297 bytes = do_pread(fd, off, cnt, io_buffersize);
298 if (bytes == 0)
299 break;
300 if (bytes < 0) {
301 perror("pread");
302 return -1;
303 }
304 ops++;
305 *total += bytes;
306 if (bytes < bytes_requested)
307 break;
308 cnt -= bytes;
309 }
310 return ops;
311 }
312
313 static int
314 read_forward(
315 int fd,
316 off_t offset,
317 long long count,
318 long long *total,
319 int verbose,
320 int onlyone,
321 int eof)
322 {
323 ssize_t bytes;
324 int ops = 0;
325
326 *total = 0;
327 while (count > 0 || eof) {
328 bytes = do_pread(fd, offset, count, io_buffersize);
329 if (bytes == 0)
330 break;
331 if (bytes < 0) {
332 perror("pread");
333 return -1;
334 }
335 ops++;
336 if (verbose)
337 dump_buffer(offset, bytes);
338 *total += bytes;
339 if (onlyone || bytes < min(count, io_buffersize))
340 break;
341 offset += bytes;
342 count -= bytes;
343 }
344 return ops;
345 }
346
347 int
348 read_buffer(
349 int fd,
350 off_t offset,
351 long long count,
352 long long *total,
353 int verbose,
354 int onlyone)
355 {
356 return read_forward(fd, offset, count, total, verbose, onlyone, 0);
357 }
358
359 static int
360 pread_f(
361 int argc,
362 char **argv)
363 {
364 size_t bsize;
365 off_t offset;
366 unsigned int zeed = 0;
367 long long count, total, tmp;
368 size_t fsblocksize, fssectsize;
369 struct timeval t1, t2;
370 char *sp;
371 int Cflag, qflag, uflag, vflag;
372 int eof = 0, direction = IO_FORWARD;
373 int c;
374
375 Cflag = qflag = uflag = vflag = 0;
376 init_cvtnum(&fsblocksize, &fssectsize);
377 bsize = fsblocksize;
378
379 while ((c = getopt(argc, argv, "b:BCFRquvV:Z:")) != EOF) {
380 switch (c) {
381 case 'b':
382 tmp = cvtnum(fsblocksize, fssectsize, optarg);
383 if (tmp < 0) {
384 printf(_("non-numeric bsize -- %s\n"), optarg);
385 exitcode = 1;
386 return 0;
387 }
388 bsize = tmp;
389 break;
390 case 'C':
391 Cflag = 1;
392 break;
393 case 'F':
394 direction = IO_FORWARD;
395 break;
396 case 'B':
397 direction = IO_BACKWARD;
398 break;
399 case 'R':
400 direction = IO_RANDOM;
401 break;
402 case 'q':
403 qflag = 1;
404 break;
405 case 'u':
406 uflag = 1;
407 break;
408 case 'v':
409 vflag = 1;
410 break;
411 case 'V':
412 vectors = strtoul(optarg, &sp, 0);
413 if (!sp || sp == optarg) {
414 printf(_("non-numeric vector count == %s\n"),
415 optarg);
416 exitcode = 1;
417 return 0;
418 }
419 break;
420 case 'Z':
421 zeed = strtoul(optarg, &sp, 0);
422 if (!sp || sp == optarg) {
423 printf(_("non-numeric seed -- %s\n"), optarg);
424 exitcode = 1;
425 return 0;
426 }
427 break;
428 default:
429 exitcode = 1;
430 return command_usage(&pread_cmd);
431 }
432 }
433 if (optind != argc - 2) {
434 exitcode = 1;
435 return command_usage(&pread_cmd);
436 }
437
438 offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
439 if (offset < 0 && (direction & (IO_RANDOM|IO_BACKWARD))) {
440 eof = -1; /* read from EOF */
441 } else if (offset < 0) {
442 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
443 exitcode = 1;
444 return 0;
445 }
446 optind++;
447 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
448 if (count < 0 && (direction & (IO_RANDOM|IO_FORWARD))) {
449 eof = -1; /* read to EOF */
450 } else if (count < 0) {
451 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
452 exitcode = 1;
453 return 0;
454 }
455
456 if (alloc_buffer(bsize, uflag, 0xabababab) < 0) {
457 exitcode = 1;
458 return 0;
459 }
460
461 gettimeofday(&t1, NULL);
462 switch (direction) {
463 case IO_RANDOM:
464 if (!zeed) /* srandom seed */
465 zeed = time(NULL);
466 c = read_random(file->fd, offset, count, &total, zeed, eof);
467 break;
468 case IO_FORWARD:
469 c = read_forward(file->fd, offset, count, &total, vflag, 0, eof);
470 if (eof)
471 count = total;
472 break;
473 case IO_BACKWARD:
474 c = read_backward(file->fd, &offset, &count, &total, eof);
475 break;
476 default:
477 ASSERT(0);
478 }
479 if (c < 0) {
480 exitcode = 1;
481 return 0;
482 }
483
484 if (qflag)
485 return 0;
486 gettimeofday(&t2, NULL);
487 t2 = tsub(t2, t1);
488
489 report_io_times("read", &t2, (long long)offset, count, total, c, Cflag);
490 return 0;
491 }
492
493 void
494 pread_init(void)
495 {
496 pread_cmd.name = "pread";
497 pread_cmd.altname = "r";
498 pread_cmd.cfunc = pread_f;
499 pread_cmd.argmin = 2;
500 pread_cmd.argmax = -1;
501 pread_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
502 pread_cmd.args = _("[-b bs] [-qv] [-i N] [-FBR [-Z N]] off len");
503 pread_cmd.oneline = _("reads a number of bytes at a specified offset");
504 pread_cmd.help = pread_help;
505
506 add_command(&pread_cmd);
507 }