001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.internal;
022:
023: import com.db4o.*;
024: import com.db4o.ext.OldFormatException;
025: import com.db4o.foundation.Cool;
026: import com.db4o.internal.fileheader.*;
027: import com.db4o.internal.handlers.IntHandler;
028:
029: /**
030: * configuration and agent to write the configuration block
031: * The configuration block also contains the timer lock and
032: * a pointer to the running transaction.
033: * @exclude
034: */
035: public final class ConfigBlock {
036:
037: // ConfigBlock Format
038:
039: // int length of the config block
040: // long last access time for timer lock
041: // long last access time for timer lock (duplicate for atomicity)
042: // byte unicode or not
043: // int transaction-in-process address
044: // int transaction-in-process address (duplicate for atomicity)
045: // int id of PBootRecord
046: // int unused (and lost)
047: // 5 bytes of the encryption password
048: // byte freespace system used
049: // int freespace address
050: // int converter versions
051:
052: private final LocalObjectContainer _container;
053: private final TimerFileLock _timerFileLock;
054:
055: private int _address;
056: private Transaction _transactionToCommit;
057: public int _bootRecordID;
058:
059: private static final int MINIMUM_LENGTH = Const4.INT_LENGTH // own length
060: + (Const4.LONG_LENGTH * 2) // candidate ID and last access time
061: + 1; // Unicode byte
062:
063: static final int OPEN_TIME_OFFSET = Const4.INT_LENGTH;
064: public static final int ACCESS_TIME_OFFSET = OPEN_TIME_OFFSET
065: + Const4.LONG_LENGTH;
066:
067: public static final int TRANSACTION_OFFSET = MINIMUM_LENGTH;
068: private static final int BOOTRECORD_OFFSET = TRANSACTION_OFFSET
069: + Const4.INT_LENGTH * 2;
070: private static final int INT_FORMERLY_KNOWN_AS_BLOCK_OFFSET = BOOTRECORD_OFFSET
071: + Const4.INT_LENGTH;
072: private static final int ENCRYPTION_PASSWORD_LENGTH = 5;
073: private static final int PASSWORD_OFFSET = INT_FORMERLY_KNOWN_AS_BLOCK_OFFSET
074: + ENCRYPTION_PASSWORD_LENGTH;
075: private static final int FREESPACE_SYSTEM_OFFSET = PASSWORD_OFFSET + 1;
076: private static final int FREESPACE_ADDRESS_OFFSET = FREESPACE_SYSTEM_OFFSET
077: + Const4.INT_LENGTH;
078: private static final int CONVERTER_VERSION_OFFSET = FREESPACE_ADDRESS_OFFSET
079: + Const4.INT_LENGTH;
080: private static final int UUID_INDEX_ID_OFFSET = CONVERTER_VERSION_OFFSET
081: + Const4.INT_LENGTH;
082:
083: // complete possible data in config block
084: private static final int LENGTH = MINIMUM_LENGTH
085: + (Const4.INT_LENGTH * 7) // (two transaction pointers, PDB ID, lost int, freespace address, converter_version, index id)
086: + ENCRYPTION_PASSWORD_LENGTH + 1;
087:
088: public static ConfigBlock forNewFile(LocalObjectContainer file)
089: throws Db4oIOException {
090: return new ConfigBlock(file, true, 0);
091: }
092:
093: public static ConfigBlock forExistingFile(
094: LocalObjectContainer file, int address)
095: throws Db4oIOException, OldFormatException {
096: return new ConfigBlock(file, false, address);
097: }
098:
099: private ConfigBlock(LocalObjectContainer stream, boolean isNew,
100: int address) throws Db4oIOException, OldFormatException {
101: _container = stream;
102: _timerFileLock = TimerFileLock.forFile(stream);
103: timerFileLock().writeHeaderLock();
104: if (!isNew) {
105: read(address);
106: }
107: timerFileLock().start();
108: }
109:
110: private TimerFileLock timerFileLock() {
111: return _timerFileLock;
112: }
113:
114: public long openTime() {
115: return timerFileLock().openTime();
116: }
117:
118: public Transaction getTransactionToCommit() {
119: return _transactionToCommit;
120: }
121:
122: private byte[] passwordToken() {
123: byte[] pwdtoken = new byte[ENCRYPTION_PASSWORD_LENGTH];
124: String fullpwd = configImpl().password();
125: if (configImpl().encrypt() && fullpwd != null) {
126: try {
127: byte[] pwdbytes = new LatinStringIO().write(fullpwd);
128: Buffer encwriter = new StatefulBuffer(_container
129: .transaction(), pwdbytes.length
130: + ENCRYPTION_PASSWORD_LENGTH);
131: encwriter.append(pwdbytes);
132: encwriter.append(new byte[ENCRYPTION_PASSWORD_LENGTH]);
133: _container._handlers.decrypt(encwriter);
134: System.arraycopy(encwriter._buffer, 0, pwdtoken, 0,
135: ENCRYPTION_PASSWORD_LENGTH);
136: } catch (Exception exc) {
137: // should never happen
138: //if(Debug.atHome) {
139: exc.printStackTrace();
140: //}
141: }
142: }
143: return pwdtoken;
144: }
145:
146: private SystemData systemData() {
147: return _container.systemData();
148: }
149:
150: private void read(int address) throws Db4oIOException,
151: OldFormatException {
152: addressChanged(address);
153: timerFileLock().writeOpenTime();
154: StatefulBuffer reader = _container.getWriter(_container
155: .systemTransaction(), _address, LENGTH);
156: _container.readBytes(reader._buffer, _address, LENGTH);
157: int oldLength = reader.readInt();
158: if (oldLength > LENGTH || oldLength < MINIMUM_LENGTH) {
159: throw new IncompatibleFileFormatException();
160: }
161: if (oldLength != LENGTH) {
162: // TODO: instead of bailing out, somehow trigger wrapping the stream's io adapter in
163: // a readonly decorator, issue a notification and continue?
164: if (!allowVersionUpdate()) {
165: if (allowAutomaticShutdown()) {
166: Platform4.removeShutDownHook(_container);
167: }
168: throw new OldFormatException();
169: }
170: }
171:
172: reader.readLong(); // open time
173: long lastAccessTime = reader.readLong();
174:
175: systemData().stringEncoding(reader.readByte());
176:
177: if (oldLength > TRANSACTION_OFFSET) {
178: _transactionToCommit = LocalTransaction
179: .readInterruptedTransaction(_container, reader);
180: }
181:
182: if (oldLength > BOOTRECORD_OFFSET) {
183: _bootRecordID = reader.readInt();
184: }
185:
186: if (oldLength > INT_FORMERLY_KNOWN_AS_BLOCK_OFFSET) {
187: // this one is dead.
188: // Blocksize is in the very first bytes
189: reader.readInt();
190: }
191:
192: if (oldLength > PASSWORD_OFFSET) {
193: byte[] encpassword = reader
194: .readBytes(ENCRYPTION_PASSWORD_LENGTH);
195: boolean nonZeroByte = false;
196: for (int i = 0; i < encpassword.length; i++) {
197: if (encpassword[i] != 0) {
198: nonZeroByte = true;
199: break;
200: }
201: }
202: if (!nonZeroByte) {
203: // no password in the databasefile, work without encryption
204: _container._handlers.oldEncryptionOff();
205: } else {
206: byte[] storedpwd = passwordToken();
207: for (int idx = 0; idx < storedpwd.length; idx++) {
208: if (storedpwd[idx] != encpassword[idx]) {
209: _container.fatalException(54);
210: }
211: }
212: }
213: }
214:
215: if (oldLength > FREESPACE_SYSTEM_OFFSET) {
216: systemData().freespaceSystem(reader.readByte());
217: }
218:
219: if (oldLength > FREESPACE_ADDRESS_OFFSET) {
220: systemData().freespaceAddress(reader.readInt());
221: }
222:
223: if (oldLength > CONVERTER_VERSION_OFFSET) {
224: systemData().converterVersion(reader.readInt());
225: }
226: if (oldLength > UUID_INDEX_ID_OFFSET) {
227: final int uuidIndexId = reader.readInt();
228: if (0 != uuidIndexId) {
229: systemData().uuidIndexId(uuidIndexId);
230: }
231: }
232:
233: _container.ensureFreespaceSlot();
234:
235: if (FileHeader.lockedByOtherSession(_container, lastAccessTime)) {
236: _timerFileLock.checkIfOtherSessionAlive(_container,
237: _address, ACCESS_TIME_OFFSET, lastAccessTime);
238: }
239:
240: if (_container.needsLockFileThread()) {
241: // We give the other process a chance to
242: // write its lock.
243: Cool.sleepIgnoringInterruption(100);
244: _container.syncFiles();
245: timerFileLock().checkOpenTime();
246: }
247: if (oldLength < LENGTH) {
248: write();
249: }
250: }
251:
252: private boolean allowAutomaticShutdown() {
253: return configImpl().automaticShutDown();
254: }
255:
256: private boolean allowVersionUpdate() {
257: Config4Impl configImpl = configImpl();
258: return !configImpl.isReadOnly()
259: && configImpl.allowVersionUpdates();
260: }
261:
262: private Config4Impl configImpl() {
263: return _container.configImpl();
264: }
265:
266: public void write() {
267:
268: timerFileLock().checkHeaderLock();
269: addressChanged(_container.getSlot(LENGTH).address());
270:
271: StatefulBuffer writer = _container.getWriter(_container
272: .transaction(), _address, LENGTH);
273: IntHandler.writeInt(LENGTH, writer);
274: for (int i = 0; i < 2; i++) {
275: writer.writeLong(timerFileLock().openTime());
276: }
277: writer.writeByte(systemData().stringEncoding());
278: IntHandler.writeInt(0, writer);
279: IntHandler.writeInt(0, writer);
280: IntHandler.writeInt(_bootRecordID, writer);
281: IntHandler.writeInt(0, writer); // dead byte from wrong attempt for blocksize
282: writer.append(passwordToken());
283: writer.writeByte(systemData().freespaceSystem());
284: _container.ensureFreespaceSlot();
285: IntHandler.writeInt(systemData().freespaceAddress(), writer);
286: IntHandler.writeInt(systemData().converterVersion(), writer);
287: IntHandler.writeInt(systemData().uuidIndexId(), writer);
288: writer.write();
289: writePointer();
290: _container.syncFiles();
291: }
292:
293: private void addressChanged(int address) {
294: _address = address;
295: timerFileLock().setAddresses(_address, OPEN_TIME_OFFSET,
296: ACCESS_TIME_OFFSET);
297: }
298:
299: private void writePointer() {
300: timerFileLock().checkHeaderLock();
301: StatefulBuffer writer = _container.getWriter(_container
302: .transaction(), 0, Const4.ID_LENGTH);
303: writer.moveForward(2);
304: IntHandler.writeInt(_address, writer);
305: writer.noXByteCheck();
306: writer.write();
307: timerFileLock().writeHeaderLock();
308: }
309:
310: public int address() {
311: return _address;
312: }
313:
314: public void close() throws Db4oIOException {
315: timerFileLock().close();
316: }
317:
318: }
|