?? recover.java
字號(hào):
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.tools;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.h2.command.Parser;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.MetaRecord;
import org.h2.log.LogFile;
import org.h2.message.Message;
import org.h2.result.SimpleRow;
import org.h2.security.SHA256;
import org.h2.store.DataHandler;
import org.h2.store.DataPage;
import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache;
import org.h2.value.Value;
import org.h2.value.ValueLob;
/**
* Dumps the contents of a database file to a human readable text file. This
* text file can be used to recover most of the data. This tool does not open
* the database and can be used even if the database files are corrupted. A
* database can get corrupted if there is a bug in the database engine or file
* system software, or if an application writes into the database file that
* doesn't understand the the file format, or if there is a hardware problem.
*/
public class Recover implements DataHandler {
private String databaseName;
private boolean textStorage;
private int block;
private int blockCount;
private int storageId;
private int recordLength;
private int valueId;
private boolean log;
private boolean lobFilesInDirectories;
private void showUsage() {
System.out.println("java "+getClass().getName()+" [-dir <dir>] [-db <database>] [-log true]");
System.out.println("See also http://h2database.com/javadoc/org/h2/tools/Recover.html");
}
/**
* The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",...
* Options are case sensitive. The following options are supported:
* <ul>
* <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory)
* </li><li>-db database name (all databases if no name is specified)
* </li><li>-log {true|false} (log additional messages)
* </li></ul>
*
* @param args the command line arguments
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
new Recover().run(args);
}
private void run(String[] args) throws SQLException {
String dir = ".";
String db = null;
boolean removePassword = false;
for (int i = 0; args != null && i < args.length; i++) {
if ("-dir".equals(args[i])) {
dir = args[++i];
} else if ("-db".equals(args[i])) {
db = args[++i];
} else if ("-removePassword".equals(args[i])) {
removePassword = true;
log = true;
} else if ("-log".equals(args[i])) {
log = Boolean.valueOf(args[++i]).booleanValue();
} else {
showUsage();
return;
}
}
if (removePassword) {
removePassword(dir, db);
} else {
process(dir, db);
}
}
/**
* INTERNAL
*/
public static Reader readClob(String fileName) throws IOException {
return new BufferedReader(new InputStreamReader(readBlob(fileName)));
}
/**
* INTERNAL
*/
public static InputStream readBlob(String fileName) throws IOException {
return new BufferedInputStream(new FileInputStream(fileName));
}
private void removePassword(String dir, String db) throws SQLException {
ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
for (int i = 0; i < list.size(); i++) {
String fileName = (String) list.get(i);
if (fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
removePassword(fileName);
}
}
}
private void log(String message) {
if (log) {
System.out.println(message);
}
}
private void logError(String message, Throwable t) {
System.out.println(message + ": " + t.toString());
if (log) {
t.printStackTrace();
}
}
private void removePassword(String fileName) throws SQLException {
setDatabaseName(fileName.substring(fileName.length() - Constants.SUFFIX_DATA_FILE.length()));
textStorage = Database.isTextStorage(fileName, false);
byte[] magic = Database.getMagic(textStorage);
FileStore store = FileStore.open(null, fileName, "rw", magic);
long length = store.length();
int offset = FileStore.HEADER_LENGTH;
int blockSize = DiskFile.BLOCK_SIZE;
int blocks = (int) (length / blockSize);
blockCount = 1;
for (int block = 0; block < blocks; block += blockCount) {
store.seek(offset + (long) block * blockSize);
byte[] bytes = new byte[blockSize];
DataPage s = DataPage.create(this, bytes);
long start = store.getFilePointer();
store.readFully(bytes, 0, blockSize);
blockCount = s.readInt();
storageId = -1;
recordLength = -1;
valueId = -1;
if (blockCount == 0) {
// free block
blockCount = 1;
continue;
} else if (blockCount < 0) {
blockCount = 1;
continue;
}
try {
s.checkCapacity(blockCount * blockSize);
} catch (OutOfMemoryError e) {
blockCount = 1;
continue;
}
if (blockCount > 1) {
store.readFully(s.getBytes(), blockSize, blockCount * blockSize - blockSize);
}
try {
s.check(blockCount * blockSize);
} catch (SQLException e) {
blockCount = 1;
continue;
}
storageId = s.readInt();
if (storageId != 0) {
continue;
}
recordLength = s.readInt();
if (recordLength <= 0) {
continue;
}
Value[] data;
try {
data = new Value[recordLength];
} catch (Throwable e) {
continue;
}
for (valueId = 0; valueId < recordLength; valueId++) {
try {
data[valueId] = s.readValue();
} catch (Throwable e) {
continue;
}
}
if (storageId == 0) {
try {
String sql = data[3].getString();
if (!sql.startsWith("CREATE USER ")) {
continue;
}
int idx = sql.indexOf("SALT");
if (idx < 0) {
continue;
}
String userName = sql.substring("CREATE USER ".length(), idx - 1);
if (userName.startsWith("\"")) {
// TODO doesn't work for all cases ("" inside user name)
userName = userName.substring(1, userName.length() - 1);
}
SHA256 sha = new SHA256();
byte[] userPasswordHash = sha.getKeyPasswordHash(userName, "".toCharArray());
byte[] salt = RandomUtils.getSecureBytes(Constants.SALT_LEN);
byte[] passwordHash = sha.getHashWithSalt(userPasswordHash, salt);
boolean admin = sql.indexOf("ADMIN") >= 0;
StringBuffer buff = new StringBuffer();
buff.append("CREATE USER ");
buff.append(Parser.quoteIdentifier(userName));
buff.append(" SALT '");
buff.append(ByteUtils.convertBytesToString(salt));
buff.append("' HASH '");
buff.append(ByteUtils.convertBytesToString(passwordHash));
buff.append("'");
if (admin) {
buff.append(" ADMIN");
}
byte[] replacement = buff.toString().getBytes();
int at = ByteUtils.indexOf(s.getBytes(), "CREATE USER ".getBytes(), 0);
System.arraycopy(replacement, 0, s.getBytes(), at, replacement.length);
s.fill(blockCount * blockSize);
s.updateChecksum();
store.seek(start);
store.write(s.getBytes(), 0, s.length());
if (log) {
System.out.println("User: " + userName);
}
break;
} catch (Throwable e) {
e.printStackTrace();
}
}
}
closeSilently(store);
}
/**
* Dumps the database.
*
* @param dir the directory
* @param db the database name (null for all databases)
* @throws SQLException
*/
public static void execute(String dir, String db) throws SQLException {
new Recover().process(dir, db);
}
private void process(String dir, String db) throws SQLException {
ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
for (int i = 0; i < list.size(); i++) {
String fileName = (String) list.get(i);
if (fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
dumpData(fileName);
} else if (fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
dumpIndex(fileName);
} else if (fileName.endsWith(Constants.SUFFIX_LOG_FILE)) {
dumpLog(fileName);
} else if (fileName.endsWith(Constants.SUFFIX_LOB_FILE)) {
dumpLob(fileName, true);
dumpLob(fileName, false);
}
}
}
private PrintWriter getWriter(String fileName, String suffix) throws IOException, SQLException {
fileName = fileName.substring(0, fileName.length() - 3);
String outputFile = fileName + suffix;
log("Created file: " + outputFile);
return new PrintWriter(new BufferedWriter(FileUtils.openFileWriter(outputFile, false)));
}
private void writeDataError(PrintWriter writer, String error, byte[] data, int dumpBlocks) throws IOException {
writer.println("-- ERROR:" + error + " block:" + block + " blockCount:" + blockCount + " storageId:"
+ storageId + " recordLength: " + recordLength + " valueId:" + valueId);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < dumpBlocks * DiskFile.BLOCK_SIZE; i++) {
int x = (data[i] & 0xff);
if (x >= ' ' && x < 128) {
sb.append((char) x);
} else {
sb.append('?');
}
}
writer.println("-- dump: " + sb.toString());
sb = new StringBuffer();
for (int i = 0; i < dumpBlocks * DiskFile.BLOCK_SIZE; i++) {
int x = (data[i] & 0xff);
sb.append(' ');
if (x < 16) {
sb.append('0');
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -