1 /* 2 * extent_inode.c --- direct extent tree manipulation 3 * 4 * Copyright (C) 2012 Theodore Ts'o. This file may be redistributed 5 * under the terms of the GNU Public License. 6 */ 7 8 #include "config.h" 9 #include <stdio.h> 10 #include <unistd.h> 11 #include <stdlib.h> 12 #include <ctype.h> 13 #include <string.h> 14 #include <time.h> 15 #ifdef HAVE_ERRNO_H 16 #include <errno.h> 17 #endif 18 #include <sys/types.h> 19 #ifdef HAVE_GETOPT_H 20 #include <getopt.h> 21 #else 22 extern int optind; 23 extern char *optarg; 24 #endif 25 26 #include "debugfs.h" 27 28 static ext2_ino_t current_ino; 29 static ext2_extent_handle_t current_handle; 30 31 static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) 32 { 33 if (desc) 34 printf("%s: ", desc); 35 printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", 36 extent->e_lblk, extent->e_lblk + extent->e_len - 1, 37 extent->e_len, extent->e_pblk); 38 if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) 39 fputs("LEAF ", stdout); 40 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) 41 fputs("UNINIT ", stdout); 42 if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) 43 fputs("2ND_VISIT ", stdout); 44 if (!extent->e_flags) 45 fputs("(none)", stdout); 46 fputc('\n', stdout); 47 48 } 49 50 static int common_extent_args_process(int argc, char *argv[], int min_argc, 51 int max_argc, const char *cmd, 52 const char *usage, int flags) 53 { 54 if (common_args_process(argc, argv, min_argc, max_argc, cmd, 55 usage, flags)) 56 return 1; 57 58 if (!current_handle) { 59 com_err(cmd, 0, "Extent handle not open"); 60 return 1; 61 } 62 return 0; 63 } 64 65 static char *orig_prompt, *extent_prompt; 66 67 void do_extent_open(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 68 void *infop EXT2FS_ATTR((unused))) 69 { 70 ext2_ino_t inode; 71 int ret; 72 errcode_t retval; 73 char *cp; 74 75 if (check_fs_open(argv[0])) 76 return; 77 78 if (argc == 1) { 79 if (current_ino) 80 printf("Current inode is %d\n", current_ino); 81 else 82 printf("No current inode\n"); 83 return; 84 } 85 86 if (common_inode_args_process(argc, argv, &inode, 0)) 87 return; 88 89 current_ino = 0; 90 91 retval = ext2fs_extent_open(current_fs, inode, ¤t_handle); 92 if (retval) { 93 com_err(argv[1], retval, "while opening extent handle"); 94 return; 95 } 96 97 current_ino = inode; 98 99 orig_prompt = ss_get_prompt(sci_idx); 100 extent_prompt = malloc(strlen(orig_prompt) + 32); 101 if (extent_prompt == NULL) { 102 com_err(argv[1], retval, "out of memory"); 103 return; 104 } 105 106 strcpy(extent_prompt, orig_prompt); 107 cp = strchr(extent_prompt, ':'); 108 if (cp) 109 *cp = 0; 110 sprintf(extent_prompt + strlen(extent_prompt), " (extent ino %d): ", 111 current_ino); 112 ss_add_request_table(sci_idx, &extent_cmds, 1, &ret); 113 ss_set_prompt(sci_idx, extent_prompt); 114 return; 115 } 116 117 void do_extent_close(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 118 void *infop EXT2FS_ATTR((unused))) 119 { 120 int ret; 121 122 if (common_args_process(argc, argv, 1, 1, 123 "extent_close", "", 0)) 124 return; 125 126 if (!current_handle) { 127 com_err(argv[0], 0, "Extent handle not open"); 128 return; 129 } 130 131 ext2fs_extent_free(current_handle); 132 current_handle = NULL; 133 current_ino = 0; 134 ss_delete_request_table(sci_idx, &extent_cmds, &ret); 135 ss_set_prompt(sci_idx, orig_prompt); 136 free(extent_prompt); 137 extent_prompt = NULL; 138 } 139 140 static void generic_goto_node(const char *my_name, int argc, 141 char **argv, int op) 142 { 143 struct ext2fs_extent extent; 144 errcode_t retval; 145 146 if (my_name && common_args_process(argc, argv, 1, 1, 147 my_name, "", 0)) 148 return; 149 150 if (!current_handle) { 151 com_err(argv[0], 0, "Extent handle not open"); 152 return; 153 } 154 155 retval = ext2fs_extent_get(current_handle, op, &extent); 156 if (retval) { 157 com_err(argv[0], retval, 0); 158 return; 159 } 160 dbg_print_extent(0, &extent); 161 } 162 163 void do_current_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 164 void *infop EXT2FS_ATTR((unused))) 165 { 166 generic_goto_node("current_node", argc, argv, EXT2_EXTENT_CURRENT); 167 } 168 169 void do_root_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 170 void *infop EXT2FS_ATTR((unused))) 171 { 172 generic_goto_node("root_node", argc, argv, EXT2_EXTENT_ROOT); 173 } 174 175 void do_last_leaf(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 176 void *infop EXT2FS_ATTR((unused))) 177 { 178 generic_goto_node("last_leaf", argc, argv, EXT2_EXTENT_LAST_LEAF); 179 } 180 181 void do_first_sib(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 182 void *infop EXT2FS_ATTR((unused))) 183 { 184 generic_goto_node("first_sib", argc, argv, EXT2_EXTENT_FIRST_SIB); 185 } 186 187 void do_last_sib(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 188 void *infop EXT2FS_ATTR((unused))) 189 { 190 generic_goto_node("next_sib", argc, argv, EXT2_EXTENT_LAST_SIB); 191 } 192 193 void do_next_sib(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 194 void *infop EXT2FS_ATTR((unused))) 195 { 196 generic_goto_node("next_sib", argc, argv, EXT2_EXTENT_NEXT_SIB); 197 } 198 199 void do_prev_sib(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 200 void *infop EXT2FS_ATTR((unused))) 201 { 202 generic_goto_node("prev_sib", argc, argv, EXT2_EXTENT_PREV_SIB); 203 } 204 205 void do_next_leaf(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 206 void *infop EXT2FS_ATTR((unused))) 207 { 208 generic_goto_node("next_leaf", argc, argv, EXT2_EXTENT_NEXT_LEAF); 209 } 210 211 void do_prev_leaf(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 212 void *infop EXT2FS_ATTR((unused))) 213 { 214 generic_goto_node("prev_leaf", argc, argv, EXT2_EXTENT_PREV_LEAF); 215 } 216 217 void do_next(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 218 void *infop EXT2FS_ATTR((unused))) 219 { 220 generic_goto_node("next", argc, argv, EXT2_EXTENT_NEXT); 221 } 222 223 void do_prev(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 224 void *infop EXT2FS_ATTR((unused))) 225 { 226 generic_goto_node("prev", argc, argv, EXT2_EXTENT_PREV); 227 } 228 229 void do_up(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 230 void *infop EXT2FS_ATTR((unused))) 231 { 232 generic_goto_node("up", argc, argv, EXT2_EXTENT_UP); 233 } 234 235 void do_down(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 236 void *infop EXT2FS_ATTR((unused))) 237 { 238 generic_goto_node("down", argc, argv, EXT2_EXTENT_DOWN); 239 } 240 241 void do_delete_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 242 void *infop EXT2FS_ATTR((unused))) 243 { 244 struct ext2fs_extent extent; 245 errcode_t retval; 246 247 if (common_extent_args_process(argc, argv, 1, 1, "delete_node", 248 "", CHECK_FS_RW | CHECK_FS_BITMAPS)) 249 return; 250 251 retval = ext2fs_extent_delete(current_handle, 0); 252 if (retval) { 253 com_err(argv[0], retval, 0); 254 return; 255 } 256 257 retval = ext2fs_extent_get(current_handle, EXT2_EXTENT_CURRENT, 258 &extent); 259 if (retval) 260 return; 261 dbg_print_extent(0, &extent); 262 } 263 264 void do_replace_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 265 void *infop EXT2FS_ATTR((unused))) 266 { 267 const char *usage = "[--uninit] <lblk> <len> <pblk>"; 268 errcode_t retval; 269 struct ext2fs_extent extent; 270 int err; 271 272 if (common_extent_args_process(argc, argv, 3, 5, "replace_node", 273 usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) 274 return; 275 276 extent.e_flags = 0; 277 278 if (!strcmp(argv[1], "--uninit")) { 279 argc--; 280 argv++; 281 extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; 282 } 283 284 if (argc != 4) { 285 fprintf(stderr, "Usage: %s %s\n", argv[0], usage); 286 return; 287 } 288 289 err = strtoblk(argv[0], argv[1], "logical block", &extent.e_lblk); 290 if (err) 291 return; 292 293 extent.e_len = parse_ulong(argv[2], argv[0], "length", &err); 294 if (err) 295 return; 296 297 err = strtoblk(argv[0], argv[3], "physical block", &extent.e_pblk); 298 if (err) 299 return; 300 301 retval = ext2fs_extent_replace(current_handle, 0, &extent); 302 if (retval) { 303 com_err(argv[0], retval, 0); 304 return; 305 } 306 generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT); 307 } 308 309 void do_split_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 310 void *infop EXT2FS_ATTR((unused))) 311 { 312 errcode_t retval; 313 314 if (common_extent_args_process(argc, argv, 1, 1, "split_node", 315 "", CHECK_FS_RW | CHECK_FS_BITMAPS)) 316 return; 317 318 retval = ext2fs_extent_node_split(current_handle); 319 if (retval) { 320 com_err(argv[0], retval, 0); 321 return; 322 } 323 generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT); 324 } 325 326 void do_insert_node(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)), 327 void *infop EXT2FS_ATTR((unused))) 328 { 329 const char *usage = "[--after] [--uninit] <lblk> <len> <pblk>"; 330 errcode_t retval; 331 struct ext2fs_extent extent; 332 char *cmd; 333 int err; 334 int flags = 0; 335 336 if (common_extent_args_process(argc, argv, 3, 6, "insert_node", 337 usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) 338 return; 339 340 cmd = argv[0]; 341 342 extent.e_flags = 0; 343 344 while (argc > 2) { 345 if (!strcmp(argv[1], "--after")) { 346 argc--; 347 argv++; 348 flags |= EXT2_EXTENT_INSERT_AFTER; 349 continue; 350 } 351 if (!strcmp(argv[1], "--uninit")) { 352 argc--; 353 argv++; 354 extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; 355 continue; 356 } 357 break; 358 } 359 360 if (argc != 4) { 361 fprintf(stderr, "usage: %s %s\n", cmd, usage); 362 return; 363 } 364 365 err = strtoblk(cmd, argv[1], "logical block", &extent.e_lblk); 366 if (err) 367 return; 368 369 extent.e_len = parse_ulong(argv[2], cmd, "length", &err); 370 if (err) 371 return; 372 373 err = strtoblk(cmd, argv[3], "physical block", &extent.e_pblk); 374 if (err) 375 return; 376 377 retval = ext2fs_extent_insert(current_handle, flags, &extent); 378 if (retval) { 379 com_err(cmd, retval, 0); 380 return; 381 } 382 generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT); 383 } 384 385 void do_set_bmap(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 386 void *infop EXT2FS_ATTR((unused))) 387 { 388 const char *usage = "[--uninit] <lblk> <pblk>"; 389 struct ext2fs_extent extent; 390 errcode_t retval; 391 blk64_t logical; 392 blk64_t physical; 393 char *cmd = argv[0]; 394 int flags = 0; 395 int err; 396 397 if (common_extent_args_process(argc, argv, 3, 5, "set_bmap", 398 usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) 399 return; 400 401 if (argc > 2 && !strcmp(argv[1], "--uninit")) { 402 argc--; 403 argv++; 404 flags |= EXT2_EXTENT_SET_BMAP_UNINIT; 405 } 406 407 if (argc != 3) { 408 fprintf(stderr, "Usage: %s %s\n", cmd, usage); 409 return; 410 } 411 412 err = strtoblk(cmd, argv[1], "logical block", &logical); 413 if (err) 414 return; 415 416 err = strtoblk(cmd, argv[2], "physical block", &physical); 417 if (err) 418 return; 419 420 retval = ext2fs_extent_set_bmap(current_handle, logical, 421 physical, flags); 422 if (retval) { 423 com_err(cmd, retval, 0); 424 return; 425 } 426 427 retval = ext2fs_extent_get(current_handle, EXT2_EXTENT_CURRENT, 428 &extent); 429 if (retval) 430 return; 431 dbg_print_extent(0, &extent); 432 } 433 434 void do_print_all(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 435 void *infop EXT2FS_ATTR((unused))) 436 { 437 const char *usage = "[--leaf-only|--reverse|--reverse-leaf]"; 438 struct ext2fs_extent extent; 439 errcode_t retval; 440 errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT; 441 int op = EXT2_EXTENT_NEXT; 442 int first_op = EXT2_EXTENT_ROOT; 443 444 445 if (common_extent_args_process(argc, argv, 1, 2, "print_all", 446 usage, 0)) 447 return; 448 449 if (argc == 2) { 450 if (!strcmp(argv[1], "--leaf-only")) 451 op = EXT2_EXTENT_NEXT_LEAF; 452 else if (!strcmp(argv[1], "--reverse")) { 453 op = EXT2_EXTENT_PREV; 454 first_op = EXT2_EXTENT_LAST_LEAF; 455 end_err = EXT2_ET_EXTENT_NO_PREV; 456 } else if (!strcmp(argv[1], "--reverse-leaf")) { 457 op = EXT2_EXTENT_PREV_LEAF; 458 first_op = EXT2_EXTENT_LAST_LEAF; 459 end_err = EXT2_ET_EXTENT_NO_PREV; 460 } else { 461 fprintf(stderr, "Usage: %s %s\n", argv[0], usage); 462 return; 463 } 464 } 465 466 retval = ext2fs_extent_get(current_handle, first_op, &extent); 467 if (retval) { 468 com_err(argv[0], retval, 0); 469 return; 470 } 471 dbg_print_extent(0, &extent); 472 473 while (1) { 474 retval = ext2fs_extent_get(current_handle, op, &extent); 475 if (retval == end_err) 476 break; 477 478 if (retval) { 479 com_err(argv[0], retval, 0); 480 return; 481 } 482 dbg_print_extent(0, &extent); 483 } 484 } 485 486 void do_fix_parents(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 487 void *infop EXT2FS_ATTR((unused))) 488 { 489 errcode_t retval; 490 491 if (common_extent_args_process(argc, argv, 1, 1, "fix_parents", "", 492 CHECK_FS_RW)) 493 return; 494 495 retval = ext2fs_extent_fix_parents(current_handle); 496 if (retval) { 497 com_err(argv[0], retval, 0); 498 return; 499 } 500 } 501 502 void do_info(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 503 void *infop EXT2FS_ATTR((unused))) 504 { 505 struct ext2fs_extent extent; 506 struct ext2_extent_info info; 507 errcode_t retval; 508 509 if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0)) 510 return; 511 512 retval = ext2fs_extent_get_info(current_handle, &info); 513 if (retval) { 514 com_err(argv[0], retval, 0); 515 return; 516 } 517 518 retval = ext2fs_extent_get(current_handle, 519 EXT2_EXTENT_CURRENT, &extent); 520 if (retval) { 521 com_err(argv[0], retval, 0); 522 return; 523 } 524 525 dbg_print_extent(0, &extent); 526 527 printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n", 528 info.curr_entry, info.num_entries, info.max_entries, 529 info.bytes_avail, info.curr_level, info.max_depth); 530 printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk, 531 info.max_pblk); 532 printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len, 533 info.max_uninit_len); 534 } 535 536 void do_goto_block(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)), 537 void *infop EXT2FS_ATTR((unused))) 538 { 539 errcode_t retval; 540 blk64_t blk; 541 int level = 0, err; 542 543 if (common_extent_args_process(argc, argv, 2, 3, "goto_block", 544 "block [level]", 0)) 545 return; 546 547 if (strtoblk(argv[0], argv[1], NULL, &blk)) 548 return; 549 550 if (argc == 3) { 551 level = parse_ulong(argv[2], argv[0], "level", &err); 552 if (err) 553 return; 554 } 555 556 retval = ext2fs_extent_goto2(current_handle, level, (blk64_t) blk); 557 558 if (retval) { 559 com_err(argv[0], retval, 560 "while trying to go to block %llu, level %d", 561 (unsigned long long) blk, level); 562 return; 563 } 564 565 generic_goto_node(NULL, argc, argv, EXT2_EXTENT_CURRENT); 566 } 567