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