EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.store]

COVERAGE SUMMARY FOR SOURCE FILE [FileStore.java]

nameclass, %method, %block, %line, %
FileStore.java100% (1/1)90%  (28/31)65%  (473/726)72%  (128.6/179)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileStore100% (1/1)90%  (28/31)65%  (473/726)72%  (128.6/179)
closeFile (): void 0%   (0/1)0%   (0/7)0%   (0/3)
closeFileSilently (): void 0%   (0/1)0%   (0/6)0%   (0/4)
sync (): void 0%   (0/1)0%   (0/14)0%   (0/6)
trace (String, String, Object): void 100% (1/1)14%  (3/21)67%  (2/3)
openFile (): void 100% (1/1)24%  (4/17)50%  (2/4)
length (): long 100% (1/1)30%  (24/81)46%  (6/13)
seek (long): void 100% (1/1)52%  (23/44)67%  (6/9)
readFully (byte [], int, int): void 100% (1/1)55%  (26/47)67%  (6/9)
close (): void 100% (1/1)59%  (17/29)71%  (5.7/8)
write (byte [], int, int): void 100% (1/1)60%  (35/58)67%  (8/12)
getFilePointer (): long 100% (1/1)62%  (13/21)57%  (4/7)
FileStore (DataHandler, String, String): void 100% (1/1)67%  (39/58)81%  (13/16)
setLength (long): void 100% (1/1)68%  (49/72)76%  (13/17)
tryLock (): boolean 100% (1/1)73%  (11/15)48%  (1.9/4)
closeSilently (): void 100% (1/1)80%  (4/5)75%  (3/4)
releaseLock (): void 100% (1/1)93%  (14/15)83%  (5/6)
init (): void 100% (1/1)94%  (80/85)95%  (21/22)
<static initializer> 100% (1/1)100% (13/13)100% (1/1)
autoDelete (): void 100% (1/1)100% (13/13)100% (3/3)
checkPowerOff (): void 100% (1/1)100% (7/7)100% (3/3)
checkWritingAllowed (): void 100% (1/1)100% (10/10)100% (3/3)
closeAndDeleteSilently (): void 100% (1/1)100% (17/17)100% (5/5)
generateSalt (): byte [] 100% (1/1)100% (4/4)100% (1/1)
initKey (byte []): void 100% (1/1)100% (1/1)100% (1/1)
open (DataHandler, String, String): FileStore 100% (1/1)100% (8/8)100% (1/1)
open (DataHandler, String, String, String, byte []): FileStore 100% (1/1)100% (8/8)100% (1/1)
open (DataHandler, String, String, String, byte [], int): FileStore 100% (1/1)100% (22/22)100% (4/4)
readFullyDirect (byte [], int, int): void 100% (1/1)100% (6/6)100% (2/2)
setCheckedWriting (boolean): void 100% (1/1)100% (4/4)100% (2/2)
stopAutoDelete (): void 100% (1/1)100% (12/12)100% (3/3)
writeDirect (byte [], int, int): void 100% (1/1)100% (6/6)100% (2/2)

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

[all classes][org.h2.store]
EMMA 2.0.5312 (C) Vladimir Roubtsov