(final List<String> corruptFiles, final Configuration conf,
final String backup)
| 478 | } |
| 479 | |
| 480 | private static void recoverFiles(final List<String> corruptFiles, final Configuration conf, |
| 481 | final String backup) |
| 482 | throws IOException { |
| 483 | |
| 484 | byte[] magicBytes = OrcFile.MAGIC.getBytes(StandardCharsets.UTF_8); |
| 485 | int magicLength = magicBytes.length; |
| 486 | int readSize = conf.getInt(RECOVER_READ_SIZE, DEFAULT_BLOCK_SIZE); |
| 487 | |
| 488 | for (String corruptFile : corruptFiles) { |
| 489 | System.err.println("Recovering file " + corruptFile); |
| 490 | Path corruptPath = new Path(corruptFile); |
| 491 | FileSystem fs = corruptPath.getFileSystem(conf); |
| 492 | FSDataInputStream fdis = fs.open(corruptPath); |
| 493 | try { |
| 494 | long corruptFileLen = fs.getFileStatus(corruptPath).getLen(); |
| 495 | long remaining = corruptFileLen; |
| 496 | List<Long> footerOffsets = new ArrayList<>(); |
| 497 | |
| 498 | // start reading the data file form top to bottom and record the valid footers |
| 499 | while (remaining > 0 && corruptFileLen > (2L * magicLength)) { |
| 500 | int toRead = (int) Math.min(readSize, remaining); |
| 501 | long startPos = corruptFileLen - remaining; |
| 502 | byte[] data; |
| 503 | if (startPos == 0) { |
| 504 | data = new byte[toRead]; |
| 505 | fdis.readFully(startPos, data, 0, toRead); |
| 506 | } |
| 507 | else { |
| 508 | // For non-first reads, we let startPos move back magicLength bytes |
| 509 | // which prevents two adjacent reads from separating OrcFile.MAGIC |
| 510 | startPos = startPos - magicLength; |
| 511 | data = new byte[toRead + magicLength]; |
| 512 | fdis.readFully(startPos, data, 0, toRead + magicLength); |
| 513 | } |
| 514 | |
| 515 | // find all MAGIC string and see if the file is readable from there |
| 516 | int index = 0; |
| 517 | long nextFooterOffset; |
| 518 | while (index != -1) { |
| 519 | // There are two reasons for searching from index + 1 |
| 520 | // 1. to skip the OrcFile.MAGIC in the file header when the first match is made |
| 521 | // 2. When the match is successful, the index is moved backwards to search for |
| 522 | // the subsequent OrcFile.MAGIC |
| 523 | index = indexOf(data, magicBytes, index + 1); |
| 524 | if (index != -1) { |
| 525 | nextFooterOffset = startPos + index + magicBytes.length + 1; |
| 526 | if (isReadable(corruptPath, conf, nextFooterOffset)) { |
| 527 | footerOffsets.add(nextFooterOffset); |
| 528 | } |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | System.err.println("Scanning for valid footers - startPos: " + startPos + |
| 533 | " toRead: " + toRead + " remaining: " + remaining); |
| 534 | remaining = remaining - toRead; |
| 535 | } |
| 536 | |
| 537 | System.err.println("Readable footerOffsets: " + footerOffsets); |
no test coverage detected