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