File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\store\FileStore.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.store;
8
9 import java.io.IOException;
10 import java.lang.ref.Reference;
11 import java.nio.ByteBuffer;
12 import java.nio.channels.FileChannel;
13 import org.h2.constant.ErrorCode;
14 import org.h2.constant.SysProperties;
15 import org.h2.engine.Constants;
16 import org.h2.message.DbException;
17 import org.h2.security.SecureFileStore;
18 import org.h2.store.fs.FileUtils;
19 import org.h2.util.TempFileDeleter;
20 import org.h2.util.Utils;
21
22 /**
23 * This class is an abstraction of a random access file.
24 * Each file contains a magic header, and reading / writing is done in blocks.
25 * See also {@link SecureFileStore}
26 */
27 public class FileStore {
28
29 /**
30 * The size of the file header in bytes.
31 */
32 public static final int HEADER_LENGTH = 3 * Constants.FILE_BLOCK_SIZE;
33
34 /**
35 * The magic file header.
36 */
37 private static final String HEADER = "-- H2 0.5/B -- ".substring(0, Constants.FILE_BLOCK_SIZE - 1) + "\n";
38
39 /**
40 * The file name.
41 */
42 protected String name;
43
44 /**
45 * The callback object is responsible to check access rights, and free up
46 * disk space if required.
47 */
48 private final DataHandler handler;
49
50 private FileChannel file;
51 private long filePos;
52 private long fileLength;
53 private Reference<?> autoDeleteReference;
54 private boolean checkedWriting = true;
55 private final String mode;
56 private final TempFileDeleter tempFileDeleter;
57 private java.nio.channels.FileLock lock;
58
59 /**
60 * Create a new file using the given settings.
61 *
62 * @param handler the callback object
63 * @param name the file name
64 * @param mode the access mode ("r", "rw", "rws", "rwd")
65 */
66 protected FileStore(DataHandler handler, String name, String mode) {
67 this.handler = handler;
68 this.name = name;
69 if (handler != null) {
70 tempFileDeleter = handler.getTempFileDeleter();
71 } else {
72 tempFileDeleter = null;
73 }
74 try {
75 boolean exists = FileUtils.exists(name);
76 if (exists && !FileUtils.canWrite(name)) {
77 mode = "r";
78 } else {
79 FileUtils.createDirectories(FileUtils.getParent(name));
80 }
81 file = FileUtils.open(name, mode);
82 if (exists) {
83 fileLength = file.size();
84 }
85 } catch (IOException e) {
86 throw DbException.convertIOException(e, "name: " + name + " mode: " + mode);
87 }
88 this.mode = mode;
89 }
90
91 /**
92 * Open a non encrypted file store with the given settings.
93 *
94 * @param handler the data handler
95 * @param name the file name
96 * @param mode the access mode (r, rw, rws, rwd)
97 * @return the created object
98 */
99 public static FileStore open(DataHandler handler, String name, String mode) {
100 return open(handler, name, mode, null, null, 0);
101 }
102
103 /**
104 * Open an encrypted file store with the given settings.
105 *
106 * @param handler the data handler
107 * @param name the file name
108 * @param mode the access mode (r, rw, rws, rwd)
109 * @param cipher the name of the cipher algorithm
110 * @param key the encryption key
111 * @return the created object
112 */
113 public static FileStore open(DataHandler handler, String name, String mode, String cipher, byte[] key) {
114 return open(handler, name, mode, cipher, key, Constants.ENCRYPTION_KEY_HASH_ITERATIONS);
115 }
116
117 /**
118 * Open an encrypted file store with the given settings.
119 *
120 * @param handler the data handler
121 * @param name the file name
122 * @param mode the access mode (r, rw, rws, rwd)
123 * @param cipher the name of the cipher algorithm
124 * @param key the encryption key
125 * @param keyIterations the number of iterations the key should be hashed
126 * @return the created object
127 */
128 public static FileStore open(DataHandler handler, String name, String mode, String cipher,
129 byte[] key, int keyIterations) {
130 FileStore store;
131 if (cipher == null) {
132 store = new FileStore(handler, name, mode);
133 } else {
134 store = new SecureFileStore(handler, name, mode, cipher, key, keyIterations);
135 }
136 return store;
137 }
138
139 /**
140 * Generate the random salt bytes if required.
141 *
142 * @return the random salt or the magic
143 */
144 protected byte[] generateSalt() {
145 return HEADER.getBytes();
146 }
147
148 /**
149 * Initialize the key using the given salt.
150 *
151 * @param salt the salt
152 */
153 protected void initKey(byte[] salt) {
154 // do nothing
155 }
156
157 public void setCheckedWriting(boolean value) {
158 this.checkedWriting = value;
159 }
160
161 private void checkWritingAllowed() {
162 if (handler != null && checkedWriting) {
163 handler.checkWritingAllowed();
164 }
165 }
166
167 private void checkPowerOff() {
168 if (handler != null) {
169 handler.checkPowerOff();
170 }
171 }
172
173 /**
174 * Initialize the file. This method will write or check the file header if
175 * required.
176 */
177 public void init() {
178 int len = Constants.FILE_BLOCK_SIZE;
179 byte[] salt;
180 byte[] magic = HEADER.getBytes();
181 if (length() < HEADER_LENGTH) {
182 // write unencrypted
183 checkedWriting = false;
184 writeDirect(magic, 0, len);
185 salt = generateSalt();
186 writeDirect(salt, 0, len);
187 initKey(salt);
188 // write (maybe) encrypted
189 write(magic, 0, len);
190 checkedWriting = true;
191 } else {
192 // read unencrypted
193 seek(0);
194 byte[] buff = new byte[len];
195 readFullyDirect(buff, 0, len);
196 if (Utils.compareNotNull(buff, magic) != 0) {
197 throw DbException.get(ErrorCode.FILE_VERSION_ERROR_1, name);
198 }
199 salt = new byte[len];
200 readFullyDirect(salt, 0, len);
201 initKey(salt);
202 // read (maybe) encrypted
203 readFully(buff, 0, Constants.FILE_BLOCK_SIZE);
204 if (Utils.compareNotNull(buff, magic) != 0) {
205 throw DbException.get(ErrorCode.FILE_ENCRYPTION_ERROR_1, name);
206 }
207 }
208 }
209
210 /**
211 * Close the file.
212 */
213 public void close() {
214 if (file != null) {
215 try {
216 trace("close", name, file);
217 file.close();
218 } catch (IOException e) {
219 throw DbException.convertIOException(e, name);
220 } finally {
221 file = null;
222 }
223 }
224 }
225
226 /**
227 * Close the file without throwing any exceptions. Exceptions are simply
228 * ignored.
229 */
230 public void closeSilently() {
231 try {
232 close();
233 } catch (Exception e) {
234 // ignore
235 }
236 }
237
238 /**
239 * Close the file (ignoring exceptions) and delete the file.
240 */
241 public void closeAndDeleteSilently() {
242 if (file != null) {
243 closeSilently();
244 tempFileDeleter.deleteFile(autoDeleteReference, name);
245 name = null;
246 }
247 }
248
249 /**
250 * Read a number of bytes without decrypting.
251 *
252 * @param b the target buffer
253 * @param off the offset
254 * @param len the number of bytes to read
255 */
256 protected void readFullyDirect(byte[] b, int off, int len) {
257 readFully(b, off, len);
258 }
259
260 /**
261 * Read a number of bytes.
262 *
263 * @param b the target buffer
264 * @param off the offset
265 * @param len the number of bytes to read
266 */
267 public void readFully(byte[] b, int off, int len) {
268 if (SysProperties.CHECK && (len < 0 || len % Constants.FILE_BLOCK_SIZE != 0)) {
269 DbException.throwInternalError("unaligned read " + name + " len " + len);
270 }
271 checkPowerOff();
272 try {
273 FileUtils.readFully(file, ByteBuffer.wrap(b, off, len));
274 } catch (IOException e) {
275 throw DbException.convertIOException(e, name);
276 }
277 filePos += len;
278 }
279
280 /**
281 * Go to the specified file location.
282 *
283 * @param pos the location
284 */
285 public void seek(long pos) {
286 if (SysProperties.CHECK && pos % Constants.FILE_BLOCK_SIZE != 0) {
287 DbException.throwInternalError("unaligned seek " + name + " pos " + pos);
288 }
289 try {
290 if (pos != filePos) {
291 file.position(pos);
292 filePos = pos;
293 }
294 } catch (IOException e) {
295 throw DbException.convertIOException(e, name);
296 }
297 }
298
299 /**
300 * Write a number of bytes without encrypting.
301 *
302 * @param b the source buffer
303 * @param off the offset
304 * @param len the number of bytes to write
305 */
306 protected void writeDirect(byte[] b, int off, int len) {
307 write(b, off, len);
308 }
309
310 /**
311 * Write a number of bytes.
312 *
313 * @param b the source buffer
314 * @param off the offset
315 * @param len the number of bytes to write
316 */
317 public void write(byte[] b, int off, int len) {
318 if (SysProperties.CHECK && (len < 0 || len % Constants.FILE_BLOCK_SIZE != 0)) {
319 DbException.throwInternalError("unaligned write " + name + " len " + len);
320 }
321 checkWritingAllowed();
322 checkPowerOff();
323 try {
324 FileUtils.writeFully(file, ByteBuffer.wrap(b, off, len));
325 } catch (IOException e) {
326 closeFileSilently();
327 throw DbException.convertIOException(e, name);
328 }
329 filePos += len;
330 fileLength = Math.max(filePos, fileLength);
331 }
332
333 /**
334 * Set the length of the file. This will expand or shrink the file.
335 *
336 * @param newLength the new file size
337 */
338 public void setLength(long newLength) {
339 if (SysProperties.CHECK && newLength % Constants.FILE_BLOCK_SIZE != 0) {
340 DbException.throwInternalError("unaligned setLength " + name + " pos " + newLength);
341 }
342 checkPowerOff();
343 checkWritingAllowed();
344 try {
345 if (newLength > fileLength) {
346 long pos = filePos;
347 file.position(newLength - 1);
348 FileUtils.writeFully(file, ByteBuffer.wrap(new byte[1]));
349 file.position(pos);
350 } else {
351 file.truncate(newLength);
352 }
353 fileLength = newLength;
354 } catch (IOException e) {
355 closeFileSilently();
356 throw DbException.convertIOException(e, name);
357 }
358 }
359
360 /**
361 * Get the file size in bytes.
362 *
363 * @return the file size
364 */
365 public long length() {
366 try {
367 long len = fileLength;
368 if (SysProperties.CHECK2) {
369 len = file.size();
370 if (len != fileLength) {
371 DbException.throwInternalError("file " + name + " length " + len + " expected " + fileLength);
372 }
373 }
374 if (SysProperties.CHECK2 && len % Constants.FILE_BLOCK_SIZE != 0) {
375 long newLength = len + Constants.FILE_BLOCK_SIZE - (len % Constants.FILE_BLOCK_SIZE);
376 file.truncate(newLength);
377 fileLength = newLength;
378 DbException.throwInternalError("unaligned file length " + name + " len " + len);
379 }
380 return len;
381 } catch (IOException e) {
382 throw DbException.convertIOException(e, name);
383 }
384 }
385
386 /**
387 * Get the current location of the file pointer.
388 *
389 * @return the location
390 */
391 public long getFilePointer() {
392 if (SysProperties.CHECK2) {
393 try {
394 if (file.position() != filePos) {
395 DbException.throwInternalError();
396 }
397 } catch (IOException e) {
398 throw DbException.convertIOException(e, name);
399 }
400 }
401 return filePos;
402 }
403
404 /**
405 * Call fsync. Depending on the operating system and hardware, this may or
406 * may not in fact write the changes.
407 */
408 public void sync() {
409 try {
410 file.force(true);
411 } catch (IOException e) {
412 closeFileSilently();
413 throw DbException.convertIOException(e, name);
414 }
415 }
416
417 /**
418 * Automatically delete the file once it is no longer in use.
419 */
420 public void autoDelete() {
421 if (autoDeleteReference == null) {
422 autoDeleteReference = tempFileDeleter.addFile(name, this);
423 }
424 }
425
426 /**
427 * No longer automatically delete the file once it is no longer in use.
428 */
429 public void stopAutoDelete() {
430 tempFileDeleter.stopAutoDelete(autoDeleteReference, name);
431 autoDeleteReference = null;
432 }
433
434 /**
435 * Close the file. The file may later be re-opened using openFile.
436 */
437 public void closeFile() throws IOException {
438 file.close();
439 file = null;
440 }
441
442 /**
443 * Just close the file, without setting the reference to null. This method
444 * is called when writing failed. The reference is not set to null so that
445 * there are no NullPointerExceptions later on.
446 */
447 private void closeFileSilently() {
448 try {
449 file.close();
450 } catch (IOException e) {
451 // ignore
452 }
453 }
454
455 /**
456 * Re-open the file. The file pointer will be reset to the previous
457 * location.
458 */
459 public void openFile() throws IOException {
460 if (file == null) {
461 file = FileUtils.open(name, mode);
462 file.position(filePos);
463 }
464 }
465
466 private static void trace(String method, String fileName, Object o) {
467 if (SysProperties.TRACE_IO) {
468 System.out.println("FileStore." + method + " " + fileName + " " + o);
469 }
470 }
471
472 /**
473 * Try to lock the file.
474 *
475 * @return true if successful
476 */
477 public synchronized boolean tryLock() {
478 try {
479 lock = file.tryLock();
480 return lock != null;
481 } catch (Exception e) {
482 // ignore OverlappingFileLockException
483 return false;
484 }
485 }
486
487 /**
488 * Release the file lock.
489 */
490 public synchronized void releaseLock() {
491 if (file != null && lock != null) {
492 try {
493 lock.release();
494 } catch (Exception e) {
495 // ignore
496 }
497 lock = null;
498 }
499 }
500
501 }
502