|
22 | 22 | import org.apache.logging.log4j.LogManager;
|
23 | 23 | import org.apache.logging.log4j.Logger;
|
24 | 24 | import org.exist.storage.DBBroker;
|
| 25 | +import org.exist.util.ByteConversion; |
25 | 26 |
|
26 | 27 | import javax.annotation.Nullable;
|
27 | 28 | import java.io.IOException;
|
|
31 | 32 | import java.nio.file.Path;
|
32 | 33 |
|
33 | 34 | import static java.nio.file.StandardOpenOption.READ;
|
34 |
| -import static org.exist.storage.journal.Journal.LOG_ENTRY_BACK_LINK_LEN; |
35 |
| -import static org.exist.storage.journal.Journal.LOG_ENTRY_BASE_LEN; |
36 |
| -import static org.exist.storage.journal.Journal.LOG_ENTRY_HEADER_LEN; |
| 35 | +import static org.exist.storage.journal.Journal.*; |
37 | 36 |
|
38 | 37 | /**
|
39 | 38 | * Read log entries from the journal file. This class is used during recovery to scan the
|
@@ -66,12 +65,40 @@ public JournalReader(final DBBroker broker, final Path file, final int fileNumbe
|
66 | 65 | this.fileNumber = fileNumber;
|
67 | 66 | try {
|
68 | 67 | this.fc = Files.newByteChannel(file, READ);
|
| 68 | + validateJournalHeader(file, fc); |
69 | 69 | } catch (final IOException e) {
|
70 | 70 | close();
|
71 | 71 | throw new LogException("Failed to read journal file " + file.toAbsolutePath().toString(), e);
|
72 | 72 | }
|
73 | 73 | }
|
74 | 74 |
|
| 75 | + private void validateJournalHeader(final Path file, final SeekableByteChannel fc) throws IOException, LogException { |
| 76 | + // read the magic number |
| 77 | + final ByteBuffer buf = ByteBuffer.allocate(JOURNAL_HEADER_LEN); |
| 78 | + fc.read(buf); |
| 79 | + buf.flip(); |
| 80 | + |
| 81 | + // check the magic number |
| 82 | + final boolean validMagic = |
| 83 | + buf.get() == JOURNAL_MAGIC_NUMBER[0] |
| 84 | + && buf.get() == JOURNAL_MAGIC_NUMBER[1] |
| 85 | + && buf.get() == JOURNAL_MAGIC_NUMBER[2] |
| 86 | + && buf.get() == JOURNAL_MAGIC_NUMBER[3]; |
| 87 | + |
| 88 | + if (!validMagic) { |
| 89 | + throw new LogException("File was not recognised as a valid eXist-db journal file: " + file.toAbsolutePath().toString()); |
| 90 | + } |
| 91 | + |
| 92 | + // check the version of the journal format |
| 93 | + final short storedVersion = ByteConversion.byteToShortH(new byte[] {buf.get(), buf.get()}, 0); |
| 94 | + final boolean validVersion = |
| 95 | + storedVersion == JOURNAL_VERSION; |
| 96 | + |
| 97 | + if (!validVersion) { |
| 98 | + throw new LogException("Journal file was version " + storedVersion + ", but required version " + JOURNAL_VERSION + ": " + file.toAbsolutePath().toString()); |
| 99 | + } |
| 100 | + } |
| 101 | + |
75 | 102 | /**
|
76 | 103 | * Returns the next entry found from the current position.
|
77 | 104 | *
|
@@ -105,8 +132,8 @@ Loggable previousEntry() throws LogException {
|
105 | 132 | try {
|
106 | 133 | checkOpen();
|
107 | 134 |
|
108 |
| - // are we at the start of the journal? |
109 |
| - if (fc.position() == 0) { |
| 135 | + // is there a previous entry to read? |
| 136 | + if (fc.position() < JOURNAL_HEADER_LEN + LOG_ENTRY_BASE_LEN) { |
110 | 137 | return null;
|
111 | 138 | }
|
112 | 139 |
|
@@ -143,7 +170,7 @@ Loggable previousEntry() throws LogException {
|
143 | 170 | Loggable lastEntry() throws LogException {
|
144 | 171 | try {
|
145 | 172 | checkOpen();
|
146 |
| - fc.position(fc.size()); |
| 173 | + positionLast(); |
147 | 174 | return previousEntry();
|
148 | 175 | } catch (final IOException e) {
|
149 | 176 | throw new LogException("Fatal error while reading last journal entry: " + e.getMessage(), e);
|
@@ -230,6 +257,34 @@ public void position(final long lsn) throws LogException {
|
230 | 257 | }
|
231 | 258 | }
|
232 | 259 |
|
| 260 | + /** |
| 261 | + * Re-position the file position so it points to the first entry. |
| 262 | + * |
| 263 | + * @throws LogException if the journal file cannot be re-positioned |
| 264 | + */ |
| 265 | + public void positionFirst() throws LogException { |
| 266 | + try { |
| 267 | + checkOpen(); |
| 268 | + fc.position(JOURNAL_HEADER_LEN); |
| 269 | + } catch (final IOException e) { |
| 270 | + throw new LogException("Fatal error while seeking first journal entry: " + e.getMessage(), e); |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | + /** |
| 275 | + * Re-position the file position so it points to the last entry. |
| 276 | + * |
| 277 | + * @throws LogException if the journal file cannot be re-positioned |
| 278 | + */ |
| 279 | + public void positionLast() throws LogException { |
| 280 | + try { |
| 281 | + checkOpen(); |
| 282 | + fc.position(fc.size()); |
| 283 | + } catch (final IOException e) { |
| 284 | + throw new LogException("Fatal error while seeking last journal entry: " + e.getMessage(), e); |
| 285 | + } |
| 286 | + } |
| 287 | + |
233 | 288 | private void checkOpen() throws IOException {
|
234 | 289 | if (fc == null) {
|
235 | 290 | throw new IOException("Journal file is closed");
|
|
0 commit comments