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