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