00001
package com.quadcap.sql.file;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
import java.io.File;
00042
import java.io.FileDescriptor;
00043
import java.io.IOException;
00044
import java.io.RandomAccessFile;
00045
00046
import java.util.BitSet;
00047
00048
import com.quadcap.util.Debug;
00049
import com.quadcap.util.Util;
00050
00051
import com.quadcap.sql.Version;
00052
00053
00054
00055
00056
00057
00058
00059 public class BlockStore {
00060 File
file;
00061 FileRandomAccess fra;
00062 boolean readOnly;
00063 Log log;
00064 int blockSize;
00065 BitSet
modified =
new BitSet();
00066 Object
lock;
00067 boolean encrypted =
false;
00068 boolean isTempFile =
false;
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 public BlockStore() {
00080 }
00081
00082 public String
getName() {
00083
return file == null ?
"null" :
file.getName();
00084 }
00085
00086 public void init(File file, String mode,
int blockSize, Object lock)
00087
throws IOException
00088 {
00089
this.file =
file;
00090
boolean exists =
file.exists();
00091 RandomAccessFile ra =
new RandomAccessFile(
file.getAbsolutePath(), mode);
00092
this.fra =
new FileRandomAccess(ra, 1024L * 1024L * 1024L * 1024L);
00093
this.readOnly = mode.equalsIgnoreCase(
"r");
00094
this.lock =
lock;
00095
if (!exists) {
00096
createHeader(
blockSize);
00097 }
else {
00098
readHeader();
00099 }
00100
isTempFile = !(
file.getPath().endsWith(
"datafile"));
00101 }
00102
00103
00104
00105
00106 public void setLog(
Log log) {
this.log = log; }
00107
00108
00109
00110
00111 public Log getLog() {
return log; }
00112
00113
00114
00115
00116 public int blockSize() {
return blockSize; }
00117
00118
00119
00120
00121 public File
getFile() {
return file; }
00122
00123
00124
00125
00126
00127
00128
00129
00130 public void read(
long blockNum, byte[] buf)
throws IOException {
00131 read(blockNum, buf, 0);
00132 }
00133
00134 public void read(
long blockNum, byte[] buf,
int off)
00135
throws IOException
00136 {
00137
synchronized (
lock) {
00138
readCount++;
00139
fra.
read(blockNum * blockSize, buf, off, blockSize);
00140 }
00141 }
00142
00143 public static int writeCount = 0;
00144 public static int readCount = 0;
00145
00146
00147
00148
00149
00150
00151
00152 public void write(
long blockNum, byte[] buf)
00153
throws IOException
00154 {
00155
synchronized (
lock) {
00156
writeCount++;
00157
if (
readOnly) {
00158
throw new IOException(
"Attempt to write readonly file: " +
00159
file.getName());
00160 }
00161
long pos = blockNum * blockSize;
00162
if (
log != null && !
modified.get((
int)blockNum)) {
00163
modified.set((
int)blockNum);
00164
if (pos <
fra.
size()) {
00165
log.
saveBlock(blockNum);
00166 }
00167 }
00168
00169
if (
Trace.bit(20)) {
00170
Debug.println(
toString() +
".write(" + blockNum +
"," +
00171
Block.signature(buf) +
") " +
Util.stackTrace());
00172 }
00173
00174
fra.
write(blockNum * blockSize, buf, 0, buf.length);
00175 }
00176 }
00177
00178
00179
00180
00181 public void restore(
long blockNum, byte[] buf,
int off)
00182
throws IOException
00183 {
00184
synchronized (
lock) {
00185
00186
if (
Trace.bit(20)) {
00187
Debug.println(
toString() +
".restore(" + blockNum +
"," +
00188
Block.signature(buf, off, blockSize) +
")");
00189 }
00190
00191
fra.
write(blockNum * blockSize, buf, off, blockSize);
00192 }
00193
00194 }
00195
00196 public void clearModified() throws IOException {
00197
modified =
new BitSet();
00198
if (
log != null)
log.
resetBlocks();
00199 }
00200
00201
00202 public void setLength(
long length)
throws IOException {
00203
if (
readOnly) {
00204
throw new IOException(
"Attempt to write readonly file: " +
00205
file.getName());
00206 }
00207
fra.
resize(length);
00208 }
00209
00210 public boolean isEncrypted() {
00211
return encrypted;
00212 }
00213
00214 private void readHeader() throws IOException {
00215 byte[] buf =
new byte[32];
00216
fra.
read(0, buf, 0, buf.length);
00217
int v = ((buf[0] & 0xff) << 8) + (buf[1] & 0xff);
00218
switch (v) {
00219
case 0x040c:
00220
encrypted =
false;
00221
break;
00222
case 0x0e0c:
00223
encrypted =
true;
00224
break;
00225
default:
00226
throw new IOException(
"BlockFile: bad magic");
00227 }
00228
00229
00230
00231
00232
if (buf[2] !=
Version.majorVersion) {
00233
throw new IOException(
"BlockFile: bad version (" +
00234 (
int)buf[2] +
"." + (
int)buf[3] +
00235
" vs " +
00236
Version.majorVersion +
"." +
00237
Version.minorVersion +
")");
00238 }
00239
this.blockSize =
ByteUtil.getInt(buf,
BlockFile.oBLOCKSIZE);
00240 }
00241
00242 private void createHeader(
int blockSize)
throws IOException {
00243
this.blockSize = blockSize;
00244 byte[] buf =
newHeader(blockSize, 2);
00245
fra.
write(0, buf, 0, buf.length);
00246 }
00247
00248 protected final byte[]
newHeader(
int blockSize,
long lastBlock) {
00249 byte[] buf =
new byte[blockSize];
00250
for (
int i = 0; i < blockSize; i++) buf[i] = (byte)0;
00251 buf[0] = 0x4; buf[1] = 0xc;
00252 buf[2] =
Version.majorVersion;
00253 buf[3] =
Version.minorVersion;
00254
ByteUtil.putInt(buf,
BlockFile.oBLOCKSIZE, blockSize);
00255
ByteUtil.putLong(buf,
BlockFile.oLASTBLOCK, lastBlock);
00256
return buf;
00257 }
00258
00259 public void close() throws IOException {
00260
try {
00261
if (
fra != null)
fra.
close();
00262 } finally {
00263
fra = null;
00264 }
00265 }
00266
00267 public void flush() throws IOException {
00268
try {
00269
fra.
flush();
00270
readCount =
writeCount = 0;
00271 }
catch (Throwable t) {}
00272 }
00273
00274
00275 public void setKey(com.quadcap.crypto.SymmetricKey key)
00276 throws IOException
00277 {
00278
throw new IOException(
"Not encrypted");
00279 }
00280
00281
00282
00283 public String
toString() {
00284
return "BlockStore(" +
file.getPath() +
")";
00285 }
00286
00287 public static String
rw() {
00288
return "(r=" +
BlockStore.readCount +
00289
" w=" +
BlockStore.writeCount +
")";
00290 }
00291
00292
00293
00294 }