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

Improved compatibility with the Java 7 FileSystem abstraction.

上级 6e710caa
......@@ -506,9 +506,8 @@ public class Database implements DataHandler {
throw DbException.get(ErrorCode.FILE_VERSION_ERROR_1,
"Old database: " + dataFileName + " - please convert the database to a SQL script and re-create it.");
}
if (existsPage && FileUtils.isReadOnly(pageFileName)) {
// if it is already read-only because ACCESS_MODE_DATA=r
readOnly = readOnly | FileUtils.isReadOnly(pageFileName);
if (existsPage && !FileUtils.canWrite(pageFileName)) {
readOnly = true;
}
if (readOnly) {
traceSystem = new TraceSystem(null);
......
......@@ -296,7 +296,7 @@ public class TraceSystem implements TraceWriter {
if (printWriter == null) {
try {
FileUtils.createDirectories(FileUtils.getParent(fileName));
if (FileUtils.exists(fileName) && FileUtils.isReadOnly(fileName)) {
if (FileUtils.exists(fileName) && !FileUtils.canWrite(fileName)) {
// read only database: don't log error if the trace file
// can't be opened
return false;
......
......@@ -132,6 +132,10 @@ public class FileObjectDiskMapped implements FileObject {
return pos;
}
public String toString() {
return FileSystemDiskNioMapped.PREFIX + name;
}
public synchronized long size() throws IOException {
return file.length();
}
......@@ -161,9 +165,24 @@ public class FileObjectDiskMapped implements FileObject {
if (newLength >= size()) {
return;
}
setFileLength(newLength);
}
public synchronized void setFileLength(long newLength) throws IOException {
checkFileSizeLimit(newLength);
int oldPos = pos;
unMap();
file.setLength(newLength);
for (int i = 0;; i++) {
try {
file.setLength(newLength);
break;
} catch (IOException e) {
if (i > 16 || e.toString().indexOf("user-mapped section open") < 0) {
throw e;
}
}
System.gc();
}
reMap();
pos = (int) Math.min(newLength, oldPos);
}
......@@ -176,11 +195,7 @@ public class FileObjectDiskMapped implements FileObject {
public synchronized void write(byte[] b, int off, int len) throws IOException {
// check if need to expand file
if (mapped.capacity() < pos + len) {
int oldPos = pos;
unMap();
file.setLength(pos + len);
reMap();
pos = oldPos;
setFileLength(pos + len);
}
mapped.position(pos);
mapped.put(b, off, len);
......@@ -210,8 +225,4 @@ public class FileObjectDiskMapped implements FileObject {
}
}
public String toString() {
return name;
}
}
/*
* 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.store.fs;
import java.io.IOException;
/**
* A file object that records all write operations and can re-play them.
*/
public class FileObjectRec implements FileObject {
private final FilePathRec fs;
private final FileObject file;
private final String name;
FileObjectRec(FilePathRec fs, FileObject file, String fileName) {
this.fs = fs;
this.file = file;
this.name = fileName;
}
public void close() throws IOException {
file.close();
}
public long position() throws IOException {
return file.position();
}
public long size() throws IOException {
return file.size();
}
public void readFully(byte[] b, int off, int len) throws IOException {
file.readFully(b, off, len);
}
public void position(long pos) throws IOException {
file.position(pos);
}
public void truncate(long newLength) throws IOException {
fs.log(Recorder.TRUNCATE, name, null, newLength);
file.truncate(newLength);
}
public void sync() throws IOException {
file.sync();
}
public void write(byte[] b, int off, int len) throws IOException {
byte[] buff = b;
if (off != 0 || len != b.length) {
buff = new byte[len];
System.arraycopy(b, off, buff, 0, len);
}
file.write(b, off, len);
fs.log(Recorder.WRITE, name, buff, file.position());
}
public boolean tryLock() {
return file.tryLock();
}
public void releaseLock() {
file.releaseLock();
}
}
\ No newline at end of file
/*
* 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.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
/**
* A path to a file. It similar to the Java 7 <code>java.nio.file.Path</code>,
* but simpler, and works with older versions of Java. It also implements the
* relevant methods found in <code>java.nio.file.FileSystem</code> and
* <code>FileSystems</code>
*/
public abstract class FilePath {
private static final FilePath DEFAULT = new FilePathDisk();
private static Map<String, FilePath> providers;
/**
* The prefix for temporary files. See also TestClearReferences.
*/
private static String tempRandom;
private static long tempSequence;
protected String name;
/**
* Get the file path object for the given path.
*
* @param path the path
* @return the file path object
*/
public static FilePath get(String path) {
int index = path.indexOf(':');
if (index < 2) {
// use the default provider if no prefix or
// only a single character (drive name)
return DEFAULT.getPath(path);
}
String scheme = path.substring(0, index);
registerDefaultProviders();
FilePath p = providers.get(scheme);
if (p == null) {
// provider not found - use the default
p = DEFAULT;
}
return p.getPath(path);
}
private static void registerDefaultProviders() {
if (providers == null) {
Map<String, FilePath> map = Collections.synchronizedMap(New.<String, FilePath>hashMap());
for (String c : new String[] {
"org.h2.store.fs.FilePathMem",
"org.h2.store.fs.FilePathMemLZF",
"org.h2.store.fs.FilePathSplit",
"org.h2.store.fs.FilePathNio",
"org.h2.store.fs.FilePathNioMapped",
"org.h2.store.fs.FilePathZip"
}) {
try {
FilePath p = (FilePath) Class.forName(c).newInstance();
map.put(p.getScheme(), p);
} catch (Exception e) {
// ignore - the files may be excluded in purpose
}
}
providers = map;
}
}
/**
* Register a file provider.
*
* @param provider the file provider
*/
public static void register(FilePath provider) {
registerDefaultProviders();
providers.put(provider.getScheme(), provider);
}
/**
* Unregister a file provider.
*
* @param provider the file provider
*/
public static void unregister(FilePath provider) {
registerDefaultProviders();
providers.remove(provider.getScheme());
}
/**
* Get the size of a file in bytes
*
* @return the size in bytes
*/
public abstract long size();
/**
* Rename a file if this is allowed.
*
* @param newName the new fully qualified file name
*/
public abstract void moveTo(FilePath newName);
/**
* Create a new file.
*
* @return true if creating was successful
*/
public abstract boolean createFile();
/**
* Checks if a file exists.
*
* @return true if it exists
*/
public abstract boolean exists();
/**
* Delete a file or directory if it exists.
* Directories may only be deleted if they are empty.
*/
public abstract void delete();
/**
* List the files in the given directory.
*
* @return the list of fully qualified file names
*/
public abstract List<FilePath> listFiles();
/**
* Normalize a file name.
*
* @return the normalized file name
*/
public abstract FilePath getCanonicalPath();
/**
* Get the parent directory of a file or directory.
*
* @return the parent directory name
*/
public abstract FilePath getParent();
/**
* Check if it is a file or a directory.
*
* @return true if it is a directory
*/
public abstract boolean isDirectory();
/**
* Check if the file name includes a path.
*
* @return if the file name is absolute
*/
public abstract boolean isAbsolute();
/**
* Get the last modified date of a file
*
* @return the last modified date
*/
public abstract long lastModified();
/**
* Check if the file is writable.
*
* @return if the file is writable
*/
public abstract boolean canWrite();
/**
* Create a directory (all required parent directories already exist).
*/
public abstract void createDirectory();
/**
* Get the file or directory name (the last element of the path).
*
* @return the last element of the path
*/
public String getName() {
int idx = Math.max(name.indexOf(':'), name.lastIndexOf('/'));
return idx < 0 ? name : name.substring(idx + 1);
}
/**
* Check if a file starts with a given prefix.
*
* @param prefix the prefix
* @return true if it starts with the prefix
*/
public abstract boolean fileStartsWith(String prefix);
/**
* Create an output stream to write into the file.
*
* @param append if true, the file will grow, if false, the file will be
* truncated first
* @return the output stream
*/
public abstract OutputStream newOutputStream(boolean append);
/**
* Open a random access file object.
*
* @param mode the access mode. Supported are r, rw, rws, rwd
* @return the file object
*/
public abstract FileObject openFileObject(String mode) throws IOException;
/**
* Create an input stream to read from the file.
*
* @return the input stream
*/
public abstract InputStream newInputStream() throws IOException;
/**
* Disable the ability to write.
*
* @return true if the call was successful
*/
public abstract boolean setReadOnly();
/**
* Create a new temporary file.
*
* @param suffix the suffix
* @param deleteOnExit if the file should be deleted when the virtual
* machine exists
* @param inTempDir if the file should be stored in the temporary directory
* @return the name of the created file
*/
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
while (true) {
FilePath p = getPath(name + getNextTempFileNamePart(false) + suffix);
if (p.exists() || !p.createFile()) {
// in theory, the random number could collide
getNextTempFileNamePart(true);
continue;
}
p.openFileObject("rw").close();
return p;
}
}
/**
* Get the next temporary file name part (the part in the middle).
*
* @param newRandom if the random part of the filename should change
* @return the file name part
*/
protected static synchronized String getNextTempFileNamePart(boolean newRandom) {
if (newRandom || tempRandom == null) {
byte[] prefix = new byte[8];
MathUtils.randomBytes(prefix);
tempRandom = StringUtils.convertBytesToHex(prefix) + ".";
}
return tempRandom + tempSequence++;
}
/**
* Get the string representation. The returned string can be used to
* construct a new object.
*
* @return the path as a string
*/
public String toString() {
return name;
}
/**
* Get the scheme (prefix) for this file provider.
* This is similar to <code>java.nio.file.spi.FileSystemProvider.getScheme</code>.
*
* @return the scheme
*/
public abstract String getScheme();
/**
* Convert a file to a path. This is similar to
* <code>java.nio.file.spi.FileSystemProvider.getPath</code>, but may
* return an object even if the scheme doesn't match in case of the the
* default file provider.
*
* @param path
* @return the file path
*/
public abstract FilePath getPath(String path);
/**
* Get the unwrapped file name (without wrapper prefixes if wrapping /
* delegating file systems are used).
*
* @return the unwrapped path
*/
public FilePath unwrap() {
return this;
}
}
差异被折叠。
/*
* 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.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* This file system keeps files fully in memory. There is an option to compress
* file blocks to safe memory.
*/
public class FilePathMem extends FilePath {
private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
public FilePathMem getPath(String path) {
FilePathMem p = new FilePathMem();
p.name = getCanonicalPath(path);
return p;
}
public long size() {
return getMemoryFile().length();
}
public void moveTo(FilePath newName) {
synchronized (MEMORY_FILES) {
FileObjectMemoryData f = getMemoryFile();
f.setName(newName.name);
MEMORY_FILES.remove(name);
MEMORY_FILES.put(newName.name, f);
}
}
public boolean createFile() {
synchronized (MEMORY_FILES) {
if (exists()) {
return false;
}
getMemoryFile();
}
return true;
}
public boolean exists() {
if (isRoot()) {
return true;
}
synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(name) != null;
}
}
public void delete() {
if (isRoot()) {
return;
}
synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(name);
}
}
public List<FilePath> listFiles() {
ArrayList<FilePath> list = New.arrayList();
synchronized (MEMORY_FILES) {
for (String n : MEMORY_FILES.tailMap(name).keySet()) {
if (n.startsWith(name)) {
list.add(getPath(n));
} else {
break;
}
}
return list;
}
}
public boolean setReadOnly() {
return getMemoryFile().setReadOnly();
}
public boolean canWrite() {
return getMemoryFile().canWrite();
}
public FilePathMem getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isDirectory() {
// TODO in memory file system currently
// does not really support directories
return false;
}
public boolean isAbsolute() {
// TODO relative files are not supported
return true;
}
public FilePathMem getCanonicalPath() {
return this;
}
public long lastModified() {
return getMemoryFile().getLastModified();
}
public void createDirectory() {
// TODO directories are not really supported
}
public boolean fileStartsWith(String prefix) {
prefix = getCanonicalPath(prefix);
return name.startsWith(prefix);
}
public OutputStream newOutputStream(boolean append) {
try {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, false);
return new FileObjectOutputStream(m, append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public InputStream newInputStream() {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, true);
return new FileObjectInputStream(m);
}
public FileObject openFileObject(String mode) {
FileObjectMemoryData obj = getMemoryFile();
return new FileObjectMemory(obj, "r".equals(mode));
}
private FileObjectMemoryData getMemoryFile() {
synchronized (MEMORY_FILES) {
FileObjectMemoryData m = MEMORY_FILES.get(name);
if (m == null) {
m = new FileObjectMemoryData(name, compressed());
MEMORY_FILES.put(name, m);
}
return m;
}
}
private boolean isRoot() {
return name.equals(getScheme());
}
protected boolean accepts(String fileName) {
return fileName.startsWith(getScheme());
}
public String unwrap(String fileName) {
return fileName;
}
private static String getCanonicalPath(String fileName) {
fileName = fileName.replace('\\', '/');
int idx = fileName.indexOf(':') + 1;
if (fileName.length() > idx && fileName.charAt(idx) != '/') {
fileName = fileName.substring(0, idx) + "/" + fileName.substring(idx);
}
return fileName;
}
public String getScheme() {
return "memFS";
}
boolean compressed() {
return false;
}
}
/**
* A memory file system that compresses blocks to conserve memory.
*/
class FilePathMemLZF extends FilePathMem {
boolean compressed() {
return true;
}
public String getScheme() {
return "memLZF";
}
}
/*
* 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.store.fs;
import java.io.IOException;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class uses FileChannel.
*/
public class FilePathNio extends FilePathWrapper {
/**
* Try to open a file with this name and mode.
*
* @param fileName the file name
* @param mode the open mode
* @return the file object
* @throws IOException if opening fails
*/
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskChannel(fileName, mode);
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "nio";
}
}
/*
* 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.store.fs;
import java.io.IOException;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class used memory mapped files.
*/
public class FilePathNioMapped extends FilePathNio {
protected FileObject open(String mode) throws IOException {
return new FileObjectDiskMapped(name, mode);
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "nioMapped";
}
}
/*
* 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.store.fs;
import java.io.IOException;
import java.io.OutputStream;
/**
* A file system that records all write operations and can re-play them.
*/
public class FilePathRec extends FilePathWrapper {
private static final FilePathRec INSTANCE = new FilePathRec();
private static Recorder recorder;
private boolean trace;
/**
* Register the file system.
*/
public static void register() {
FilePath.register(INSTANCE);
}
/**
* Set the recorder class.
*
* @param recorder the recorder
*/
public static void setRecorder(Recorder recorder) {
FilePathRec.recorder = recorder;
}
public boolean createFile() {
log(Recorder.CREATE_NEW_FILE, name);
return super.createFile();
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
log(Recorder.CREATE_TEMP_FILE, unwrap(name) + ":" + suffix + ":" + deleteOnExit + ":" + inTempDir);
return super.createTempFile(suffix, deleteOnExit, inTempDir);
}
public void delete() {
log(Recorder.DELETE, name);
super.delete();
}
public FileObject openFileObject(String mode) throws IOException {
return new FileObjectRec(this, super.openFileObject(mode), name);
}
public OutputStream newOutputStream(boolean append) {
log(Recorder.OPEN_OUTPUT_STREAM, name);
return super.newOutputStream(append);
}
public void moveTo(FilePath newPath) {
log(Recorder.RENAME, unwrap(name) + ":" + unwrap(newPath.name));
super.moveTo(newPath);
}
public boolean isTrace() {
return trace;
}
public void setTrace(boolean trace) {
this.trace = trace;
}
/**
* Log the operation.
*
* @param op the operation
* @param fileName the file name(s)
*/
void log(int op, String fileName) {
log(op, fileName, null, 0);
}
/**
* Log the operation.
*
* @param op the operation
* @param fileName the file name
* @param data the data or null
* @param x the value or 0
*/
void log(int op, String fileName, byte[] data, long x) {
if (recorder != null) {
recorder.log(op, fileName, data, x);
}
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "rec";
}
}
/*
* 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.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.List;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* A file system that may split files into multiple smaller files.
* (required for a FAT32 because it only support files up to 2 GB).
*/
public class FilePathSplit extends FilePathWrapper {
private static final String PART_SUFFIX = ".part";
protected String getPrefix() {
return getScheme() + ":" + parse(name)[0] + ":";
}
public FilePath unwrap(String fileName) {
return FilePath.get(parse(fileName)[1]);
}
public boolean setReadOnly() {
boolean result = false;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
result = f.setReadOnly();
} else {
break;
}
}
return result;
}
public void delete() {
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
f.delete();
} else {
break;
}
}
}
public long lastModified() {
long lastModified = 0;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
long l = f.lastModified();
lastModified = Math.max(lastModified, l);
} else {
break;
}
}
return lastModified;
}
public long size() {
long length = 0;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
length += f.size();
} else {
break;
}
}
return length;
}
public ArrayList<FilePath> listFiles() {
List<FilePath> list = getBase().listFiles();
ArrayList<FilePath> newList = New.arrayList();
for (int i = 0, size = list.size(); i < size; i++) {
FilePath f = list.get(i);
if (!f.getName().endsWith(PART_SUFFIX)) {
newList.add(wrap(f));
}
}
return newList;
}
public InputStream newInputStream() throws IOException {
InputStream input = getBase().newInputStream();
for (int i = 1;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
InputStream i2 = f.newInputStream();
input = new SequenceInputStream(input, i2);
} else {
break;
}
}
return input;
}
public FileObject openFileObject(String mode) throws IOException {
ArrayList<FileObject> list = New.arrayList();
FileObject o = getBase().openFileObject(mode);
list.add(o);
for (int i = 1;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
o = f.openFileObject(mode);
list.add(o);
} else {
break;
}
}
FileObject[] array = new FileObject[list.size()];
list.toArray(array);
long maxLength = array[0].size();
long length = maxLength;
if (array.length == 1) {
long defaultMaxLength = getDefaultMaxLength();
if (maxLength < defaultMaxLength) {
maxLength = defaultMaxLength;
}
} else {
if (maxLength == 0) {
closeAndThrow(0, array, array[0], maxLength);
}
for (int i = 1; i < array.length - 1; i++) {
o = array[i];
long l = o.size();
length += l;
if (l != maxLength) {
closeAndThrow(i, array, o, maxLength);
}
}
o = array[array.length - 1];
long l = o.size();
length += l;
if (l > maxLength) {
closeAndThrow(array.length - 1, array, o, maxLength);
}
}
FileObjectSplit fo = new FileObjectSplit(name, mode, array, length, maxLength);
return fo;
}
private long getDefaultMaxLength() {
return 1L << Integer.decode(parse(name)[0]).intValue();
}
private void closeAndThrow(int id, FileObject[] array, FileObject o, long maxLength) throws IOException {
String message = "Expected file length: " + maxLength + " got: " + o.size() + " for " + getName(id);
for (FileObject f : array) {
f.close();
}
throw new IOException(message);
}
public OutputStream newOutputStream(boolean append) {
try {
return new FileObjectOutputStream(openFileObject("rw"), append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public void moveTo(FilePath path) {
FilePathSplit newName = (FilePathSplit) path;
for (int i = 0;; i++) {
FilePath o = getBase(i);
if (o.exists()) {
o.moveTo(newName.getBase(i));
} else {
break;
}
}
}
/**
* Split the file name into size and base file name.
*
* @param fileName the file name
* @return an array with size and file name
*/
private String[] parse(String fileName) {
if (!fileName.startsWith(getScheme())) {
DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
}
fileName = fileName.substring(getScheme().length() + 1);
String size;
if (fileName.length() > 0 && Character.isDigit(fileName.charAt(0))) {
int idx = fileName.indexOf(':');
size = fileName.substring(0, idx);
try {
fileName = fileName.substring(idx + 1);
} catch (NumberFormatException e) {
// ignore
}
} else {
size = Long.toString(SysProperties.SPLIT_FILE_SIZE_SHIFT);
}
return new String[] { size, fileName };
}
/**
* Get the file name of a part file.
*
* @param id the part id
* @return the file name including the part id
*/
private FilePath getBase(int id) {
return FilePath.get(getName(id));
}
private String getName(int id) {
return id > 0 ? getBase().name + "." + id + PART_SUFFIX : getBase().name;
}
public String getScheme() {
return "split";
}
}
\ No newline at end of file
/*
* 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.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.h2.message.DbException;
/**
* The base class for wrapping / delegating file systems such as
* the split file system.
*/
public abstract class FilePathWrapper extends FilePath {
private FilePath base;
public FilePathWrapper getPath(String path) {
return create(path, unwrap(path));
}
public FilePathWrapper wrap(FilePath base) {
return create(getPrefix() + base.name, base);
}
public FilePath unwrap() {
return unwrap(name);
}
private FilePathWrapper create(String path, FilePath base) {
try {
FilePathWrapper p = getClass().newInstance();
p.name = path;
p.base = base;
return p;
} catch (Exception e) {
throw DbException.convert(e);
}
}
protected String getPrefix() {
return getScheme() + ":";
}
protected FilePath unwrap(String path) {
return FilePath.get(path.substring(getScheme().length() + 1));
}
protected FilePath getBase() {
return base;
}
public boolean canWrite() {
return base.canWrite();
}
public void createDirectory() {
base.createDirectory();
}
public boolean createFile() {
return base.createFile();
}
public void delete() {
base.delete();
}
public boolean exists() {
return base.exists();
}
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public FilePath getParent() {
return wrap(base.getParent());
}
public boolean isAbsolute() {
return base.isAbsolute();
}
public boolean isDirectory() {
return base.isDirectory();
}
public long lastModified() {
return base.lastModified();
}
public FilePath getCanonicalPath() {
return wrap(base.getCanonicalPath());
}
public List<FilePath> listFiles() {
List<FilePath> list = base.listFiles();
for (int i = 0, len = list.size(); i < len; i++) {
list.set(i, wrap(list.get(i)));
}
return list;
}
public void moveTo(FilePath newName) {
base.moveTo(((FilePathWrapper) newName).base);
}
public InputStream newInputStream() throws IOException {
return base.newInputStream();
}
public OutputStream newOutputStream(boolean append) {
return base.newOutputStream(append);
}
public FileObject openFileObject(String mode) throws IOException {
return base.openFileObject(mode);
}
public boolean setReadOnly() {
return base.setReadOnly();
}
public long size() {
return base.size();
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
return wrap(base.createTempFile(suffix, deleteOnExit, inTempDir));
}
}
/*
* 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.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* This is a read-only file system that allows
* to access databases stored in a .zip or .jar file.
*/
public class FilePathZip extends FilePath {
public FilePathZip getPath(String path) {
FilePathZip p = new FilePathZip();
p.name = path;
return p;
}
public void createDirectory() {
// ignore
}
public boolean createFile() {
throw DbException.getUnsupportedException("write");
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return FileSystemDisk.getInstance().createTempFile(prefix, suffix, deleteOnExit, true);
}
public void delete() {
throw DbException.getUnsupportedException("write");
}
public boolean exists() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile();
return file.getEntry(entryName) != null;
} catch (IOException e) {
return false;
}
}
public boolean fileStartsWith(String fileName, String prefix) {
return fileName.startsWith(prefix);
}
// public String _getName(String name) {
// name = getEntryName(name);
// if (name.endsWith("/")) {
// name = name.substring(0, name.length() - 1);
// }
// int idx = name.lastIndexOf('/');
// if (idx >= 0) {
// name = name.substring(idx + 1);
// }
// return name;
// }
public long lastModified() {
return 0;
}
public FilePath getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isAbsolute() {
return true;
}
public boolean isDirectory() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile();
Enumeration<? extends ZipEntry> en = file.entries();
while (en.hasMoreElements()) {
ZipEntry entry = en.nextElement();
String n = entry.getName();
if (n.equals(entryName)) {
return entry.isDirectory();
} else if (n.startsWith(entryName)) {
if (n.length() == entryName.length() + 1) {
if (n.equals(entryName + "/")) {
return true;
}
}
}
}
return false;
} catch (IOException e) {
return false;
}
}
public boolean canWrite() {
return false;
}
public boolean setReadOnly() {
return true;
}
public long size() {
try {
ZipFile file = openZipFile();
ZipEntry entry = file.getEntry(getEntryName());
return entry == null ? 0 : entry.getSize();
} catch (IOException e) {
return 0;
}
}
public ArrayList<FilePath> listFiles() {
String path = name;
ArrayList<FilePath> list = New.arrayList();
try {
if (path.indexOf('!') < 0) {
path += "!";
}
if (!path.endsWith("/")) {
path += "/";
}
ZipFile file = openZipFile();
String dirName = getEntryName();
String prefix = path.substring(0, path.length() - dirName.length());
Enumeration<? extends ZipEntry> en = file.entries();
while (en.hasMoreElements()) {
ZipEntry entry = en.nextElement();
String name = entry.getName();
if (!name.startsWith(dirName)) {
continue;
}
if (name.length() <= dirName.length()) {
continue;
}
int idx = name.indexOf('/', dirName.length());
if (idx < 0 || idx >= name.length() - 1) {
list.add(getPath(prefix + name));
}
}
return list;
} catch (IOException e) {
throw DbException.convertIOException(e, "listFiles " + path);
}
}
public InputStream newInputStream() throws IOException {
return new FileObjectInputStream(openFileObject("r"));
}
public FileObject openFileObject(String mode) throws IOException {
ZipFile file = openZipFile();
ZipEntry entry = file.getEntry(getEntryName());
if (entry == null) {
throw new FileNotFoundException(name);
}
return new FileObjectZip(file, entry);
}
public OutputStream newOutputStream(boolean append) {
throw DbException.getUnsupportedException("write");
}
public void moveTo(FilePath newName) {
throw DbException.getUnsupportedException("write");
}
private static String translateFileName(String fileName) {
if (fileName.startsWith("zip:")) {
fileName = fileName.substring("zip:".length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
}
public FilePath getCanonicalPath() {
return this;
}
private String getEntryName() {
int idx = name.indexOf('!');
String fileName;
if (idx <= 0) {
fileName = "";
} else {
fileName = name.substring(idx + 1);
}
fileName = fileName.replace('\\', '/');
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private ZipFile openZipFile() throws IOException {
String fileName = translateFileName(name);
return new ZipFile(fileName);
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return new FilePathDisk().getPath(name).createTempFile(suffix, deleteOnExit, true);
}
// protected boolean accepts(String fileName) {
// return fileName.startsWith(PREFIX);
// }
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public String getScheme() {
return "zip";
}
}
......@@ -143,14 +143,6 @@ public abstract class FileSystem {
*/
public abstract String[] listFiles(String directory);
/**
* Check if a file is read-only.
*
* @param fileName the file name
* @return if it is read only
*/
public abstract boolean isReadOnly(String fileName);
/**
* Normalize a file name.
*
......
......@@ -162,7 +162,7 @@ public class FileSystemDisk extends FileSystem {
dir = new File(Utils.getProperty("java.io.tmpdir", "."));
} else {
dir = new File(name).getAbsoluteFile().getParentFile();
IOUtils.mkdirs(dir);
FileUtils.createDirectories(dir.getAbsolutePath());
}
File f;
while (true) {
......@@ -207,12 +207,6 @@ public class FileSystemDisk extends FileSystem {
}
}
public boolean isReadOnly(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
return f.exists() && !canWriteInternal(f);
}
public boolean canWrite(String fileName) {
fileName = translateFileName(fileName);
return canWriteInternal(new File(fileName));
......
......@@ -103,14 +103,14 @@ public class FileSystemMemory extends FileSystem {
}
}
public boolean isReadOnly(String fileName) {
return !getMemoryFile(fileName).canWrite();
}
public boolean setReadOnly(String fileName) {
return getMemoryFile(fileName).setReadOnly();
}
public boolean canWrite(String fileName) {
return getMemoryFile(fileName).canWrite();
}
public String getCanonicalPath(String fileName) {
fileName = fileName.replace('\\', '/');
int idx = fileName.indexOf(':') + 1;
......@@ -144,10 +144,6 @@ public class FileSystemMemory extends FileSystem {
return getMemoryFile(fileName).getLastModified();
}
public boolean canWrite(String fileName) {
return true;
}
public void createDirectory(String directoryName) {
// TODO directories are not really supported
}
......
......@@ -24,10 +24,6 @@ public abstract class FileSystemWrapper extends FileSystem {
*/
protected abstract String getPrefix();
public boolean canWrite(String fileName) {
return FileUtils.canWrite(unwrap(fileName));
}
public boolean setReadOnly(String fileName) {
return FileUtils.setReadOnly(unwrap(fileName));
}
......@@ -77,8 +73,8 @@ public abstract class FileSystemWrapper extends FileSystem {
return FileUtils.isDirectory(unwrap(fileName));
}
public boolean isReadOnly(String fileName) {
return FileUtils.isReadOnly(unwrap(fileName));
public boolean canWrite(String fileName) {
return FileUtils.canWrite(unwrap(fileName));
}
public long size(String fileName) {
......
......@@ -29,10 +29,6 @@ public class FileSystemZip extends FileSystem {
FileSystem.register(new FileSystemZip());
}
public boolean canWrite(String fileName) {
return false;
}
public void createDirectory(String directoryName) {
// ignore
}
......@@ -124,8 +120,8 @@ public class FileSystemZip extends FileSystem {
}
}
public boolean isReadOnly(String fileName) {
return true;
public boolean canWrite(String fileName) {
return false;
}
public boolean setReadOnly(String fileName) {
......
......@@ -3,6 +3,8 @@ package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.IOUtils;
/**
......@@ -191,10 +193,10 @@ public class FileUtils {
return getFileSystem(fileName).newOutputStream(fileName, append);
}
// special methods =======================================
/**
* Check if the file is writable.
* This method is similar to Java 7
* <code>java.nio.file.Path.checkAccess(AccessMode.WRITE)</code>
*
* @param fileName the file name
* @return if the file is writable
......@@ -203,20 +205,10 @@ public class FileUtils {
return getFileSystem(fileName).canWrite(fileName);
}
/**
* Check if a file is read-only.
* This method is similar to Java 7
* <code>java.nio.file.Path.checkAccess(AccessMode.WRITE)</code>
*
* @param fileName the file name
* @return if it is read only
*/
public static boolean isReadOnly(String fileName) {
return getFileSystem(fileName).isReadOnly(fileName);
}
// special methods =======================================
/**
* Disable the ability to write.
* Disable the ability to write. The file can still be deleted afterwards.
*
* @param fileName the file name
* @return true if the call was successful
......@@ -292,10 +284,18 @@ public class FileUtils {
* @param dir the directory name
*/
public static void createDirectories(String dir) {
if (dir != null && !FileUtils.exists(dir)) {
String parent = FileUtils.getParent(dir);
createDirectories(parent);
createDirectory(dir);
if (dir != null) {
if (FileUtils.exists(dir)) {
if (!FileUtils.isDirectory(dir)) {
DbException.get(ErrorCode.FILE_CREATION_FAILED_1,
"Could not create directory, " +
"because a file with the same name already exists: " + dir);
}
} else {
String parent = FileUtils.getParent(dir);
createDirectories(parent);
createDirectory(dir);
}
}
}
......
差异被折叠。
......@@ -11,7 +11,6 @@ import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
......@@ -449,32 +448,6 @@ public class IOUtils {
}
}
/**
* Create the directory and all parent directories if required.
*
* @param directory the directory
* @throws IOException
*/
public static void mkdirs(File directory) throws IOException {
int todo;
// loop, to deal with race conditions (if another thread creates or
// deletes the same directory at the same time).
for (int i = 0; i < 5; i++) {
if (directory.exists()) {
if (directory.isDirectory()) {
return;
}
throw new IOException("Could not create directory, " +
"because a file with the same name already exists: " +
directory.getAbsolutePath());
}
if (directory.mkdirs()) {
return;
}
}
throw new IOException("Could not create directory: " + directory.getAbsolutePath());
}
/**
* Trace input or output operations if enabled.
*
......
......@@ -139,11 +139,7 @@ public class SourceCompiler {
File dir = new File(compileDir);
if (packageName != null) {
dir = new File(dir, packageName.replace('.', '/'));
try {
IOUtils.mkdirs(dir);
} catch (IOException e) {
throw DbException.convertIOException(e, compileDir);
}
FileUtils.createDirectories(dir.getAbsolutePath());
}
File javaFile = new File(dir, className + ".java");
File classFile = new File(dir, className + ".class");
......
......@@ -637,7 +637,7 @@ public class TestLinkedTable extends TestBase {
String name = FileUtils.getName(file);
if ((name.startsWith("testLinkedTableInReadOnlyDb")) && (!name.endsWith(".trace.db"))) {
FileUtils.setReadOnly(file);
boolean isReadOnly = FileUtils.isReadOnly(file);
boolean isReadOnly = !FileUtils.canWrite(file);
if (!isReadOnly) {
fail("File " + file + " is not read only. Can't test it.");
}
......
......@@ -440,7 +440,7 @@ public class TestLob extends TestBase {
private void testTempFilesDeleted() throws Exception {
String[] list;
FileUtils.deleteRecursive(TEMP_DIR, true);
IOUtils.mkdirs(new File(TEMP_DIR));
FileUtils.createDirectories(TEMP_DIR);
list = FileUtils.listFiles(TEMP_DIR);
if (list.length > 0) {
fail("Unexpected temp file: " + list[0]);
......
......@@ -170,10 +170,6 @@ public class FileSystemDatabase extends FileSystem {
return new RuntimeException(e.toString(), e);
}
public boolean canWrite(String fileName) {
return true;
}
public void createDirectory(String directoryName) {
directoryName = unwrap(directoryName);
try {
......@@ -272,8 +268,8 @@ public class FileSystemDatabase extends FileSystem {
}
}
public boolean isReadOnly(String fileName) {
return false;
public boolean canWrite(String fileName) {
return true;
}
public boolean setReadOnly(String fileName) {
......
差异被折叠。
/*
* 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.utils;
import java.io.IOException;
import org.h2.store.fs.FileObject;
/**
* A debugging file that logs all operations.
*/
public class DebugFile implements FileObject {
private final DebugFilePath fs;
private final FileObject file;
private final String name;
DebugFile(DebugFilePath fs, FileObject file, String name) {
this.fs = fs;
this.file = file;
this.name = fs.getScheme() + ":" + name;
}
public void close() throws IOException {
debug("close");
file.close();
}
public long position() throws IOException {
debug("getFilePointer");
return file.position();
}
public long size() throws IOException {
debug("length");
return file.size();
}
public void readFully(byte[] b, int off, int len) throws IOException {
debug("readFully", file.position(), off, len);
file.readFully(b, off, len);
}
public void position(long pos) throws IOException {
debug("seek", pos);
file.position(pos);
}
public void truncate(long newLength) throws IOException {
checkPowerOff();
debug("truncate", newLength);
file.truncate(newLength);
}
public void sync() throws IOException {
debug("sync");
file.sync();
}
public void write(byte[] b, int off, int len) throws IOException {
checkPowerOff();
debug("write", file.position(), off, len);
file.write(b, off, len);
}
private void debug(String method, Object... params) {
fs.trace(name, method, params);
}
private void checkPowerOff() throws IOException {
try {
fs.checkPowerOff();
} catch (IOException e) {
try {
file.close();
} catch (IOException e2) {
// ignore
}
throw e;
}
}
public boolean tryLock() {
debug("tryLock");
return file.tryLock();
}
public void releaseLock() {
debug("releaseLock");
file.releaseLock();
}
}
\ No newline at end of file
/*
* 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.utils;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathWrapper;
/**
* A debugging file system that logs all operations.
*/
public class DebugFilePath extends FilePathWrapper {
private static final DebugFilePath INSTANCE = new DebugFilePath();
private static final IOException POWER_OFF = new IOException("Simulated power failure");
private int powerOffCount;
private boolean trace;
/**
* Register the file system.
*
* @return the instance
*/
public static DebugFilePath register() {
FilePath.register(INSTANCE);
return INSTANCE;
}
/**
* Check if the simulated power failure occurred.
* This call will decrement the countdown.
*
* @throws IOException if the simulated power failure occurred
*/
void checkPowerOff() throws IOException {
if (powerOffCount == 0) {
return;
}
if (powerOffCount > 1) {
powerOffCount--;
return;
}
powerOffCount = -1;
// throw new IOException("Simulated power failure");
throw POWER_OFF;
}
public void createDirectory() {
trace(name, "createDirectory");
super.createDirectory();
}
public boolean createFile() {
trace(name, "createFile");
return super.createFile();
}
public void delete() {
trace(name, "fileName");
super.delete();
}
public boolean exists() {
trace(name, "exists");
return super.exists();
}
// public boolean fileStartsWith(String fileName, String prefix) {
// trace(fileName, "fileStartsWith", unwrap(prefix));
// return super.fileStartsWith(fileName, prefix);
// }
public String getName() {
trace(name, "getName");
return super.getName();
}
public long lastModified() {
trace(name, "lastModified");
return super.lastModified();
}
public FilePath getParent() {
trace(name, "getParent");
return super.getParent();
}
public boolean isAbsolute() {
trace(name, "isAbsolute");
return super.isAbsolute();
}
public boolean isDirectory() {
trace(name, "isDirectory");
return super.isDirectory();
}
public boolean canWrite() {
trace(name, "canWrite");
return super.canWrite();
}
public boolean setReadOnly() {
trace(name, "setReadOnly");
return super.setReadOnly();
}
public long size() {
trace(name, "size");
return super.size();
}
public List<FilePath> listFiles() {
trace(name, "listFiles");
return super.listFiles();
}
public FilePath getCanonicalPath() {
trace(name, "getCanonicalPath");
return super.getCanonicalPath();
}
public InputStream newInputStream() throws IOException {
trace(name, "newInputStream");
InputStream in = super.newInputStream();
if (!trace) {
return in;
}
final String fileName = name;
return new FilterInputStream(in) {
public int read(byte[] b) throws IOException {
trace(fileName, "in.read(b)");
return super.read(b);
}
public int read(byte[] b, int off, int len) throws IOException {
trace(fileName, "in.read(b)", "in.read(b, " + off + ", " + len + ")");
return super.read(b, off, len);
}
public long skip(long n) throws IOException {
trace(fileName, "in.read(b)", "in.skip(" + n + ")");
return super.skip(n);
}
};
}
public FileObject openFileObject(String mode) throws IOException {
trace(name, "openFileObject", mode);
return new DebugFile(this, super.openFileObject(mode), name);
}
public OutputStream newOutputStream(boolean append) {
trace(name, "newOutputStream", append);
return super.newOutputStream(append);
}
public void moveTo(FilePath newName) {
trace(name, "moveTo", unwrap(((DebugFilePath) newName).name));
super.moveTo(newName);
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
trace(name, "createTempFile", suffix, deleteOnExit, inTempDir);
return super.createTempFile(suffix, deleteOnExit, inTempDir);
}
/**
* Print a debug message.
*
* @param fileName the (wrapped) file name
* @param method the method name
* @param params parameters if any
*/
void trace(String fileName, String method, Object... params) {
if (trace) {
StringBuilder buff = new StringBuilder(" ");
buff.append(unwrap(fileName)).append(' ').append(method);
for (Object s : params) {
buff.append(' ').append(s);
}
System.out.println(buff);
}
}
public void setPowerOffCount(int count) {
this.powerOffCount = count;
}
public int getPowerOffCount() {
return powerOffCount;
}
public boolean isTrace() {
return trace;
}
public void setTrace(boolean trace) {
this.trace = trace;
}
public String getScheme() {
return "debug";
}
}
......@@ -60,11 +60,6 @@ public class DebugFileSystem extends FileSystemWrapper {
throw POWER_OFF;
}
public boolean canWrite(String fileName) {
trace(fileName, "canWrite");
return super.canWrite(fileName);
}
public void createDirectory(String directoryName) {
trace(directoryName, "createDirectory");
super.createDirectory(directoryName);
......@@ -121,9 +116,9 @@ public class DebugFileSystem extends FileSystemWrapper {
return super.isDirectory(fileName);
}
public boolean isReadOnly(String fileName) {
trace(fileName, "isReadOnly");
return super.isReadOnly(fileName);
public boolean canWrite(String fileName) {
trace(fileName, "canWrite");
return super.canWrite(fileName);
}
public boolean setReadOnly(String fileName) {
......
/*
* 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.FilePath;
import org.h2.store.fs.FilePathWrapper;
import org.h2.store.fs.FileUtils;
/**
* A file system that encrypts the contents of the files.
*/
public class FilePathCrypt extends FilePathWrapper {
public static void register() {
FilePath.register(new FilePathCrypt());
}
protected String getPrefix() {
String[] parsed = parse(name);
return getScheme() + ":" + parsed[0] + ":" + parsed[1] + ":";
}
public FilePath unwrap(String fileName) {
return FilePath.get(parse(fileName)[2]);
}
public long size() {
long len = getBase().size();
return Math.max(0, len - FileObjectCrypt.HEADER_LENGTH - FileObjectCrypt.BLOCK_SIZE);
}
public FileObject openFileObject(String mode) throws IOException {
String[] parsed = parse(name);
FileObject file = FileUtils.openFileObject(parsed[2], mode);
return new FileObjectCrypt(name, parsed[0], parsed[1], file);
}
public OutputStream newOutputStream(boolean append) {
try {
FileObject file = openFileObject("rw");
return new FileObjectOutputStream(file, append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public InputStream newInputStream() {
try {
FileObject file = openFileObject("r");
return new FileObjectInputStream(file);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
/**
* 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(getScheme())) {
DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
}
fileName = fileName.substring(getScheme().length() + 1);
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 };
}
public String getScheme() {
return "crypt";
}
}
/*
* 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.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.h2.engine.Constants;
import org.h2.message.DbException;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileObjectInputStream;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathDisk;
import org.h2.store.fs.FileSystemDisk;
import org.h2.store.fs.FileUtils;
import org.h2.util.New;
/**
* This is a read-only file system that allows to access databases stored in a
* .zip or .jar file. The problem of this file system is that data is always
* accessed as a stream. But unlike FileSystemZip, it is possible to stack file
* systems.
*/
public class FilePathZip2 extends FilePath {
/**
* Register the file system.
*
* @return the instance
*/
public static FilePathZip2 register() {
FilePathZip2 instance = new FilePathZip2();
FilePath.register(instance);
return instance;
}
public FilePathZip2 getPath(String path) {
FilePathZip2 p = new FilePathZip2();
p.name = path;
return p;
}
public void createDirectory() {
// ignore
}
public boolean createFile() {
throw DbException.getUnsupportedException("write");
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return new FilePathDisk().getPath(name).createTempFile(suffix, deleteOnExit, true);
}
public void delete() {
throw DbException.getUnsupportedException("write");
}
public boolean exists() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipInputStream file = openZip();
boolean result = false;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
result = true;
break;
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return false;
}
}
// public boolean fileStartsWith(String fileName, String prefix) {
// return fileName.startsWith(prefix);
// }
public long lastModified() {
return 0;
}
public FilePath getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isAbsolute() {
return true;
}
public boolean isDirectory() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipInputStream file = openZip();
boolean result = false;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
String n = entry.getName();
if (n.equals(entryName)) {
result = entry.isDirectory();
break;
} else if (n.startsWith(entryName)) {
if (n.length() == entryName.length() + 1) {
if (n.equals(entryName + "/")) {
result = true;
break;
}
}
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return false;
}
}
public boolean canWrite() {
return false;
}
public boolean setReadOnly() {
return true;
}
public long size() {
try {
String entryName = getEntryName();
ZipInputStream file = openZip();
long result = 0;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
result = entry.getSize();
if (result == -1) {
result = 0;
while (true) {
long x = file.skip(16 * Constants.IO_BUFFER_SIZE);
if (x == 0) {
break;
}
result += x;
}
}
break;
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return 0;
}
}
public ArrayList<FilePath> listFiles() {
String path = name;
try {
if (path.indexOf('!') < 0) {
path += "!";
}
if (!path.endsWith("/")) {
path += "/";
}
ZipInputStream file = openZip();
String dirName = getEntryName();
String prefix = path.substring(0, path.length() - dirName.length());
ArrayList<FilePath> list = New.arrayList();
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
String name = entry.getName();
if (name.startsWith(dirName) && name.length() > dirName.length()) {
int idx = name.indexOf('/', dirName.length());
if (idx < 0 || idx >= name.length() - 1) {
list.add(getPath(prefix + name));
}
}
file.closeEntry();
}
file.close();
return list;
} catch (IOException e) {
throw DbException.convertIOException(e, "listFiles " + path);
}
}
public FilePath getCanonicalPath() {
return this;
}
public InputStream newInputStream() throws IOException {
FileObject file = openFileObject("r");
return new FileObjectInputStream(file);
}
public FileObject openFileObject(String mode) throws IOException {
String entryName = getEntryName();
if (entryName.length() == 0) {
throw new FileNotFoundException();
}
ZipInputStream in = openZip();
while (true) {
ZipEntry entry = in.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
return new FileObjectZip2(name, entryName, in, size());
}
in.closeEntry();
}
in.close();
throw new FileNotFoundException(name);
}
public OutputStream newOutputStream(boolean append) {
throw DbException.getUnsupportedException("write");
}
public void moveTo(FilePath newName) {
throw DbException.getUnsupportedException("write");
}
// public String unwrap(String fileName) {
// if (fileName.startsWith(PREFIX)) {
// fileName = fileName.substring(PREFIX.length());
// }
// int idx = fileName.indexOf('!');
// if (idx >= 0) {
// fileName = fileName.substring(0, idx);
// }
// return FileSystemDisk.expandUserHomeDirectory(fileName);
// }
private String getEntryName() {
int idx = name.indexOf('!');
String fileName;
if (idx <= 0) {
fileName = "";
} else {
fileName = name.substring(idx + 1);
}
fileName = fileName.replace('\\', '/');
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private ZipInputStream openZip() throws IOException {
String fileName = translateFileName(name);
return new ZipInputStream(FileUtils.newInputStream(fileName));
}
private static String translateFileName(String fileName) {
if (fileName.startsWith("zip2:")) {
fileName = fileName.substring("zip2:".length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
}
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public String getScheme() {
return "zip2";
}
}
\ No newline at end of file
......@@ -215,7 +215,7 @@ public class FileShell extends Tool {
for (String file : FileUtils.listFiles(dir)) {
StringBuilder buff = new StringBuilder();
buff.append(FileUtils.isDirectory(file) ? "d" : "-");
buff.append(FileUtils.isReadOnly(file) ? "r-" : "rw");
buff.append(FileUtils.canWrite(file) ? "rw" : "r-");
buff.append(' ');
buff.append(String.format("%10d", FileUtils.size(file)));
buff.append(' ');
......
......@@ -44,10 +44,6 @@ public class FileSystemZip2 extends FileSystem {
return INSTANCE;
}
public boolean canWrite(String fileName) {
return false;
}
public void createDirectory(String directoryName) {
// ignore
}
......@@ -159,8 +155,8 @@ public class FileSystemZip2 extends FileSystem {
}
}
public boolean isReadOnly(String fileName) {
return true;
public boolean canWrite(String fileName) {
return false;
}
public boolean setReadOnly(String fileName) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论