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