]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/mmap.c
xfsprogs: Release v6.10.1
[thirdparty/xfsprogs-dev.git] / io / mmap.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2004-2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include "command.h"
8 #include "input.h"
9 #include <sys/mman.h>
10 #include <signal.h>
11 #include "init.h"
12 #include "io.h"
13
14 static cmdinfo_t mmap_cmd;
15 static cmdinfo_t mread_cmd;
16 static cmdinfo_t msync_cmd;
17 static cmdinfo_t munmap_cmd;
18 static cmdinfo_t mwrite_cmd;
19 static cmdinfo_t mremap_cmd;
20
21 mmap_region_t *maptable;
22 int mapcount;
23 mmap_region_t *mapping;
24
25 static void
26 print_mapping(
27 mmap_region_t *map,
28 int index,
29 int braces)
30 {
31 char buffer[8] = { 0 };
32 int i;
33
34 static struct {
35 int prot;
36 int mode;
37 } *p, pflags[] = {
38 { PROT_READ, 'r' },
39 { PROT_WRITE, 'w' },
40 { PROT_EXEC, 'x' },
41 { PROT_NONE, 0 }
42 };
43
44 for (i = 0, p = pflags; p->prot != PROT_NONE; i++, p++)
45 buffer[i] = (map->prot & p->prot) ? p->mode : '-';
46
47 #ifdef HAVE_MAP_SYNC
48 if ((map->flags & (MAP_SYNC | MAP_SHARED_VALIDATE)) ==
49 (MAP_SYNC | MAP_SHARED_VALIDATE))
50 sprintf(&buffer[i], " S");
51 #endif
52
53 printf("%c%03d%c 0x%lx - 0x%lx %s %14s (%lld : %ld)\n",
54 braces? '[' : ' ', index, braces? ']' : ' ',
55 (unsigned long)map->addr,
56 (unsigned long)((char *)map->addr + map->length),
57 buffer, map->name ? map->name : "???",
58 (long long)map->offset, (long)map->length);
59 }
60
61 void *
62 check_mapping_range(
63 mmap_region_t *map,
64 off_t offset,
65 size_t length,
66 int pagealign)
67 {
68 off_t relative;
69
70 if (offset < mapping->offset) {
71 printf(_("offset (%lld) is before start of mapping (%lld)\n"),
72 (long long)offset, (long long)mapping->offset);
73 return NULL;
74 }
75 relative = offset - mapping->offset;
76 if (relative > mapping->length) {
77 printf(_("offset (%lld) is beyond end of mapping (%lld)\n"),
78 (long long)relative, (long long)mapping->offset);
79 return NULL;
80 }
81 if ((relative + length) > (mapping->offset + mapping->length)) {
82 printf(_("range (%lld:%lld) is beyond mapping (%lld:%ld)\n"),
83 (long long)offset, (long long)relative,
84 (long long)mapping->offset, (long)mapping->length);
85 return NULL;
86 }
87 if (pagealign && (long)((char *)mapping->addr + relative) % pagesize) {
88 printf(_("offset address (%p) is not page aligned\n"),
89 (char *)mapping->addr + relative);
90 return NULL;
91 }
92
93 return (char *)mapping->addr + relative;
94 }
95
96 int
97 maplist_f(void)
98 {
99 int i;
100
101 for (i = 0; i < mapcount; i++)
102 print_mapping(&maptable[i], i, &maptable[i] == mapping);
103 return 0;
104 }
105
106 static int
107 mapset_f(
108 int argc,
109 char **argv)
110 {
111 int i;
112
113 ASSERT(argc == 2);
114 i = atoi(argv[1]);
115 if (i < 0 || i >= mapcount) {
116 printf("value %d is out of range (0-%d)\n", i, mapcount);
117 exitcode = 1;
118 } else {
119 mapping = &maptable[i];
120 maplist_f();
121 }
122 return 0;
123 }
124
125 static void
126 mmap_help(void)
127 {
128 printf(_(
129 "\n"
130 " maps a range within the current file into memory\n"
131 "\n"
132 " Example:\n"
133 " 'mmap -rw 0 1m' - maps one megabyte from the start of the current file\n"
134 "\n"
135 " Memory maps a range of a file for subsequent use by other xfs_io commands.\n"
136 " With no arguments, mmap shows the current mappings. The current mapping\n"
137 " can be set by using the single argument form (mapping number or address).\n"
138 " If two arguments are specified (a range), a new mapping is created and the\n"
139 " following options are available:\n"
140 " -r -- map with PROT_READ protection\n"
141 " -w -- map with PROT_WRITE protection\n"
142 " -x -- map with PROT_EXEC protection\n"
143 #ifdef HAVE_MAP_SYNC
144 " -S -- map with MAP_SYNC and MAP_SHARED_VALIDATE flags\n"
145 #endif
146 " -s <size> -- first do mmap(size)/munmap(size), try to reserve some free space\n"
147 " If no protection mode is specified, all are used by default.\n"
148 "\n"));
149 }
150
151 static int
152 mmap_f(
153 int argc,
154 char **argv)
155 {
156 off_t offset;
157
158 ssize_t length = 0, length2 = 0;
159 void *address = NULL;
160 char *filename;
161 size_t blocksize, sectsize;
162 int c, prot = 0, flags = MAP_SHARED;
163
164 if (argc == 1) {
165 if (mapping)
166 return maplist_f();
167 fprintf(stderr, file ?
168 _("no mapped regions, try 'help mmap'\n") :
169 _("no files are open, try 'help open'\n"));
170 exitcode = 1;
171 return 0;
172 } else if (argc == 2) {
173 if (mapping)
174 return mapset_f(argc, argv);
175 fprintf(stderr, file ?
176 _("no mapped regions, try 'help mmap'\n") :
177 _("no files are open, try 'help open'\n"));
178 exitcode = 1;
179 return 0;
180 } else if (!file) {
181 fprintf(stderr, _("no files are open, try 'help open'\n"));
182 exitcode = 1;
183 return 0;
184 }
185
186 init_cvtnum(&blocksize, &sectsize);
187
188 while ((c = getopt(argc, argv, "rwxSs:")) != EOF) {
189 switch (c) {
190 case 'r':
191 prot |= PROT_READ;
192 break;
193 case 'w':
194 prot |= PROT_WRITE;
195 break;
196 case 'x':
197 prot |= PROT_EXEC;
198 break;
199 case 'S':
200 #ifdef HAVE_MAP_SYNC
201 flags = MAP_SYNC | MAP_SHARED_VALIDATE;
202 break;
203 #else
204 printf("MAP_SYNC not supported\n");
205 exitcode = 1;
206 return command_usage(&mmap_cmd);
207 #endif
208 case 's':
209 length2 = cvtnum(blocksize, sectsize, optarg);
210 break;
211 default:
212 exitcode = 1;
213 return command_usage(&mmap_cmd);
214 }
215 }
216 if (!prot)
217 prot = PROT_READ | PROT_WRITE | PROT_EXEC;
218
219 if (optind != argc - 2) {
220 exitcode = 1;
221 return command_usage(&mmap_cmd);
222 }
223
224 offset = cvtnum(blocksize, sectsize, argv[optind]);
225 if (offset < 0) {
226 printf(_("non-numeric offset argument -- %s\n"), argv[optind]);
227 exitcode = 1;
228 return 0;
229 }
230 optind++;
231 length = cvtnum(blocksize, sectsize, argv[optind]);
232 if (length < 0) {
233 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
234 exitcode = 1;
235 return 0;
236 }
237
238 filename = strdup(file->name);
239 if (!filename) {
240 perror("strdup");
241 exitcode = 1;
242 return 0;
243 }
244
245 /*
246 * mmap and munmap memory area of length2 region is helpful to
247 * make a region of extendible free memory. It's generally used
248 * for later mremap operation(no MREMAP_MAYMOVE flag). But there
249 * isn't guarantee that the memory after length (up to length2)
250 * will stay free.
251 */
252 if (length2 > length) {
253 address = mmap(NULL, length2, prot,
254 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
255 munmap(address, length2);
256 }
257 address = mmap(address, length, prot, flags, file->fd, offset);
258 if (address == MAP_FAILED) {
259 perror("mmap");
260 free(filename);
261 exitcode = 1;
262 return 0;
263 }
264
265 /* Extend the control array of mmap'd regions */
266 maptable = (mmap_region_t *)realloc(maptable, /* growing */
267 ++mapcount * sizeof(mmap_region_t));
268 if (!maptable) {
269 perror("realloc");
270 mapcount = 0;
271 munmap(address, length);
272 free(filename);
273 exitcode = 1;
274 return 0;
275 }
276
277 /* Finally, make this the new active mapping */
278 mapping = &maptable[mapcount - 1];
279 mapping->addr = address;
280 mapping->length = length;
281 mapping->offset = offset;
282 mapping->name = filename;
283 mapping->prot = prot;
284 mapping->flags = flags;
285 return 0;
286 }
287
288 static void
289 msync_help(void)
290 {
291 printf(_(
292 "\n"
293 " flushes a range of bytes in the current memory mapping\n"
294 "\n"
295 " Writes all modified copies of pages over the specified range (or entire\n"
296 " mapping if no range specified) to their backing storage locations. Also,\n"
297 " optionally invalidates so that subsequent references to the pages will be\n"
298 " obtained from their backing storage locations (instead of cached copies).\n"
299 " -a -- perform asynchronous writes (MS_ASYNC)\n"
300 " -i -- invalidate mapped pages (MS_INVALIDATE)\n"
301 " -s -- perform synchronous writes (MS_SYNC)\n"
302 "\n"));
303 }
304
305 static int
306 msync_f(
307 int argc,
308 char **argv)
309 {
310 off_t offset;
311 ssize_t length;
312 void *start;
313 int c, flags = 0;
314 size_t blocksize, sectsize;
315
316 while ((c = getopt(argc, argv, "ais")) != EOF) {
317 switch (c) {
318 case 'a':
319 flags |= MS_ASYNC;
320 break;
321 case 'i':
322 flags |= MS_INVALIDATE;
323 break;
324 case 's':
325 flags |= MS_SYNC;
326 break;
327 default:
328 exitcode = 1;
329 return command_usage(&msync_cmd);
330 }
331 }
332
333 if (optind == argc) {
334 offset = mapping->offset;
335 length = mapping->length;
336 } else if (optind == argc - 2) {
337 init_cvtnum(&blocksize, &sectsize);
338 offset = cvtnum(blocksize, sectsize, argv[optind]);
339 if (offset < 0) {
340 printf(_("non-numeric offset argument -- %s\n"),
341 argv[optind]);
342 exitcode = 1;
343 return 0;
344 }
345 optind++;
346 length = cvtnum(blocksize, sectsize, argv[optind]);
347 if (length < 0) {
348 printf(_("non-numeric length argument -- %s\n"),
349 argv[optind]);
350 exitcode = 1;
351 return 0;
352 }
353 } else {
354 exitcode = 1;
355 return command_usage(&msync_cmd);
356 }
357
358 start = check_mapping_range(mapping, offset, length, 1);
359 if (!start) {
360 exitcode = 1;
361 return 0;
362 }
363
364 if (msync(start, length, flags) < 0) {
365 perror("msync");
366 exitcode = 1;
367 return 0;
368 }
369
370 return 0;
371 }
372
373 static void
374 mread_help(void)
375 {
376 printf(_(
377 "\n"
378 " reads a range of bytes in the current memory mapping\n"
379 "\n"
380 " Example:\n"
381 " 'mread -v 512 20' - dumps 20 bytes read from 512 bytes into the mapping\n"
382 "\n"
383 " Accesses a range of the current memory mapping, optionally dumping it to\n"
384 " the standard output stream (with -v option) for subsequent inspection.\n"
385 " -f -- verbose mode, dump bytes with offsets relative to start of file.\n"
386 " -r -- reverse order; start accessing from the end of range, moving backward\n"
387 " -v -- verbose mode, dump bytes with offsets relative to start of mapping.\n"
388 " The accesses are performed sequentially from the start offset by default.\n"
389 " Notes:\n"
390 " References to whole pages following the end of the backing file results\n"
391 " in delivery of the SIGBUS signal. SIGBUS signals may also be delivered\n"
392 " on various filesystem conditions, including quota exceeded errors, and\n"
393 " for physical device errors (such as unreadable disk blocks). No attempt\n"
394 " has been made to catch signals at this stage...\n"
395 "\n"));
396 }
397
398 static int
399 mread_f(
400 int argc,
401 char **argv)
402 {
403 off_t offset, tmp, dumpoffset, printoffset;
404 ssize_t length;
405 size_t dumplen, cnt = 0;
406 char *bp;
407 void *start;
408 int dump = 0, rflag = 0, c;
409 size_t blocksize, sectsize;
410
411 while ((c = getopt(argc, argv, "frv")) != EOF) {
412 switch (c) {
413 case 'f':
414 dump = 2; /* file offset dump */
415 break;
416 case 'r':
417 rflag = 1; /* read in reverse */
418 break;
419 case 'v':
420 dump = 1; /* mapping offset dump */
421 break;
422 default:
423 exitcode = 1;
424 return command_usage(&mread_cmd);
425 }
426 }
427
428 if (optind == argc) {
429 offset = mapping->offset;
430 length = mapping->length;
431 } else if (optind == argc - 2) {
432 init_cvtnum(&blocksize, &sectsize);
433 offset = cvtnum(blocksize, sectsize, argv[optind]);
434 if (offset < 0) {
435 printf(_("non-numeric offset argument -- %s\n"),
436 argv[optind]);
437 exitcode = 1;
438 return 0;
439 }
440 optind++;
441 length = cvtnum(blocksize, sectsize, argv[optind]);
442 if (length < 0) {
443 printf(_("non-numeric length argument -- %s\n"),
444 argv[optind]);
445 exitcode = 1;
446 return 0;
447 }
448 } else {
449 return command_usage(&mread_cmd);
450 }
451
452 start = check_mapping_range(mapping, offset, length, 0);
453 if (!start) {
454 exitcode = 1;
455 return 0;
456 }
457 dumpoffset = offset - mapping->offset;
458 if (dump == 2)
459 printoffset = offset;
460 else
461 printoffset = dumpoffset;
462
463 if (alloc_buffer(pagesize, 0, 0) < 0) {
464 exitcode = 1;
465 return 0;
466 }
467 bp = (char *)io_buffer;
468
469 dumplen = length % pagesize;
470 if (!dumplen)
471 dumplen = pagesize;
472
473 if (rflag) {
474 for (tmp = length - 1; tmp >= 0; tmp--) {
475 bp[cnt++] = ((char *)mapping->addr)[dumpoffset + tmp];
476 if (cnt == dumplen) {
477 if (dump) {
478 dump_buffer(printoffset, dumplen);
479 printoffset += dumplen;
480 }
481 dumplen = pagesize;
482 cnt = 0;
483 }
484 }
485 } else {
486 for (tmp = 0; tmp < length; tmp++) {
487 bp[cnt++] = ((char *)mapping->addr)[dumpoffset + tmp];
488 if (cnt == dumplen) {
489 if (dump)
490 dump_buffer(printoffset + tmp -
491 (dumplen - 1), dumplen);
492 dumplen = pagesize;
493 cnt = 0;
494 }
495 }
496 }
497 return 0;
498 }
499
500 static int
501 munmap_f(
502 int argc,
503 char **argv)
504 {
505 ssize_t length;
506 unsigned int offset;
507
508 if (munmap(mapping->addr, mapping->length) < 0) {
509 perror("munmap");
510 exitcode = 1;
511 return 0;
512 }
513 free(mapping->name);
514
515 /* Shuffle the mapping table entries down over the removed entry */
516 offset = mapping - &maptable[0];
517 length = mapcount * sizeof(mmap_region_t);
518 length -= (offset + 1) * sizeof(mmap_region_t);
519 if (length)
520 memmove(mapping, mapping + 1, length);
521
522 /* Resize the memory allocated for the table, possibly freeing */
523 if (--mapcount) {
524 maptable = (mmap_region_t *)realloc(maptable, /* shrinking */
525 mapcount * sizeof(mmap_region_t));
526 if (offset == mapcount)
527 offset--;
528 mapping = maptable + offset;
529 } else {
530 free(maptable);
531 mapping = maptable = NULL;
532 }
533 maplist_f();
534 return 0;
535 }
536
537 static void
538 mwrite_help(void)
539 {
540 printf(_(
541 "\n"
542 " dirties a range of bytes in the current memory mapping\n"
543 "\n"
544 " Example:\n"
545 " 'mwrite 512 20 - writes 20 bytes at 512 bytes into the current mapping.\n"
546 "\n"
547 " Stores a byte into memory for a range within a mapping.\n"
548 " The default stored value is 'X', repeated to fill the range specified.\n"
549 " -S -- use an alternate seed character\n"
550 " -r -- reverse order; start storing from the end of range, moving backward\n"
551 " The stores are performed sequentially from the start offset by default.\n"
552 "\n"));
553 }
554
555 static int
556 mwrite_f(
557 int argc,
558 char **argv)
559 {
560 off_t offset, tmp;
561 ssize_t length;
562 void *start;
563 char *sp;
564 int seed = 'X';
565 int rflag = 0;
566 int c;
567 size_t blocksize, sectsize;
568
569 while ((c = getopt(argc, argv, "rS:")) != EOF) {
570 switch (c) {
571 case 'r':
572 rflag = 1;
573 break;
574 case 'S':
575 seed = (int)strtol(optarg, &sp, 0);
576 if (!sp || sp == optarg) {
577 printf(_("non-numeric seed -- %s\n"), optarg);
578 return 0;
579 }
580 break;
581 default:
582 exitcode = 1;
583 return command_usage(&mwrite_cmd);
584 }
585 }
586
587 if (optind == argc) {
588 offset = mapping->offset;
589 length = mapping->length;
590 } else if (optind == argc - 2) {
591 init_cvtnum(&blocksize, &sectsize);
592 offset = cvtnum(blocksize, sectsize, argv[optind]);
593 if (offset < 0) {
594 printf(_("non-numeric offset argument -- %s\n"),
595 argv[optind]);
596 exitcode = 1;
597 return 0;
598 }
599 optind++;
600 length = cvtnum(blocksize, sectsize, argv[optind]);
601 if (length < 0) {
602 printf(_("non-numeric length argument -- %s\n"),
603 argv[optind]);
604 exitcode = 1;
605 return 0;
606 }
607 } else {
608 exitcode = 1;
609 return command_usage(&mwrite_cmd);
610 }
611
612 start = check_mapping_range(mapping, offset, length, 0);
613 if (!start) {
614 exitcode = 1;
615 return 0;
616 }
617
618 offset -= mapping->offset;
619 if (rflag) {
620 for (tmp = offset + length -1; tmp >= offset; tmp--)
621 ((char *)mapping->addr)[tmp] = seed;
622 } else {
623 for (tmp = offset; tmp < offset + length; tmp++)
624 ((char *)mapping->addr)[tmp] = seed;
625 }
626
627 return 0;
628 }
629
630 static void
631 mremap_help(void)
632 {
633 printf(_(
634 "\n"
635 " resizes the current memory mapping\n"
636 "\n"
637 " Examples:\n"
638 " 'mremap 8192' - resizes the current mapping to 8192 bytes.\n"
639 "\n"
640 " Resizes the mapping, growing or shrinking from the current size.\n"
641 " The default stored value is 'X', repeated to fill the range specified.\n"
642 " -f <new_address> -- use MREMAP_FIXED flag to mremap on new_address\n"
643 " -m -- use the MREMAP_MAYMOVE flag\n"
644 "\n"));
645 }
646
647 static int
648 mremap_f(
649 int argc,
650 char **argv)
651 {
652 ssize_t new_length;
653 void *new_addr = NULL;
654 int flags = 0;
655 int c;
656 size_t blocksize, sectsize;
657
658 init_cvtnum(&blocksize, &sectsize);
659
660 while ((c = getopt(argc, argv, "f:m")) != EOF) {
661 switch (c) {
662 case 'f':
663 flags = MREMAP_FIXED|MREMAP_MAYMOVE;
664 new_addr = (void *)(unsigned long)cvtnum(blocksize,
665 sectsize, optarg);
666 break;
667 case 'm':
668 flags = MREMAP_MAYMOVE;
669 break;
670 default:
671 exitcode = 1;
672 return command_usage(&mremap_cmd);
673 }
674 }
675
676 if (optind != argc - 1) {
677 exitcode = 1;
678 return command_usage(&mremap_cmd);
679 }
680
681 new_length = cvtnum(blocksize, sectsize, argv[optind]);
682 if (new_length < 0) {
683 printf(_("non-numeric offset argument -- %s\n"),
684 argv[optind]);
685 exitcode = 1;
686 return 0;
687 }
688
689 if (!new_addr)
690 new_addr = mremap(mapping->addr, mapping->length,
691 new_length, flags);
692 else
693 new_addr = mremap(mapping->addr, mapping->length,
694 new_length, flags, new_addr);
695 if (new_addr == MAP_FAILED) {
696 perror("mremap");
697 exitcode = 1;
698 return 0;
699 }
700
701 mapping->addr = new_addr;
702 mapping->length = new_length;
703 return 0;
704 }
705
706 void
707 mmap_init(void)
708 {
709 mmap_cmd.name = "mmap";
710 mmap_cmd.altname = "mm";
711 mmap_cmd.cfunc = mmap_f;
712 mmap_cmd.argmin = 0;
713 mmap_cmd.argmax = -1;
714 mmap_cmd.flags = CMD_NOMAP_OK | CMD_NOFILE_OK |
715 CMD_FOREIGN_OK | CMD_FLAG_ONESHOT;
716 mmap_cmd.args = _("[N] | [-rwxS] [-s size] [off len]");
717 mmap_cmd.oneline =
718 _("mmap a range in the current file, show mappings");
719 mmap_cmd.help = mmap_help;
720
721 mread_cmd.name = "mread";
722 mread_cmd.altname = "mr";
723 mread_cmd.cfunc = mread_f;
724 mread_cmd.argmin = 0;
725 mread_cmd.argmax = -1;
726 mread_cmd.flags = CMD_NOFILE_OK | CMD_FOREIGN_OK;
727 mread_cmd.args = _("[-r] [off len]");
728 mread_cmd.oneline =
729 _("reads data from a region in the current memory mapping");
730 mread_cmd.help = mread_help;
731
732 msync_cmd.name = "msync";
733 msync_cmd.altname = "ms";
734 msync_cmd.cfunc = msync_f;
735 msync_cmd.argmin = 0;
736 msync_cmd.argmax = -1;
737 msync_cmd.flags = CMD_NOFILE_OK | CMD_FOREIGN_OK;
738 msync_cmd.args = _("[-ais] [off len]");
739 msync_cmd.oneline = _("flush a region in the current memory mapping");
740 msync_cmd.help = msync_help;
741
742 munmap_cmd.name = "munmap";
743 munmap_cmd.altname = "mu";
744 munmap_cmd.cfunc = munmap_f;
745 munmap_cmd.argmin = 0;
746 munmap_cmd.argmax = 0;
747 munmap_cmd.flags = CMD_NOFILE_OK | CMD_FOREIGN_OK;
748 munmap_cmd.oneline = _("unmaps the current memory mapping");
749
750 mwrite_cmd.name = "mwrite";
751 mwrite_cmd.altname = "mw";
752 mwrite_cmd.cfunc = mwrite_f;
753 mwrite_cmd.argmin = 0;
754 mwrite_cmd.argmax = -1;
755 mwrite_cmd.flags = CMD_NOFILE_OK | CMD_FOREIGN_OK;
756 mwrite_cmd.args = _("[-r] [-S seed] [off len]");
757 mwrite_cmd.oneline =
758 _("writes data into a region in the current memory mapping");
759 mwrite_cmd.help = mwrite_help;
760
761 mremap_cmd.name = "mremap";
762 mremap_cmd.altname = "mrm";
763 mremap_cmd.cfunc = mremap_f;
764 mremap_cmd.argmin = 1;
765 mremap_cmd.argmax = 3;
766 mremap_cmd.flags = CMD_NOFILE_OK | CMD_FOREIGN_OK;
767 mremap_cmd.args = _("[-m|-f <new_address>] newsize");
768 mremap_cmd.oneline =
769 _("alters the size of the current memory mapping");
770 mremap_cmd.help = mremap_help;
771
772 add_command(&mmap_cmd);
773 add_command(&mread_cmd);
774 add_command(&msync_cmd);
775 add_command(&munmap_cmd);
776 add_command(&mwrite_cmd);
777 add_command(&mremap_cmd);
778 }