use of org.apache.derby.io.StorageRandomAccessFile in project derby by apache.
the class LogToFile method initializeReplicationSlaveRole.
/**
* Initializes logOut so that log received from the replication
* master can be appended to the log file.
*
* Normally, logOut (the file log records are appended to) is set
* up as part of the recovery process. When the database is booted
* in replication slave mode, however, recovery will not get to
* the point where logOut is initialized until this database is no
* longer in slave mode. Since logOut is needed to append log
* records received from the master, logOut needs to be set up for
* replication slave mode.
*
* This method finds the last log record in the log file with the
* highest number. logOut is set up so that log records will be
* appended to the end of that file, and the endPosition and
* lastFlush variables are set to point to the end of the same
* file. All this is normally done as part of recovery.
*
* After the first log file switch resulting from applying log
* received from the master, recovery will be allowed to read up
* to, but not including, the current log file which is the file
* numbered logFileNumber.
*
* Note that this method must not be called until LogToFile#boot()
* has completed. Currently, this is ensured because RawStore#boot
* starts the SlaveFactory (in turn calling this method) after
* LogFactory.boot() has completed. Race conditions for
* logFileNumber may occur if this is changed.
*
* @exception StandardException Standard Derby error policy
*/
public void initializeReplicationSlaveRole() throws StandardException {
if (SanityManager.DEBUG) {
SanityManager.ASSERT(inReplicationSlaveMode, "This method should only be used when" + " in slave replication mode");
}
try {
// Find the log file with the highest file number on disk
while (getLogFileAtBeginning(logFileNumber + 1) != null) {
logFileNumber++;
}
// Scan the highest log file to find it's end.
long startInstant = LogCounter.makeLogInstantAsLong(logFileNumber, LOG_FILE_HEADER_SIZE);
long logEndInstant = LOG_FILE_HEADER_SIZE;
StreamLogScan scanOfHighestLogFile = (StreamLogScan) openForwardsScan(startInstant, (LogInstant) null);
ArrayInputStream scanInputStream = new ArrayInputStream();
while (scanOfHighestLogFile.getNextRecord(scanInputStream, null, 0) != null) {
logEndInstant = scanOfHighestLogFile.getLogRecordEnd();
}
setEndPosition(LogCounter.getLogFilePosition(logEndInstant));
// endPosition and logFileNumber now point to the end of the
// highest log file. This is where a new log record should be
// appended.
/*
* Open the highest log file and make sure log records are
* appended at the end of it
*/
StorageRandomAccessFile logFile = null;
if (isWriteSynced) {
logFile = openLogFileInWriteMode(getLogFileName(logFileNumber));
} else {
logFile = privRandomAccessFile(getLogFileName(logFileNumber), "rw");
}
logOut = new LogAccessFile(this, logFile, logBufferSize);
lastFlush = endPosition;
// append log records at the end of
logFile.seek(endPosition);
// the file
} catch (IOException ioe) {
throw StandardException.newException(SQLState.REPLICATION_UNEXPECTED_EXCEPTION, ioe);
}
}
use of org.apache.derby.io.StorageRandomAccessFile in project derby by apache.
the class LogToFile method writeControlFile.
/**
* Carefully write out this value to the control file.
* We do safe write of this data by writing the data
* into two files every time we write the control data.
* we write checksum at the end of the file, so if by
* chance system crashes while writing into the file,
* using the checksum we find that the control file
* is hosed then we use the mirror file, which will have
* the control data written at last check point.
*
* see comment at beginning of file for log control file format.
*
* <P> MT- synchronized by caller
*/
// When changing this code, also update the comment at the beginning of
// this class, the ControlFileReader of DERBY-5195, and the description
// on the web page in http://db.apache.org/derby/papers/logformats.html
boolean writeControlFile(StorageFile logControlFileName, long value) throws IOException, StandardException {
StorageRandomAccessFile logControlFile = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
DataOutputStream daos = new DataOutputStream(baos);
daos.writeInt(fid);
// so that when this db is booted by 1.1x and 1.2x JBMS, a IOException
// stack trace rather than some error message that tells
// the user to delete the database will show up.
daos.writeInt(OBSOLETE_LOG_VERSION_NUMBER);
daos.writeLong(value);
if (onDiskMajorVersion == 0) {
onDiskMajorVersion = jbmsVersion.getMajorVersion();
onDiskMinorVersion = jbmsVersion.getMinorVersion();
onDiskBeta = jbmsVersion.isBeta();
}
// previous to 1.3, that's all we wrote.
// from 1.3 and onward, also write out the JBMSVersion
daos.writeInt(onDiskMajorVersion);
daos.writeInt(onDiskMinorVersion);
// For 2.0 beta we added the build number and the isBeta indication.
// (5 bytes from our first spare long)
daos.writeInt(jbmsVersion.getBuildNumberAsInt());
byte flags = 0;
if (onDiskBeta)
flags |= IS_BETA_FLAG;
// previously booted at any time in this mode
if (logNotSynced || wasDBInDurabilityTestModeNoSync)
flags |= IS_DURABILITY_TESTMODE_NO_SYNC_FLAG;
daos.writeByte(flags);
//
// write some spare bytes after 2.0 we have 3 + 2(8) spare bytes.
long spare = 0;
daos.writeByte(0);
daos.writeByte(0);
daos.writeByte(0);
daos.writeLong(spare);
daos.flush();
// write the checksum for the control data written
checksum.reset();
checksum.update(baos.toByteArray(), 0, baos.size());
daos.writeLong(checksum.getValue());
daos.flush();
try {
checkCorrupt();
try {
logControlFile = privRandomAccessFile(logControlFileName, "rw");
} catch (IOException ioe) {
logControlFile = null;
return false;
}
if (!privCanWrite(logControlFileName))
return false;
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON(TEST_LOG_FULL))
testLogFull();
}
logControlFile.seek(0);
logControlFile.write(baos.toByteArray());
syncFile(logControlFile);
logControlFile.close();
// write the same data to mirror control file
try {
logControlFile = privRandomAccessFile(getMirrorControlFileName(), "rw");
} catch (IOException ioe) {
logControlFile = null;
return false;
}
logControlFile.seek(0);
logControlFile.write(baos.toByteArray());
syncFile(logControlFile);
} finally {
if (logControlFile != null)
logControlFile.close();
}
return true;
}
use of org.apache.derby.io.StorageRandomAccessFile in project derby by apache.
the class LogToFile method verifyLogFormat.
/*
* Private methods that helps to implement methods of LogFactory
*/
/**
* Verify that we the log file is of the right format and of the right
* version and log file number.
*
* <P>MT - not needed, no global variables used
*
* @param logFileName the name of the log file
* @param number the log file number
* @return true if the log file is of the current version and of the
* correct format
*
* @exception StandardException Standard Derby error policy
*/
private boolean verifyLogFormat(StorageFile logFileName, long number) throws StandardException {
boolean ret = false;
try {
StorageRandomAccessFile log = privRandomAccessFile(logFileName, "r");
ret = verifyLogFormat(log, number);
log.close();
} catch (IOException ioe) {
}
return ret;
}
use of org.apache.derby.io.StorageRandomAccessFile in project derby by apache.
the class LogToFile method getLogFileAtPosition.
/**
* Get a read-only handle to the log file positioned at the stated position
*
* <P> MT- read only
*
* @return null if file does not exist or of the wrong format
* @exception IOException cannot access the log at the new position.
* @exception StandardException Standard Derby error policy
*/
protected StorageRandomAccessFile getLogFileAtPosition(long logInstant) throws IOException, StandardException {
checkCorrupt();
long filenum = LogCounter.getLogFileNumber(logInstant);
long filepos = LogCounter.getLogFilePosition(logInstant);
StorageFile fileName = getLogFileName(filenum);
if (!privExists(fileName)) {
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
SanityManager.DEBUG(LogToFile.DBG_FLAG, fileName.getPath() + " does not exist");
}
return null;
}
StorageRandomAccessFile log = null;
try {
log = privRandomAccessFile(fileName, "r");
// verify that the log file is of the right format
if (!verifyLogFormat(log, filenum)) {
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
SanityManager.DEBUG(LogToFile.DBG_FLAG, fileName.getPath() + " format mismatch");
}
log.close();
log = null;
} else {
log.seek(filepos);
}
} catch (IOException ioe) {
try {
if (log != null) {
log.close();
log = null;
}
if (SanityManager.DEBUG) {
SanityManager.THROWASSERT("cannot get to position " + filepos + " for log file " + fileName.getPath(), ioe);
}
} catch (IOException ioe2) {
}
throw ioe;
}
return log;
}
use of org.apache.derby.io.StorageRandomAccessFile in project derby by apache.
the class JCECipherFactory method verifyKey.
/**
* The database can be encrypted with an encryption key given in connection url.
* For security reasons, this key is not made persistent in the database.
*
* But it is necessary to verify the encryption key when booting the database if it is similar
* to the one used when creating the database
* This needs to happen before we access the data/logs to avoid the risk of corrupting the
* database because of a wrong encryption key.
*
* This method performs the steps necessary to verify the encryption key if an external
* encryption key is given.
*
* At database creation, 4k of random data is generated using SecureRandom and MD5 is used
* to compute the checksum for the random data thus generated. This 4k page of random data
* is then encrypted using the encryption key. The checksum of unencrypted data and
* encrypted data is made persistent in the database in file by name given by
* Attribute.CRYPTO_EXTERNAL_KEY_VERIFYFILE (verifyKey.dat). This file exists directly under the
* database root directory.
*
* When trying to boot an existing encrypted database, the given encryption key is used to decrypt
* the data in the verifyKey.dat and the checksum is calculated and compared against the original
* stored checksum. If these checksums dont match an exception is thrown.
*
* Please note, this process of verifying the key does not provide any added security but only is
* intended to allow to fail gracefully if a wrong encryption key is used
*
* StandardException is thrown if there are any problems during the process of verification
* of the encryption key or if there is any mismatch of the encryption key.
*/
public void verifyKey(boolean create, StorageFactory sf, Properties properties) throws StandardException {
if (properties.getProperty(Attribute.CRYPTO_EXTERNAL_KEY) == null)
return;
// if firstTime ( ie during creation of database, initial key used )
// In order to allow for verifying the external key for future database boot,
// generate random 4k of data and store the encrypted random data and the checksum
// using MD5 of the unencrypted data. That way, on next database boot a check is performed
// to verify if the key is the same as used when the database was created
InputStream verifyKeyInputStream = null;
StorageRandomAccessFile verifyKeyFile = null;
byte[] data = new byte[VERIFYKEY_DATALEN];
try {
if (create) {
getSecureRandom().nextBytes(data);
// get the checksum
byte[] checksum = getMD5Checksum(data);
CipherProvider tmpCipherProvider = createNewCipher(ENCRYPT, mainSecretKey, mainIV);
tmpCipherProvider.encrypt(data, 0, data.length, data, 0);
// openFileForWrite
verifyKeyFile = privAccessFile(sf, Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE, "rw");
// write the checksum length as int, and then the checksum and then the encrypted data
verifyKeyFile.writeInt(checksum.length);
verifyKeyFile.write(checksum);
verifyKeyFile.write(data);
verifyKeyFile.sync();
} else {
// Read from verifyKey.dat as an InputStream. This allows for
// reading the information from verifyKey.dat successfully even when using the jar
// subprotocol to boot derby. (DERBY-1373)
verifyKeyInputStream = privAccessGetInputStream(sf, Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
DataInputStream dis = new DataInputStream(verifyKeyInputStream);
// then read the checksum length
int checksumLen = dis.readInt();
byte[] originalChecksum = new byte[checksumLen];
dis.readFully(originalChecksum);
dis.readFully(data);
// decrypt data with key
CipherProvider tmpCipherProvider = createNewCipher(DECRYPT, mainSecretKey, mainIV);
tmpCipherProvider.decrypt(data, 0, data.length, data, 0);
byte[] verifyChecksum = getMD5Checksum(data);
if (!MessageDigest.isEqual(originalChecksum, verifyChecksum)) {
throw StandardException.newException(SQLState.ENCRYPTION_BAD_EXTERNAL_KEY);
}
}
} catch (IOException ioe) {
throw StandardException.newException(SQLState.ENCRYPTION_UNABLE_KEY_VERIFICATION, ioe);
} finally {
try {
if (verifyKeyFile != null)
verifyKeyFile.close();
if (verifyKeyInputStream != null)
verifyKeyInputStream.close();
} catch (IOException ioee) {
throw StandardException.newException(SQLState.ENCRYPTION_UNABLE_KEY_VERIFICATION, ioee);
}
}
return;
}
Aggregations