]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/io.c
xfs_db: report the device associated with each io cursor
[thirdparty/xfsprogs-dev.git] / db / io.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include "libxfs.h"
8 #include "command.h"
9 #include "type.h"
10 #include "faddr.h"
11 #include "fprint.h"
12 #include "field.h"
13 #include "dquot.h"
14 #include "inode.h"
15 #include "io.h"
16 #include "output.h"
17 #include "init.h"
18 #include "malloc.h"
19 #include "crc.h"
20 #include "bit.h"
21
22 static int pop_f(int argc, char **argv);
23 static void pop_help(void);
24 static int push_f(int argc, char **argv);
25 static void push_help(void);
26 static int stack_f(int argc, char **argv);
27 static void stack_help(void);
28 static int forward_f(int argc, char **argv);
29 static void forward_help(void);
30 static int back_f(int argc, char **argv);
31 static void back_help(void);
32 static int ring_f(int argc, char **argv);
33 static void ring_help(void);
34
35 static const cmdinfo_t pop_cmd =
36 { "pop", NULL, pop_f, 0, 0, 0, NULL,
37 N_("pop location from the stack"), pop_help };
38 static const cmdinfo_t push_cmd =
39 { "push", NULL, push_f, 0, 2, 0, N_("[command]"),
40 N_("push location to the stack"), push_help };
41 static const cmdinfo_t stack_cmd =
42 { "stack", NULL, stack_f, 0, 0, 0, NULL,
43 N_("view the location stack"), stack_help };
44 static const cmdinfo_t forward_cmd =
45 { "forward", "f", forward_f, 0, 0, 0, NULL,
46 N_("move forward to next entry in the position ring"), forward_help };
47 static const cmdinfo_t back_cmd =
48 { "back", "b", back_f, 0, 0, 0, NULL,
49 N_("move to the previous location in the position ring"), back_help };
50 static const cmdinfo_t ring_cmd =
51 { "ring", NULL, ring_f, 0, 1, 0, NULL,
52 N_("show position ring or move to a specific entry"), ring_help };
53
54 iocur_t *iocur_base;
55 iocur_t *iocur_top;
56 int iocur_sp = -1;
57 int iocur_len;
58
59 #define RING_ENTRIES 20
60 static iocur_t iocur_ring[RING_ENTRIES];
61 static int ring_head = -1;
62 static int ring_tail = -1;
63 static int ring_current = -1;
64
65 void
66 io_init(void)
67 {
68 add_command(&pop_cmd);
69 add_command(&push_cmd);
70 add_command(&stack_cmd);
71 add_command(&forward_cmd);
72 add_command(&back_cmd);
73 add_command(&ring_cmd);
74 }
75
76 static inline void set_cur_boff(int off)
77 {
78 iocur_top->boff = off;
79 iocur_top->off = ((xfs_off_t)iocur_top->bb << BBSHIFT) + off;
80 iocur_top->data = (void *)((char *)iocur_top->buf + off);
81 }
82
83 void
84 off_cur(
85 int off,
86 int len)
87 {
88 if (iocur_top == NULL || off + len > BBTOB(iocur_top->blen))
89 dbprintf(_("can't set block offset to %d\n"), off);
90 else {
91 set_cur_boff(off);
92 iocur_top->len = len;
93 }
94 }
95
96 void
97 pop_cur(void)
98 {
99 if (iocur_sp < 0) {
100 dbprintf(_("can't pop anything from I/O stack\n"));
101 return;
102 }
103 if (iocur_top->bp) {
104 libxfs_buf_relse(iocur_top->bp);
105 iocur_top->bp = NULL;
106 }
107 if (iocur_top->bbmap) {
108 free(iocur_top->bbmap);
109 iocur_top->bbmap = NULL;
110 }
111 if (--iocur_sp >= 0) {
112 iocur_top = iocur_base + iocur_sp;
113 cur_typ = iocur_top->typ;
114 } else {
115 iocur_top = iocur_base;
116 iocur_sp = 0;
117 }
118 }
119
120 /*ARGSUSED*/
121 static int
122 pop_f(
123 int argc,
124 char **argv)
125 {
126 pop_cur();
127 return 0;
128 }
129
130 static void
131 pop_help(void)
132 {
133 dbprintf(_(
134 "\n"
135 " Changes the address and data type to the first entry on the stack.\n"
136 "\n"
137 ));
138 }
139
140 bool
141 iocur_is_ddev(const struct iocur *ioc)
142 {
143 if (!ioc->bp)
144 return false;
145
146 return ioc->bp->b_target == ioc->bp->b_mount->m_ddev_targp;
147 }
148
149 bool
150 iocur_is_extlogdev(const struct iocur *ioc)
151 {
152 struct xfs_buf *bp = ioc->bp;
153
154 if (!bp)
155 return false;
156 if (bp->b_mount->m_logdev_targp == bp->b_mount->m_ddev_targp)
157 return false;
158
159 return bp->b_target == bp->b_mount->m_logdev_targp;
160 }
161
162 void
163 print_iocur(
164 char *tag,
165 iocur_t *ioc)
166 {
167 const char *block_unit = "fsbno?";
168 int i;
169
170 if (iocur_is_ddev(ioc))
171 block_unit = "fsbno";
172 else if (iocur_is_extlogdev(ioc))
173 block_unit = "logbno";
174
175 dbprintf("%s\n", tag);
176 dbprintf(_("\tbyte offset %lld, length %d\n"), ioc->off, ioc->len);
177 dbprintf(_("\tbuffer block %lld (%s %lld), %d bb%s\n"), ioc->bb,
178 block_unit,
179 (xfs_fsblock_t)XFS_DADDR_TO_FSB(mp, ioc->bb),
180 ioc->blen, ioc->blen == 1 ? "" : "s");
181 if (ioc->bbmap) {
182 dbprintf(_("\tblock map"));
183 for (i = 0; i < ioc->bbmap->nmaps; i++)
184 dbprintf(" %lld:%d", ioc->bbmap->b[i].bm_bn,
185 ioc->bbmap->b[i].bm_len);
186 dbprintf("\n");
187 }
188 dbprintf(_("\tinode %lld, dir inode %lld, type %s\n"), ioc->ino,
189 ioc->dirino, ioc->typ == NULL ? _("none") : ioc->typ->name);
190 }
191
192 static void
193 print_ring(void)
194 {
195 int i;
196 iocur_t *ioc;
197
198 if (ring_current == -1) {
199 dbprintf(_("no entries in location ring.\n"));
200 return;
201 }
202
203 dbprintf(_(" type bblock bblen fsbno inode\n"));
204
205 i = ring_head;
206 for (;;) {
207 ioc = &iocur_ring[i];
208 if (i == ring_current)
209 printf("*%2d: ", i);
210 else
211 printf(" %2d: ", i);
212
213 dbprintf("%-7.7s %8lld %5d %8lld %9lld\n",
214 ioc->typ == NULL ? "none" : ioc->typ->name,
215 ioc->bb,
216 ioc->blen,
217 (xfs_fsblock_t)XFS_DADDR_TO_FSB(mp, ioc->bb),
218 ioc->ino
219 );
220
221 if (i == ring_tail)
222 break;
223
224 i = (i+(RING_ENTRIES-1))%RING_ENTRIES;
225 }
226 }
227
228
229 void
230 push_cur(void)
231 {
232 if (iocur_sp + 1 >= iocur_len) {
233 iocur_base = xrealloc(iocur_base,
234 sizeof(*iocur_base) * (iocur_len + 1));
235 iocur_len++;
236 }
237 iocur_sp++;
238 iocur_top = iocur_base + iocur_sp;
239 memset(iocur_top, 0, sizeof(*iocur_base));
240 iocur_top->ino = iocur_sp > 0 ? iocur_top[-1].ino : NULLFSINO;
241 iocur_top->dirino = iocur_sp > 0 ? iocur_top[-1].dirino : NULLFSINO;
242 iocur_top->mode = iocur_sp > 0 ? iocur_top[-1].mode : 0;
243 cur_typ = NULL;
244 }
245
246 void
247 push_cur_and_set_type(void)
248 {
249 /* save current state */
250 push_cur();
251 if (iocur_top[-1].typ && iocur_top[-1].typ->typnm == TYP_INODE)
252 set_cur_inode(iocur_top[-1].ino);
253 else
254 set_cur(iocur_top[-1].typ, iocur_top[-1].bb,
255 iocur_top[-1].blen, DB_RING_IGN,
256 iocur_top[-1].bbmap);
257 }
258
259 static int
260 push_f(
261 int argc,
262 char **argv)
263 {
264 const cmdinfo_t *ct;
265
266 if (argc > 1) {
267 /* check we can execute command */
268 ct = find_command(argv[1]);
269 if (ct == NULL) {
270 dbprintf(_("no such command %s\n"), argv[1]);
271 return 0;
272 }
273 if (!ct->canpush) {
274 dbprintf(_("no push form allowed for %s\n"), argv[1]);
275 return 0;
276 }
277 }
278
279 push_cur_and_set_type();
280
281 /* run requested command */
282 if (argc>1)
283 (void)command(argc-1, argv+1);
284 return 0;
285 }
286
287 static void
288 push_help(void)
289 {
290 dbprintf(_(
291 "\n"
292 " Allows you to push the current address and data type on the stack for\n"
293 " later return. 'push' also accepts an additional command to execute after\n"
294 " storing the current address (ex: 'push a rootino' from the superblock).\n"
295 "\n"
296 ));
297 }
298
299 /* move forward through the ring */
300 /* ARGSUSED */
301 static int
302 forward_f(
303 int argc,
304 char **argv)
305 {
306 if (ring_current == -1) {
307 dbprintf(_("ring is empty\n"));
308 return 0;
309 }
310 if (ring_current == ring_head) {
311 dbprintf(_("no further entries\n"));
312 return 0;
313 }
314
315 ring_current = (ring_current+1)%RING_ENTRIES;
316
317 set_cur(iocur_ring[ring_current].typ,
318 iocur_ring[ring_current].bb,
319 iocur_ring[ring_current].blen,
320 DB_RING_IGN,
321 iocur_ring[ring_current].bbmap);
322
323 return 0;
324 }
325
326 static void
327 forward_help(void)
328 {
329 dbprintf(_(
330 "\n"
331 " The 'forward' ('f') command moves to the next location in the position\n"
332 " ring, updating the current position and data type. If the current location\n"
333 " is the top entry in the ring, then the 'forward' command will have\n"
334 " no effect.\n"
335 "\n"
336 ));
337 }
338
339 /* move backwards through the ring */
340 /* ARGSUSED */
341 static int
342 back_f(
343 int argc,
344 char **argv)
345 {
346 if (ring_current == -1) {
347 dbprintf(_("ring is empty\n"));
348 return 0;
349 }
350 if (ring_current == ring_tail) {
351 dbprintf(_("no previous entries\n"));
352 return 0;
353 }
354
355 ring_current = (ring_current+(RING_ENTRIES-1))%RING_ENTRIES;
356
357 set_cur(iocur_ring[ring_current].typ,
358 iocur_ring[ring_current].bb,
359 iocur_ring[ring_current].blen,
360 DB_RING_IGN,
361 iocur_ring[ring_current].bbmap);
362
363 return 0;
364 }
365
366 static void
367 back_help(void)
368 {
369 dbprintf(_(
370 "\n"
371 " The 'back' ('b') command moves to the previous location in the position\n"
372 " ring, updating the current position and data type. If the current location\n"
373 " is the last entry in the ring, then the 'back' command will have no effect.\n"
374 "\n"
375 ));
376 }
377
378 /* show or go to specific point in ring */
379 static int
380 ring_f(
381 int argc,
382 char **argv)
383 {
384 int index;
385
386 if (argc == 1) {
387 print_ring();
388 return 0;
389 }
390
391 index = (int)strtoul(argv[1], NULL, 0);
392 if (index < 0 || index >= RING_ENTRIES) {
393 dbprintf(_("invalid entry: %d\n"), index);
394 return 0;
395 }
396
397 ring_current = index;
398
399 set_cur(iocur_ring[index].typ,
400 iocur_ring[index].bb,
401 iocur_ring[index].blen,
402 DB_RING_IGN,
403 iocur_ring[index].bbmap);
404
405 return 0;
406 }
407
408 static void
409 ring_help(void)
410 {
411 dbprintf(_(
412 "\n"
413 " The position ring automatically keeps track of each disk location and\n"
414 " structure type for each change of position you make during your xfs_db\n"
415 " session. The last %d most recent entries are kept in the ring.\n"
416 "\n"
417 " To display the current list of ring entries type 'ring' by itself on\n"
418 " the command line. The entry highlighted by an asterisk ('*') is the\n"
419 " current entry.\n"
420 "\n"
421 " To move to another entry in the ring type 'ring <num>' where <num> is\n"
422 " your desired entry from the ring position list.\n"
423 "\n"
424 " You may also use the 'forward' ('f') or 'back' ('b') commands to move\n"
425 " to the previous or next entry in the ring, respectively.\n"
426 "\n"
427 " Note: Unlike the 'stack', 'push' and 'pop' commands, the ring tracks your\n"
428 " location implicitly. Use the 'push' and 'pop' commands if you wish to\n"
429 " store a specific location explicitly for later return.\n"
430 "\n"),
431 RING_ENTRIES);
432 }
433
434
435 void
436 ring_add(void)
437 {
438 if (ring_head == -1) {
439 /* only get here right after startup */
440 ring_head = 0;
441 ring_tail = 0;
442 ring_current = 0;
443 iocur_ring[0] = *iocur_top;
444 } else {
445 if (ring_current == ring_head) {
446 ring_head = (ring_head+1)%RING_ENTRIES;
447 iocur_ring[ring_head] = *iocur_top;
448 if (ring_head == ring_tail)
449 ring_tail = (ring_tail+1)%RING_ENTRIES;
450 ring_current = ring_head;
451 } else {
452 ring_current = (ring_current+1)%RING_ENTRIES;
453 iocur_ring[ring_current] = *iocur_top;
454 }
455 }
456 }
457
458 static void
459 write_cur_buf(void)
460 {
461 int ret;
462
463 ret = -libxfs_bwrite(iocur_top->bp);
464 if (ret != 0)
465 dbprintf(_("write error: %s\n"), strerror(ret));
466
467 /* re-read buffer from disk */
468 ret = -libxfs_readbufr(mp->m_ddev_targp, iocur_top->bb, iocur_top->bp,
469 iocur_top->blen, 0);
470 if (ret != 0)
471 dbprintf(_("read error: %s\n"), strerror(ret));
472 }
473
474 static void
475 write_cur_bbs(void)
476 {
477 int ret;
478
479 ret = -libxfs_bwrite(iocur_top->bp);
480 if (ret != 0)
481 dbprintf(_("write error: %s\n"), strerror(ret));
482
483
484 /* re-read buffer from disk */
485 ret = -libxfs_readbufr_map(mp->m_ddev_targp, iocur_top->bp, 0);
486 if (ret != 0)
487 dbprintf(_("read error: %s\n"), strerror(ret));
488 }
489
490 void
491 xfs_dummy_verify(
492 struct xfs_buf *bp)
493 {
494 return;
495 }
496
497 void
498 xfs_verify_recalc_crc(
499 struct xfs_buf *bp)
500 {
501 xfs_buf_update_cksum(bp, iocur_top->typ->crc_off);
502 }
503
504 void
505 write_cur(void)
506 {
507 bool skip_crc = false;
508
509 if (iocur_sp < 0) {
510 dbprintf(_("nothing to write\n"));
511 return;
512 }
513
514 if (!xfs_has_crc(mp) ||
515 !iocur_top->bp->b_ops ||
516 iocur_top->bp->b_ops->verify_write == xfs_dummy_verify)
517 skip_crc = true;
518
519 if (!skip_crc) {
520 if (iocur_top->ino_buf)
521 xfs_inode_set_crc(iocur_top->bp);
522 else if (iocur_top->dquot_buf)
523 xfs_dquot_set_crc(iocur_top->bp);
524 }
525 if (iocur_top->bbmap)
526 write_cur_bbs();
527 else
528 write_cur_buf();
529
530 /* If we didn't write the crc automatically, re-check inode validity */
531 if (xfs_has_crc(mp) &&
532 skip_crc && iocur_top->ino_buf) {
533 iocur_top->ino_crc_ok = libxfs_verify_cksum(iocur_top->data,
534 mp->m_sb.sb_inodesize,
535 XFS_DINODE_CRC_OFF);
536 }
537
538 }
539
540 static void
541 __set_cur(
542 struct xfs_buftarg *btargp,
543 const typ_t *type,
544 xfs_daddr_t blknum,
545 int len,
546 int ring_flag,
547 bbmap_t *bbmap)
548 {
549 struct xfs_buf *bp;
550 xfs_ino_t dirino;
551 xfs_ino_t ino;
552 uint16_t mode;
553 const struct xfs_buf_ops *ops = type ? type->bops : NULL;
554 int error;
555
556 if (iocur_sp < 0) {
557 dbprintf(_("set_cur no stack element to set\n"));
558 return;
559 }
560
561 ino = iocur_top->ino;
562 dirino = iocur_top->dirino;
563 mode = iocur_top->mode;
564 pop_cur();
565 push_cur();
566
567 if (bbmap) {
568 #ifdef DEBUG_BBMAP
569 int i;
570 printf(_("xfs_db got a bbmap for %lld\n"), (long long)blknum);
571 printf(_("\tblock map"));
572 for (i = 0; i < bbmap->nmaps; i++)
573 printf(" %lld:%d", (long long)bbmap->b[i].bm_bn,
574 bbmap->b[i].bm_len);
575 printf("\n");
576 #endif
577 iocur_top->bbmap = malloc(sizeof(struct bbmap));
578 if (!iocur_top->bbmap)
579 return;
580 memcpy(iocur_top->bbmap, bbmap, sizeof(struct bbmap));
581 error = -libxfs_buf_read_map(btargp, bbmap->b,
582 bbmap->nmaps, LIBXFS_READBUF_SALVAGE, &bp,
583 ops);
584 } else {
585 error = -libxfs_buf_read(btargp, blknum, len,
586 LIBXFS_READBUF_SALVAGE, &bp, ops);
587 iocur_top->bbmap = NULL;
588 }
589
590 /*
591 * Salvage mode means that we still get a buffer even if the verifier
592 * says the metadata is corrupt. Therefore, the only errors we should
593 * get are for IO errors or runtime errors.
594 */
595 if (error)
596 return;
597 iocur_top->buf = bp->b_addr;
598 iocur_top->bp = bp;
599 if (!ops) {
600 bp->b_ops = NULL;
601 bp->b_flags |= LIBXFS_B_UNCHECKED;
602 }
603
604 iocur_top->bb = blknum;
605 iocur_top->blen = len;
606 iocur_top->boff = 0;
607 iocur_top->data = iocur_top->buf;
608 iocur_top->len = BBTOB(len);
609 iocur_top->off = blknum << BBSHIFT;
610 iocur_top->typ = cur_typ = type;
611 iocur_top->ino = ino;
612 iocur_top->dirino = dirino;
613 iocur_top->mode = mode;
614 iocur_top->ino_buf = 0;
615 iocur_top->dquot_buf = 0;
616
617 /* store location in ring */
618 if (ring_flag)
619 ring_add();
620 }
621
622 void
623 set_cur(
624 const typ_t *type,
625 xfs_daddr_t blknum,
626 int len,
627 int ring_flag,
628 bbmap_t *bbmap)
629 {
630 __set_cur(mp->m_ddev_targp, type, blknum, len, ring_flag, bbmap);
631 }
632
633 void
634 set_log_cur(
635 const typ_t *type,
636 xfs_daddr_t blknum,
637 int len,
638 int ring_flag,
639 bbmap_t *bbmap)
640 {
641 if (mp->m_logdev_targp->bt_bdev == mp->m_ddev_targp->bt_bdev) {
642 fprintf(stderr, "no external log specified\n");
643 exitcode = 1;
644 return;
645 }
646
647 __set_cur(mp->m_logdev_targp, type, blknum, len, ring_flag, bbmap);
648 }
649
650
651 void
652 set_iocur_type(
653 const typ_t *type)
654 {
655 int bb_count = 1; /* type's size in basic blocks */
656 int boff = iocur_top->boff;
657
658 /*
659 * Inodes are special; verifier checks all inodes in the chunk, the
660 * set_cur_inode() will help that
661 */
662 if (type->typnm == TYP_INODE) {
663 xfs_daddr_t b = iocur_top->bb;
664 xfs_agblock_t agbno;
665 xfs_agino_t agino;
666 xfs_ino_t ino;
667
668 agbno = xfs_daddr_to_agbno(mp, b);
669 agino = XFS_OFFBNO_TO_AGINO(mp, agbno,
670 iocur_top->boff / mp->m_sb.sb_inodesize);
671 ino = XFS_AGINO_TO_INO(mp, xfs_daddr_to_agno(mp, b), agino);
672 set_cur_inode(ino);
673 return;
674 }
675
676 /* adjust buffer size for types with fields & hence fsize() */
677 if (type->fields)
678 bb_count = BTOBB(byteize(fsize(type->fields,
679 iocur_top->data, 0, 0)));
680 set_cur(type, iocur_top->bb, bb_count, DB_RING_IGN, NULL);
681 set_cur_boff(boff);
682 }
683
684 static void
685 stack_help(void)
686 {
687 dbprintf(_(
688 "\n"
689 " The stack is used to explicitly store your location and data type\n"
690 " for later return. The 'push' operation stores the current address\n"
691 " and type on the stack, the 'pop' operation returns you to the\n"
692 " position and datatype of the top entry on the stack.\n"
693 "\n"
694 " The 'stack' allows explicit location saves, see 'ring' for implicit\n"
695 " position tracking.\n"
696 "\n"
697 ));
698 }
699
700 /*ARGSUSED*/
701 static int
702 stack_f(
703 int argc,
704 char **argv)
705 {
706 int i;
707 char tagbuf[14];
708
709 for (i = iocur_sp; i > 0; i--) {
710 snprintf(tagbuf, sizeof(tagbuf), "%d: ", i);
711 print_iocur(tagbuf, &iocur_base[i]);
712 }
713 return 0;
714 }