提交 f6a13699 authored 作者: Thomas Mueller's avatar Thomas Mueller

A new encrypting file system has been implemented.

上级 8b34bde2
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.io.IOException;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemWrapper;
/**
* This file system encrypts the data.
*/
public class FileSystemCrypt extends FileSystemWrapper {
private static final String PREFIX = "aes:";
private static final int HEADER_LENGTH = 4096;
static {
FileSystem.register(new FileSystemCrypt());
}
protected String getPrefix() {
return PREFIX;
}
public long length(String fileName) {
return super.length(fileName) - HEADER_LENGTH;
}
public FileObject openFileObject(String fileName, String mode) {
return null;
}
/**
* An encrypted file.
*/
static class FileObjectCrypt implements FileObject {
// private FileObject
public void close() throws IOException {
// TODO Auto-generated method stub
}
public long getFilePointer() throws IOException {
// TODO Auto-generated method stub
return 0;
}
public String getName() {
// TODO Auto-generated method stub
return null;
}
public long length() throws IOException {
// TODO Auto-generated method stub
return 0;
}
public void readFully(byte[] b, int off, int len) throws IOException {
// TODO Auto-generated method stub
}
public void releaseLock() {
// TODO Auto-generated method stub
}
public void seek(long pos) throws IOException {
// TODO Auto-generated method stub
}
public void setFileLength(long newLength) throws IOException {
// TODO Auto-generated method stub
}
public void sync() throws IOException {
// TODO Auto-generated method stub
}
public boolean tryLock() {
// TODO Auto-generated method stub
return false;
}
public void write(byte[] b, int off, int len) throws IOException {
// TODO Auto-generated method stub
}
}
}
...@@ -18,6 +18,7 @@ import java.sql.ResultSet; ...@@ -18,6 +18,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Random; import java.util.Random;
import org.h2.dev.fs.FileSystemCrypt;
import org.h2.store.fs.FileObject; import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemMemory; import org.h2.store.fs.FileSystemMemory;
...@@ -58,10 +59,12 @@ public class TestFileSystem extends TestBase { ...@@ -58,10 +59,12 @@ public class TestFileSystem extends TestBase {
testUserHome(); testUserHome();
fs.unregister(); fs.unregister();
try { try {
FileSystemCrypt.register();
testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
testFileSystem("nio:" + getBaseDir() + "/fs");
testFileSystem("nioMapped:" + getBaseDir() + "/fs");
if (!config.splitFileSystem) { if (!config.splitFileSystem) {
testFileSystem("split:" + getBaseDir() + "/fs"); testFileSystem("split:" + getBaseDir() + "/fs");
testFileSystem("nio:" + getBaseDir() + "/fs");
testFileSystem("nioMapped:" + getBaseDir() + "/fs");
testFileSystem("split:nioMapped:" + getBaseDir() + "/fs"); testFileSystem("split:nioMapped:" + getBaseDir() + "/fs");
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -192,6 +195,7 @@ public class TestFileSystem extends TestBase { ...@@ -192,6 +195,7 @@ public class TestFileSystem extends TestBase {
Random random = new Random(1); Random random = new Random(1);
random.nextBytes(buffer); random.nextBytes(buffer);
fo.write(buffer, 0, 10000); fo.write(buffer, 0, 10000);
assertEquals(10000, fo.length());
fo.seek(20000); fo.seek(20000);
assertEquals(20000, fo.getFilePointer()); assertEquals(20000, fo.getFilePointer());
try { try {
...@@ -205,7 +209,9 @@ public class TestFileSystem extends TestBase { ...@@ -205,7 +209,9 @@ public class TestFileSystem extends TestBase {
assertEquals(fsBase, fs.getParent(fo.getName()).replace('\\', '/')); assertEquals(fsBase, fs.getParent(fo.getName()).replace('\\', '/'));
fo.tryLock(); fo.tryLock();
fo.releaseLock(); fo.releaseLock();
assertEquals(10000, fo.length());
fo.close(); fo.close();
assertEquals(10000, fs.length(fsBase + "/test"));
fo = fs.openFileObject(fsBase + "/test", "r"); fo = fs.openFileObject(fsBase + "/test", "r");
byte[] test = new byte[10000]; byte[] test = new byte[10000];
fo.readFully(test, 0, 10000); fo.readFully(test, 0, 10000);
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.fs;
import java.io.EOFException;
import java.io.IOException;
import org.h2.engine.Constants;
import org.h2.security.BlockCipher;
import org.h2.security.CipherFactory;
import org.h2.security.SHA256;
import org.h2.store.fs.FileObject;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
* An encrypted file.
*/
public class FileObjectCrypt implements FileObject {
static final int HEADER_LENGTH = 4096;
// TODO header
private static final byte[] HEADER = "-- H2 crypt --\n\0".getBytes();
private static final int SALT_POS = HEADER.length;
private static final int SALT_LENGTH = 16;
private static final int HASH_ITERATIONS = Constants.ENCRYPTION_KEY_HASH_ITERATIONS;
private static final int BLOCK_SIZE = Constants.FILE_BLOCK_SIZE;
private final String name;
private final FileObject file;
private final BlockCipher cipher, cipherForInitVector;
private byte[] bufferForInitVector;
public FileObjectCrypt(String name, String algorithm, String password, FileObject file) throws IOException {
this.name = name;
this.file = file;
boolean newFile = file.length() < 2 * HEADER_LENGTH;
byte[] filePasswordHash;
if (algorithm.endsWith("-hash")) {
filePasswordHash = StringUtils.convertStringToBytes(password);
algorithm = algorithm.substring(0, algorithm.length() - "-hash".length());
} else {
filePasswordHash = SHA256.getKeyPasswordHash("file", password.toCharArray());
}
cipher = CipherFactory.getBlockCipher(algorithm);
cipherForInitVector = CipherFactory.getBlockCipher(algorithm);
int keyIterations = HASH_ITERATIONS;
byte[] salt;
if (newFile) {
salt = MathUtils.secureRandomBytes(SALT_LENGTH);
file.write(HEADER, 0, HEADER.length);
file.seek(SALT_POS);
file.write(salt, 0, salt.length);
} else {
salt = new byte[SALT_LENGTH];
file.seek(SALT_POS);
file.readFully(salt, 0, SALT_LENGTH);
}
byte[] key = SHA256.getHashWithSalt(filePasswordHash, salt);
for (int i = 0; i < keyIterations; i++) {
key = SHA256.getHash(key, true);
}
cipher.setKey(key);
bufferForInitVector = new byte[BLOCK_SIZE];
seek(0);
}
public long getFilePointer() throws IOException {
return Math.max(0, file.getFilePointer() - HEADER_LENGTH);
}
public String getName() {
return name;
}
public long length() throws IOException {
return Math.max(0, file.length() - 2 * HEADER_LENGTH);
}
public void releaseLock() {
file.releaseLock();
}
public void seek(long pos) throws IOException {
file.seek(pos + HEADER_LENGTH);
}
public void sync() throws IOException {
file.sync();
}
public boolean tryLock() {
return file.tryLock();
}
public void close() throws IOException {
file.close();
}
public void setFileLength(long newLength) throws IOException {
if (newLength < length()) {
int mod = (int) (newLength % BLOCK_SIZE);
if (mod == 0) {
file.setFileLength(newLength + HEADER_LENGTH);
} else {
file.setFileLength(newLength + HEADER_LENGTH + BLOCK_SIZE - mod);
byte[] buff = new byte[BLOCK_SIZE - mod];
long pos = getFilePointer();
seek(newLength);
write(buff, 0, buff.length);
seek(pos);
}
}
file.setFileLength(newLength + 2 * HEADER_LENGTH);
if (newLength < getFilePointer()) {
seek(newLength);
}
}
public void readFully(byte[] b, int off, int len) throws IOException {
long pos = getFilePointer();
long length = length();
if (pos + len > length) {
throw new EOFException("pos: " + pos + " len: " + len + " length: " + length);
}
int posMod = (int) (pos % BLOCK_SIZE);
int lenMod = len % BLOCK_SIZE;
if (posMod == 0 && lenMod == 0) {
readAligned(pos, b, off, len);
} else {
long p = pos - posMod;
int l = len + 2 * BLOCK_SIZE - lenMod;
seek(p);
byte[] temp = new byte[l];
try {
readAligned(p, temp, 0, l);
System.arraycopy(temp, posMod, b, off, len);
} finally {
seek(pos + len);
}
}
}
public void write(byte[] b, int off, int len) throws IOException {
long pos = getFilePointer();
int posMod = (int) (pos % BLOCK_SIZE);
int lenMod = len % BLOCK_SIZE;
if (posMod == 0 && lenMod == 0) {
byte[] temp = new byte[len];
System.arraycopy(b, off, temp, 0, len);
writeAligned(pos, temp, 0, len);
} else {
long p = pos - posMod;
int l = len + 2 * BLOCK_SIZE - lenMod;
seek(p);
byte[] temp = new byte[l];
if (file.length() < pos + l + HEADER_LENGTH) {
file.setFileLength(pos + l + HEADER_LENGTH);
}
readAligned(p, temp, 0, l);
System.arraycopy(b, off, temp, posMod, len);
seek(p);
try {
writeAligned(p, temp, 0, l);
} finally {
seek(pos + len);
}
}
pos = file.getFilePointer();
if (file.length() < pos + HEADER_LENGTH) {
file.setFileLength(pos + HEADER_LENGTH);
}
}
private void readAligned(long pos, byte[] b, int off, int len) throws IOException {
file.readFully(b, off, len);
for (int p = 0; p < len; p += BLOCK_SIZE) {
for (int i = 0; i < BLOCK_SIZE; i++) {
// currently, empty blocks are not decrypted
if (b[p + off + i] != 0) {
cipher.decrypt(b, p + off, BLOCK_SIZE);
xorInitVector(b, p + off, BLOCK_SIZE, p + pos);
break;
}
}
}
}
private void writeAligned(long pos, byte[] b, int off, int len) throws IOException {
for (int p = 0; p < len; p += BLOCK_SIZE) {
for (int i = 0; i < BLOCK_SIZE; i++) {
// currently, empty blocks are not decrypted
if (b[p + off + i] != 0) {
xorInitVector(b, p + off, BLOCK_SIZE, p + pos);
cipher.encrypt(b, p + off, BLOCK_SIZE);
break;
}
}
}
file.write(b, off, len);
}
private void xorInitVector(byte[] b, int off, int len, long p) {
byte[] iv = bufferForInitVector;
while (len > 0) {
for (int i = 0; i < BLOCK_SIZE; i += 8) {
long block = (p + i) >>> 3;
iv[i] = (byte) (block >> 56);
iv[i + 1] = (byte) (block >> 48);
iv[i + 2] = (byte) (block >> 40);
iv[i + 3] = (byte) (block >> 32);
iv[i + 4] = (byte) (block >> 24);
iv[i + 5] = (byte) (block >> 16);
iv[i + 6] = (byte) (block >> 8);
iv[i + 7] = (byte) block;
}
cipherForInitVector.encrypt(iv, 0, BLOCK_SIZE);
for (int i = 0; i < BLOCK_SIZE; i++) {
b[off + i] ^= iv[i];
}
p += BLOCK_SIZE;
off += BLOCK_SIZE;
len -= BLOCK_SIZE;
}
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.fs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.h2.command.dml.BackupCommand;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.message.DbException;
import org.h2.store.fs.FileObject;
import org.h2.util.IOUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
/**
* A shell tool that allows to list and manipulate files.
*/
public class FileShell extends Tool {
private boolean verbose;
private BufferedReader reader;
private PrintStream err = System.err;
private InputStream in = System.in;
private String currentWorkingDirectory;
/**
* Options are case sensitive. Supported options are:
* <table>
* <tr><td>[-help] or [-?]</td>
* <td>Print the list of options</td></tr>
* <tr><td>[-verbose]</td>
* <td>Print stack traces</td></tr>
* <tr><td>[-run ...]</td>
* <td>Execute the given commands and exit</td></tr>
* </table>
* Multiple commands may be executed if separated by ;
* @h2.resource
*
* @param args the command line arguments
*/
public static void main(String... args) throws SQLException {
new FileShell().runTool(args);
}
/**
* Sets the standard error stream.
*
* @param err the new standard error stream
*/
public void setErr(PrintStream err) {
this.err = err;
}
/**
* Redirects the standard input. By default, System.in is used.
*
* @param in the input stream to use
*/
public void setIn(InputStream in) {
this.in = in;
}
/**
* Redirects the standard input. By default, System.in is used.
*
* @param reader the input stream reader to use
*/
public void setInReader(BufferedReader reader) {
this.reader = reader;
}
/**
* Run the shell tool with the given command line settings.
*
* @param args the command line settings
*/
public void runTool(String... args) throws SQLException {
try {
currentWorkingDirectory = new File(".").getCanonicalPath();
} catch (IOException e) {
throw DbException.convertIOException(e, "cwd");
}
for (int i = 0; args != null && i < args.length; i++) {
String arg = args[i];
if (arg.equals("-run")) {
try {
execute(args[++i]);
} catch (Exception e) {
throw DbException.convert(e);
}
} else if (arg.equals("-verbose")) {
verbose = true;
} else if (arg.equals("-help") || arg.equals("-?")) {
showUsage();
return;
} else {
throwUnsupportedOption(arg);
}
}
promptLoop();
}
private void promptLoop() {
println("");
println("Welcome to H2 File Shell " + Constants.getFullVersion());
println("Exit with Ctrl+C");
showHelp();
if (reader == null) {
reader = new BufferedReader(new InputStreamReader(in));
}
println(IOUtils.getCanonicalPath(currentWorkingDirectory));
while (true) {
try {
print("> ");
String line = readLine();
if (line == null) {
break;
}
line = line.trim();
if (line.length() == 0) {
continue;
}
try {
execute(line);
} catch (Exception e) {
error(e);
}
} catch (Exception e) {
error(e);
break;
}
}
}
private void execute(String line) throws IOException {
String[] commands = StringUtils.arraySplit(line, ';', true);
for (String command : commands) {
String[] list = StringUtils.arraySplit(command, ' ', true);
if (!execute(list)) {
break;
}
}
}
private boolean execute(String[] list) throws IOException {
// TODO unit tests for everything (multiple commands, errors, ...)
// TODO less (support large files)
// TODO hex dump
int i = 0;
String c = list[i++];
if ("exit".equals(c) || "quit".equals(c)) {
end(list, i);
return false;
} else if ("help".equals(c) || "?".equals(c)) {
showHelp();
end(list, i);
} else if ("cat".equals(c)) {
String file = getFile(list[i++]);
end(list, i);
cat(file, Long.MAX_VALUE);
} else if ("cd".equals(c)) {
String dir = getFile(list[i++]);
end(list, i);
if (IOUtils.isDirectory(dir)) {
currentWorkingDirectory = dir;
println(dir);
} else {
println("Not a directory: " + dir);
}
} else if ("chmod".equals(c)) {
String mode = list[i++];
String file = getFile(list[i++]);
end(list, i);
if ("-w".equals(mode)) {
boolean success = IOUtils.setReadOnly(file);
println(success ? "Success" : "Failed");
} else {
println("Unsupported mode: " + mode);
}
} else if ("cp".equals(c)) {
String source = getFile(list[i++]);
String target = getFile(list[i++]);
end(list, i);
IOUtils.copy(source, target);
} else if ("head".equals(c)) {
String file = getFile(list[i++]);
end(list, i);
cat(file, 1024);
} else if ("ls".equals(c)) {
String dir = currentWorkingDirectory;
if (i < list.length) {
dir = getFile(list[i++]);
}
end(list, i);
println(dir);
for (String file : IOUtils.listFiles(dir)) {
StringBuilder buff = new StringBuilder();
buff.append(IOUtils.isDirectory(file) ? "d" : "-");
buff.append(IOUtils.isReadOnly(file) ? "r-" : "rw");
buff.append(' ');
buff.append(String.format("%10d", IOUtils.length(file)));
buff.append(' ');
long lastMod = IOUtils.getLastModified(file);
buff.append(new Timestamp(lastMod).toString());
buff.append(' ');
buff.append(IOUtils.getFileName(file));
println(buff.toString());
}
} else if ("mkdir".equals(c)) {
String dir = getFile(list[i++] + "/dummy");
end(list, i);
IOUtils.createDirs(dir);
} else if ("mv".equals(c)) {
String source = getFile(list[i++]);
String target = getFile(list[i++]);
end(list, i);
IOUtils.rename(source, target);
} else if ("pwd".equals(c)) {
end(list, i);
println(IOUtils.getCanonicalPath(currentWorkingDirectory));
} else if ("rm".equals(c)) {
if ("-r".equals(list[i])) {
i++;
String dir = getFile(list[i++]);
end(list, i);
IOUtils.deleteRecursive(dir, true);
} else if ("-rf".equals(list[i])) {
i++;
String dir = getFile(list[i++]);
end(list, i);
IOUtils.deleteRecursive(dir, false);
} else {
String file = getFile(list[i++]);
end(list, i);
IOUtils.delete(file);
}
} else if ("touch".equals(c)) {
String file = getFile(list[i++]);
end(list, i);
truncate(file, IOUtils.length(file));
} else if ("truncate".equals(c)) {
if ("-s".equals(list[i])) {
i++;
long length = Long.decode(list[i++]);
String file = getFile(list[i++]);
end(list, i);
truncate(file, length);
} else {
println("Unsupported option");
}
} else if ("unzip".equals(c)) {
String file = getFile(list[i++]);
end(list, i);
unzip(file, currentWorkingDirectory);
} else if ("zip".equals(c)) {
boolean recursive = false;
if ("-r".equals(list[i])) {
i++;
recursive = true;
}
String target = getFile(list[i++]);
ArrayList<String> source = New.arrayList();
readFileList(list, i, source, recursive);
zip(target, currentWorkingDirectory, source);
}
return true;
}
private void end(String[] list, int index) throws IOException {
if (list.length != index) {
throw new IOException("End of command expected, got: " + list[index]);
}
}
private void cat(String fileName, long length) {
if (!IOUtils.exists(fileName)) {
print("No such file: " + fileName);
}
if (IOUtils.isDirectory(fileName)) {
print("Is a directory: " + fileName);
}
InputStream in = null;
try {
in = IOUtils.openFileInputStream(fileName);
IOUtils.copy(in, out, length);
} catch (IOException e) {
error(e);
} finally {
IOUtils.closeSilently(in);
}
println("");
}
private void truncate(String fileName, long length) {
FileObject f = null;
try {
f = IOUtils.openFileObject(fileName, "rw");
f.setFileLength(length);
} catch (IOException e) {
error(e);
} finally {
try {
f.close();
} catch (IOException e) {
error(e);
}
}
}
private void error(Exception e) {
println("Exception: " + e.getMessage());
if (verbose) {
e.printStackTrace(err);
}
}
private void zip(String zipFileName, String base, ArrayList<String> source) {
if (IOUtils.exists(zipFileName)) {
IOUtils.delete(zipFileName);
}
OutputStream fileOut = null;
try {
fileOut = IOUtils.openFileOutputStream(zipFileName, false);
ZipOutputStream zipOut = new ZipOutputStream(fileOut);
for (String fileName : source) {
String f = IOUtils.getCanonicalPath(fileName);
if (!f.startsWith(base)) {
DbException.throwInternalError(f + " does not start with " + base);
}
if (f.endsWith(zipFileName)) {
continue;
}
if (IOUtils.isDirectory(fileName)) {
continue;
}
f = f.substring(base.length());
f = BackupCommand.correctFileName(f);
ZipEntry entry = new ZipEntry(f);
zipOut.putNextEntry(entry);
InputStream in = null;
try {
in = IOUtils.openFileInputStream(fileName);
IOUtils.copyAndCloseInput(in, zipOut);
} catch (FileNotFoundException e) {
// the file could have been deleted in the meantime
// ignore this (in this case an empty file is created)
} finally {
IOUtils.closeSilently(in);
}
zipOut.closeEntry();
}
zipOut.closeEntry();
zipOut.close();
} catch (IOException e) {
throw DbException.convertIOException(e, zipFileName);
} finally {
IOUtils.closeSilently(fileOut);
}
}
private void unzip(String zipFileName, String targetDir) {
InputStream in = null;
try {
in = IOUtils.openFileInputStream(zipFileName);
ZipInputStream zipIn = new ZipInputStream(in);
while (true) {
ZipEntry entry = zipIn.getNextEntry();
if (entry == null) {
break;
}
String fileName = entry.getName();
// restoring windows backups on linux and vice versa
fileName = fileName.replace('\\', SysProperties.FILE_SEPARATOR.charAt(0));
fileName = fileName.replace('/', SysProperties.FILE_SEPARATOR.charAt(0));
if (fileName.startsWith(SysProperties.FILE_SEPARATOR)) {
fileName = fileName.substring(1);
}
OutputStream o = null;
try {
o = IOUtils.openFileOutputStream(targetDir + SysProperties.FILE_SEPARATOR + fileName, false);
IOUtils.copy(zipIn, o);
o.close();
} finally {
IOUtils.closeSilently(o);
}
zipIn.closeEntry();
}
zipIn.closeEntry();
zipIn.close();
} catch (IOException e) {
error(e);
} finally {
IOUtils.closeSilently(in);
}
}
private int readFileList(String[] list, int i, ArrayList<String> target, boolean recursive) throws IOException {
while (i < list.length) {
String c = list[i++];
if (";".equals(c)) {
break;
}
c = getFile(c);
if (!IOUtils.exists(c)) {
throw new IOException("File not found: " + c);
}
if (recursive) {
addFilesRecursive(c, target);
} else {
target.add(c);
}
}
return i;
}
private void addFilesRecursive(String f, ArrayList<String> target) {
if (IOUtils.isDirectory(f)) {
for (String c : IOUtils.listFiles(f)) {
addFilesRecursive(c, target);
}
} else {
target.add(getFile(f));
}
}
private String getFile(String f) {
if (IOUtils.isAbsolute(f)) {
return f;
}
String unwrapped = IOUtils.unwrap(f);
String prefix = f.substring(0, f.length() - unwrapped.length());
f = prefix + currentWorkingDirectory + SysProperties.FILE_SEPARATOR + unwrapped;
return IOUtils.getCanonicalPath(f);
}
private void showHelp() {
println("Commands are case sensitive");
println("? or help Display this help");
println("cat <file> Print the contents of the file");
println("cd <dir> Change the directory");
println("chmod -w <file> Make the file read-only");
println("cp <source> <target> Copy a file");
println("head <file> Print the first few lines of the contents");
println("ls [<dir>] Print the directory contents");
println("mkdir <dir> Create a directory (including parent directories)");
println("mv <source> <target> Rename a file or directory");
println("pwd Print the current working directory");
println("rm <file> Remove a file");
println("rm -r <dir> Remove a directory, recursively");
println("rm -rf <dir> Remove a directory, recursively; force");
println("touch <file> Update the last modified date (creates the file)");
println("truncate -s <size> <file> Set the file length");
println("unzip <zip> Extract all files from the zip file");
println("zip [-r] <zip> <files..> Create a zip file (-r to recurse directories)");
println("exit Exit");
println("");
}
private String readLine() throws IOException {
String line = reader.readLine();
if (line == null) {
throw new IOException("Aborted");
}
return line;
}
private void print(String s) {
out.print(s);
out.flush();
}
private void println(String s) {
out.println(s);
out.flush();
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.message.DbException;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileObjectInputStream;
import org.h2.store.fs.FileObjectOutputStream;
import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemWrapper;
import org.h2.util.IOUtils;
/**
* A file system that encrypts the contents of the files.
*/
public class FileSystemCrypt extends FileSystemWrapper {
/**
* The prefix to use for this file system.
*/
public static final String PREFIX = "crypt:";
private static final FileSystemCrypt INSTANCE = new FileSystemCrypt();
protected String getPrefix() {
return PREFIX;
}
/**
* Register the file system.
*
* @return the instance
*/
public static FileSystemCrypt register() {
FileSystem.register(INSTANCE);
return INSTANCE;
}
public long length(String fileName) {
long len = super.length(fileName);
return Math.max(0, len - 2 * FileObjectCrypt.HEADER_LENGTH);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
String[] parsed = parse(fileName);
FileObject file = IOUtils.openFileObject(parsed[2], mode);
return new FileObjectCrypt(fileName, parsed[0], parsed[1], file);
}
public OutputStream openFileOutputStream(String fileName, boolean append) {
try {
FileObject file = openFileObject(fileName, "rw");
return new FileObjectOutputStream(file, append);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public InputStream openFileInputStream(String fileName) {
try {
FileObject file = openFileObject(fileName, "r");
return new FileObjectInputStream(file);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public String getParent(String fileName) {
String[] parsed = parse(fileName);
return combine(parsed[0], parsed[1], IOUtils.getParent(parsed[2]));
}
public String[] listFiles(String directory) {
String[] parsed = parse(directory);
String[] array = IOUtils.listFiles(parsed[2]);
for (int i = 0; i < array.length; i++) {
array[i] = combine(parsed[0], parsed[1], array[i]);
}
return array;
}
public String getCanonicalPath(String fileName) {
String[] parsed = parse(fileName);
return combine(parsed[0], parsed[1], IOUtils.getCanonicalPath(parsed[2]));
}
public String unwrap(String fileName) {
return parse(fileName)[2];
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
String[] parsed = parse(prefix);
return combine(parsed[0], parsed[1], IOUtils.createTempFile(parsed[2], suffix, deleteOnExit, inTempDir));
}
/**
* Combine the parameters into a file name.
*
* @param algorithm the encryption algorithm
* @param password the password
* @param fileName the base file name
* @return the combined file name
*/
private String combine(String algorithm, String password, String fileName) {
return PREFIX + algorithm + ":" + password + ":" + fileName;
}
/**
* Split the file name into algorithm, password, and base file name.
*
* @param fileName the file name
* @return an array with algorithm, password, and base file name
*/
private String[] parse(String fileName) {
if (!fileName.startsWith(PREFIX)) {
DbException.throwInternalError(fileName + " doesn't start with " + PREFIX);
}
fileName = fileName.substring(PREFIX.length());
int idx = fileName.indexOf(':');
String algorithm, password;
if (idx < 0) {
DbException.throwInternalError(fileName + " doesn't contain encryption algorithm and password");
}
algorithm = fileName.substring(0, idx);
fileName = fileName.substring(idx + 1);
idx = fileName.indexOf(':');
if (idx < 0) {
DbException.throwInternalError(fileName + " doesn't contain encryption password");
}
password = fileName.substring(0, idx);
fileName = fileName.substring(idx + 1);
return new String[] { algorithm, password, fileName };
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version 1.0,
and under the Eclipse Public License, Version 1.0
(http://h2database.com/html/license.html).
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
An encrypting file system.
</p></body></html>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论