/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ //---------------------------------------------------------------------------- // // Module: LogHandle.java // // Description: Log file handle. // // Product: com.sun.jts.CosTransactions // // Author: Simon Holdsworth // // Date: March, 1997 // // Copyright (c): 1995-1997 IBM Corp. // // The source code for this program is not published or otherwise divested // of its trade secrets, irrespective of what has been deposited with the // U.S. Copyright Office. // // This software contains confidential and proprietary information of // IBM Corp. //---------------------------------------------------------------------------- package com.sun.jts.CosTransactions; // Import required classes. import com.sun.enterprise.util.i18n.StringManager; import java.util.*; import java.io.*; /**A class containing attributes of an open log file. * * @version 0.01 * * @author Simon Holdsworth, IBM Corporation * * @see */ //---------------------------------------------------------------------------- // CHANGE HISTORY // // Version By Change Description // 0.01 SAJH Initial implementation. //----------------------------------------------------------------------------- class LogHandle { private static final StringManager sm = StringManager.getManager(LogHandle.class); // WriteMode identifies the mode in which a system journal record // is to be written, and affects the performance overhead of the write. /**Buffer the data and return (minimal overhead); */ final static int BUFFER = 0; /**Flush and force the data to permanent storage before returning (high * overhead) - ie to physically write the record. */ final static int FORCE = 1; // This type enumerates the options when truncating a log file. /**Don't include tail LSN */ final static int TAIL_NOT_INCLUSIVE = 0; /**Include tail LSN */ final static int TAIL_INCLUSIVE = 1; // Records written to the Master Log are allocated log record types /**Start-of-checkpoint record - internally generated by &damjo. */ final static int START_CKPT = 0; /**Checkpoint record from an individual registered module */ final static int INDV_CKPT = 1; /**End-of-checkpoint record - internally generated by &damjo */ final static int END_CKPT = 2; /**Record of a newly opened journal */ final static int NEW_JRNL = 3; /**Upper limit for user specified record type value. */ final static int RECORD_TYPE_MAX = 0xFFFF; /**Record type written to local system logs to indicate the position * corresponding to the start of the last successful checkpoint. */ final static int MARKER = RECORD_TYPE_MAX; /**The record type written to at the end of an extent to signify that the * log record is a link record (a dummy log record). */ final static int LINK = RECORD_TYPE_MAX + 1; // Constants used for log files. /**The maximum number of extents which can be created for a log file. */ final static int MAX_NUMBER_EXTENTS = 0xFFFFFFFF; /**Number of log write operations which will be performed before forcing * the control data to permanent storage */ // final static int CONTROL_FORCE_INTERVAL = 20; final static int CONTROL_FORCE_INTERVAL = 100; /**This determines the size of the largest log record which can be written. */ final static int MAX_EXTENT_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*16; /**This is the size of the cushion file used to find if the log is * short on space. */ final static int CUSHION_SIZE = MAX_EXTENT_SIZE; /**The length of the name assigned to a logfile. This is restricted to * 8 to support the FAT file system. */ final static int NAME_LENGTH = 8; /**The maximum number of names available to be assigned for logfiles. * The name is made up of LOG_FILENAME_PREFIX which is 5 characters * followed by a 3 digit hex extension. */ final static int MAX_NAMES = 4096; /**The length of the fixed filename prefix used when allocating new * log file names. */ final static int FILENAME_PREFIX_LEN = 5; /**The number of entries in each log file descriptor. It is used for * performance reason, so we can get to the extent descriptor quickly. */ final static int EXTENT_TABLE_SIZE = 16; /**This is used to give the maximum length of a log file name, which also * inclues the NULL terminator. 200 for the logname was derived from : * /var/cics_regions/region_name/log/<logname>.extent.00000001 * We know the maximum region name is 8 chars, therefore every character * except the logname added upto 46. Hence 255 (AIX path max) - 46 is 209, * however 200 is a nice round (and large number). */ //final static int NAME_MAX_SIZE = 200; /**This is the reason why we are calling the calling back function. */ final static int CALLBACK_REASON_SOS = 1; /**The offset in the control file for the first restart data record. */ final static int RESTART_OFFSET_1 = LogFileHandle.FILESYSTEM_BLOCKSIZE; /**The offset in the control file for the second restart data record. */ final static int RESTART_OFFSET_2 = LogFileHandle.FILESYSTEM_BLOCKSIZE*5; /**This is the maximum size of a log record. */ final static int MAX_RECORD_SIZE = MAX_EXTENT_SIZE - 2*LogRecordHeader.SIZEOF - 2*LogRecordEnding.SIZEOF; /**The maximum size of a restart record */ final static int MAX_RESTART_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*4 - 2*LogRestartDescriptor.SIZEOF; /**The size of a control file which is allocated at open time. */ final static int CONTROL_FILE_SIZE = RESTART_OFFSET_2 + MAX_RESTART_SIZE + 2*LogRestartDescriptor.SIZEOF; /**The size of a chunk to allocate from the disk space */ final static int ALLOCATE_SIZE = MAX_EXTENT_SIZE; // Instance members LogHandle blockValid = null; int restartDataLength = 0; int recordsWritten = 0; int chunkRemaining = 0; int activeRestartVersion = 0; LogUpcallTarget upcallTarget = null; ArrayList cursors = null; boolean cushionExists = false; boolean upcallInProgress = false; Hashtable extentTable = null; String logFileName = null; LogFileHandle logFileHandle = null; LogControlDescriptor logControlDescriptor = null; LogControl logControl = null; /**Creates a LogHandle object for the given log instance. * * @param control The log instance. * @param logName The name of the log. * @param controlFH The handle of the control file. * @param upcall The log upcall. * * @return * * @exception LogException The creation failed. * * @see */ LogHandle( LogControl control, String logName, LogFileHandle controlFH, LogUpcallTarget upcall ) throws LogException { // Initialise instance members. logFileName = logName; upcallTarget = upcall; logControl = control; logFileHandle = controlFH; blockValid = this; logControlDescriptor = new LogControlDescriptor(); cursors = new ArrayList(); extentTable = new Hashtable(EXTENT_TABLE_SIZE); } /**Writes a record to the log. * * @param record The log record. * @param recordType The log record type. * @param writeMode The write mode. * * @return The LSN of the written record * * @exception LogException The write failed. * * @see */ synchronized LogLSN writeRecord( byte[] record, int recordType, int writeMode ) throws LogException { // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // IF ReadOnly log // Return LOG_READ_ONLY_ACCESS if( logControl.logReadOnly ) throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3); // Sanity check the recordType and writeMode parameters if( recordType > RECORD_TYPE_MAX ) throw new LogException(null,LogException.LOG_INVALID_RECORDTYPE,5); if( writeMode != FORCE && writeMode != BUFFER ) throw new LogException(null,LogException.LOG_INVALID_WRITEMODE,6); // Calculate the total size of the log record by totalling size of all // input buffers together with the record header and record ending int recordSize = record.length + LogRecordHeader.SIZEOF + LogRecordEnding.SIZEOF; // IF the log record data is greater than LOG_MAX_LOG_RECORD_SIZE // Unlock the log file latch // Return LOG_RECORD_TOO_LARGE if( recordSize > MAX_RECORD_SIZE ) throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,7); // Calculate the remaining space in the current extent by subtracting // (log head LSN's offset + 2*LOG_HEADER_SIZE + LOG_ENDING_SIZE) from // LOG_MAX_EXTENT_SIZE int remainingSpace = MAX_EXTENT_SIZE - ( logControlDescriptor.nextLSN.offset + 2*LogRecordHeader.SIZEOF + LogRecordEnding.SIZEOF ); // Position the file pointer to the next free location // NOTE: either the record or a link record will be wrote here // Set the WORKING extent descriptor to the returned value // // IF an error occurs let it go to the caller. LogExtent logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE); // IF not enough space in current extent if( remainingSpace < recordSize ) { LogRecordHeader link = new LogRecordHeader(); // Calculate the number of the next (new) extent // Calculate LSN of first record in the new extent. // Test that the new extent number has not wrapped to become negative; // if it has, throw an exception. int nextExtent = logControlDescriptor.headLSN.extent+1; if( nextExtent < 0 ) throw new LogException(null,LogException.LOG_WRITE_FAILURE,8); // If the new extent file is already open, there is nothing we can do but // fail. We cannot run the short-on-storage upcall to try to free the // extent as the upcall needs to write information to the offending extent. if( extentTable.containsKey(LogExtent.modExtent(nextExtent)) ) throw new LogException(null,LogException.LOG_WRITE_FAILURE,9); // Create link record containing // - the LSN of the link record (i.e. its own LSN) // - the LSN of the previous log record (log head LSN from // Log_FileDescriptor block) // - the LSN of the next log record (this is the LSN of the // first record in new extent file link.recordType = LINK; link.previousLSN = new LogLSN(logControlDescriptor.headLSN); link.currentLSN = new LogLSN(logControlDescriptor.nextLSN); link.nextLSN = new LogLSN(nextExtent,0); // Move a file pointer to the next record position LogExtent nextEDP = positionFilePointer(link.nextLSN,0,LogExtent.ACCESSTYPE_WRITE); // Issue WRITE to add link record to the 'full' extent file // IF the WRITE fails // Close the new extent file // Unchain its extent descriptor block from the hash table // Deallocate the extent descriptor block // Unlock the log file latch // Return LOG_WRITE_FAILURE byte[] linkBytes = new byte[link.SIZEOF]; link.toBytes(linkBytes,0); int bytesWritten = 0; try { bytesWritten = logEDP.fileHandle.fileWrite(linkBytes); } catch( LogException le ) { extentTable.remove(logControlDescriptor.headLSN.extent); nextEDP.doFinalize(); throw new LogException(LogException.LOG_WRITE_FAILURE, 10, sm.getString("jts.log_add_link_failed"), le); } // Set its 'extent written' flag to TRUE logEDP.writtenSinceLastForce = true; logEDP.cursorPosition += bytesWritten; // Update the head LSN value in the Log_FileDescriptor block // with the LSN of the link record // Update the next LSN value in the Log_FileDescriptor block // with the LSN of the first block in the new extent logControlDescriptor.headLSN.copy(link.currentLSN); logControlDescriptor.nextLSN.copy(link.nextLSN); // Set the WORKING extent descriptor to the new/next extent logEDP = nextEDP; // Set the ChunkRemaining to Zero chunkRemaining = 0; } // Use the offset value from the next LSN to calculate the next free offset // in the extent file // Calculate the 'next free' LSN LogLSN nextFree = new LogLSN(logControlDescriptor.nextLSN.extent, logControlDescriptor.nextLSN.offset + recordSize); // Build the record header, initialising with // - log record type (recordType passed as input parameter) // - log record length (cumulative length of all data buffers) // - the LSN of the previous log record (PreviousRecord; log head LSN from // Log_FileDescriptor block) // - the LSN of the next log record (NextRecord; the 'next free' LSN value) // - the LSN of the record about to be written (ThisRecord) LogRecordHeader logRH = new LogRecordHeader(); logRH.recordType = recordType; logRH.recordLength = record.length; logRH.nextLSN = nextFree; logRH.previousLSN = new LogLSN(logControlDescriptor.headLSN); logRH.currentLSN = new LogLSN(logControlDescriptor.nextLSN); // Build the record ending, initialising with // the LSN of the record about to be written (ThisRecord) LogRecordEnding logRE = new LogRecordEnding(); logRE.currentLSN = logRH.currentLSN; // Initialise an array of iovec structures ready for a WRITEV request // (an iovec structure specifies the base address and length of an area in // memory from which data should be written) // - set the first element to point to the record header, set iovCount=1 // - LOOP for each buffer in recordPtrList // initialise next iovec element with its address and length // increment iovCount // ENDLOOP // - set the next element to point to the record ending, increment iovCount byte[] writeBytes = new byte[LogRecordHeader.SIZEOF+record.length+LogRecordEnding.SIZEOF]; logRH.toBytes(writeBytes,0); System.arraycopy(record,0,writeBytes,LogRecordHeader.SIZEOF,record.length); logRE.toBytes(writeBytes,LogRecordHeader.SIZEOF+record.length); // IF there is enough space in current chunk // Decrease ChunkRemaining by RecordSize boolean cushionFreed = false; if( chunkRemaining > recordSize ) chunkRemaining -= recordSize; else { // CALCULATE the size of disk space to grab int grabSize = chunkRemaining + ALLOCATE_SIZE; // IF there is NOT enough space in current extent // Set the Grab size to be the size of the remaining extent if( grabSize + logControlDescriptor.nextLSN.offset > MAX_EXTENT_SIZE ) grabSize = MAX_EXTENT_SIZE - logControlDescriptor.nextLSN.offset; // Set the Allocate success flag to FALSE; boolean allocateSuccess = false; do { // ALLOCATE the Grab size of disk space // IF successful // Set AllocateSuccess to TRUE // BREAK try { logEDP.fileHandle.allocFileStorage(grabSize); } catch( LogException le ) { // IF the request fails due to lack of storage, i.e. // ENOSPC - insufficient space left in file system or // EDQUOT - user or group disk block quota reached // Call the Log_FreeCushion routine // IF there was no cushion to free // Unlock the log file latch // Return LOG_NO_SPACE // Move the File pointer back to it's original offset // ELSE // EXIT LOOP with 'Allocate unsuccessful' status if( le.errorCode == LogException.LOG_NO_SPACE ) { if( cushionExists ) { freeCushion(); cushionFreed = true; } else { if( cushionFreed ) restoreCushion(false); throw new LogException(LogException.LOG_NO_SPACE,11, null, le); } try { logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE); } catch( Throwable e ) {}; } else allocateSuccess = false; } allocateSuccess = true; } while( !allocateSuccess ); // IF allocate failed // Unlock the log file latch // Return LOG_WRITE_FAILURE if (!allocateSuccess) { throw new LogException(LogException.LOG_WRITE_FAILURE, 12, sm.getString("jts.log_allocate_failed"), (Throwable) null); } // SET ChunkRemaining to the Grabbed size - RecordSize chunkRemaining = grabSize - recordSize; } // Issue a WRITEV request to the extent file, specifying the iovec array // and iovCount as input // IF write failed return the error. int bytesWritten = logEDP.fileHandle.fileWrite(writeBytes); // Set 'extent written' flag to TRUE logEDP.writtenSinceLastForce = true; logEDP.cursorPosition += bytesWritten; // IF LOG_FORCE was specified // LOOP through each extent chain in the hash table // IF 'extent written' flag is TRUE // Issue FSYNC for extent file descriptor // IF not successful // Unlock the log file latch // Return LOG_ERROR_FORCING_LOG // Set 'extent written' flag to FALSE // ENDLOOP if( writeMode == FORCE ) { Enumeration extents = extentTable.elements(); while( extents.hasMoreElements() ) { LogExtent nextEDP = (LogExtent)extents.nextElement(); if( nextEDP.writtenSinceLastForce ) try { nextEDP.fileHandle.fileSync(); nextEDP.writtenSinceLastForce = false; } catch (LogException le) { throw new LogException(LogException.LOG_ERROR_FORCING_LOG, 14, sm.getString("jts.log_file_sync_failed"), le); } } } // Update the head LSN and 'next free' LSN in the Log_FileDescriptor // block logControlDescriptor.headLSN.copy(logRH.currentLSN); logControlDescriptor.nextLSN.copy(logRH.nextLSN); // Increment the RecordsWritten counter in Log_FileDescriptor block recordsWritten++; // IF RecordsWritten = LOG_CONTROL_FORCE_INTERVAL or LOG_FORCE was specified // Write the Log_ControlDescriptor structure (embedded in the // Log_FileDescriptor block out to the control file (implied sync) // IF not successful let the error pass to the caller. // Reset the RecordsWritten counter to zero // IF LogCushionOK is FALSE // Call RestoreLogCushion Routine if( recordsWritten >= CONTROL_FORCE_INTERVAL ) { writeControlFile(); recordsWritten = 0; } if( cushionFreed ) restoreCushion(true); // Return the written LSN as the result of the write operation. LogLSN result = new LogLSN(logRH.currentLSN); return result; } /**Reads a record from the log. * * @param readLSN The LSN of the record to be read. * @param type An array with a single element which will be set to the type * of the record read. * * @return The record read in. * * @exception LogException The read failed. * * @see */ synchronized byte[] readRecord( LogLSN readLSN, int[/*1*/] type ) throws LogException { // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // IF the log file is empty (head LSN equal to LOG_NULL_LSN) // Unlock the log file latch // Return LOG_INVALID_LSN if( logControlDescriptor.headLSN.isNULL() ) throw new LogException(null,LogException.LOG_INVALID_LSN,3); // IF the lsn specified is LOG_HEAD_LSN or LOG_TAIL_LSN // substitute the current head or tail LSN from the // Log_ControlDescriptor structure // ELSE // Ensure that the lsn specified is <= current head LSN and // >= current tail LSN // IF lsn does not pass these checks // Unlock the log file latch // Return LOG_INVALID_LSN LogLSN lsn; if( readLSN.equals(LogLSN.HEAD_LSN) ) lsn = logControlDescriptor.headLSN; else if( readLSN.equals(LogLSN.TAIL_LSN) ) lsn = logControlDescriptor.tailLSN; else if( readLSN.lessThan(logControlDescriptor.tailLSN) || readLSN.greaterThan(logControlDescriptor.headLSN) ) throw new LogException(null,LogException.LOG_INVALID_LSN,4); else lsn = readLSN; // Position the file pointer to the LSN specified // IF not successful allow the error to pass to the caller. LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ); // Issue a READ for the log header record // IF the READ was not successful // Unlock the log file latch // Return LOG_READ_FAILURE byte[] headerBytes = new byte[LogRecordHeader.SIZEOF]; int bytesRead = 0; try { bytesRead = logEDP.fileHandle.fileRead(headerBytes); } catch (LogException le) { logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN; throw new LogException(le.errorCode, 6, sm.getString("jts.log_read_header_failed"), le); } LogRecordHeader logRH = new LogRecordHeader(headerBytes,0); logEDP.cursorPosition += bytesRead; // Check the record type is not a LOG_LINK_RECORD_TYPE && // the LSN in the header record is same as lsn parameter // IF either test fails // Unlock the log file latch // Return LOG_INVALID_LSN if( logRH.recordType == LINK || !logRH.currentLSN.equals(lsn) ) throw new LogException(null,LogException.LOG_INVALID_LSN,7); // Set up a 2-element iovec array to enable the log record data and record // ending to be read into a separate buffers // Issue a READV request for the extent file, passing the iovec array as // an input parameter // IF the READV was not successful // Unlock the log file latch // Return LOG_READ_FAILURE byte[][] readVect = new byte[2][]; readVect[0] = new byte[logRH.recordLength]; readVect[1] = new byte[LogRecordEnding.SIZEOF]; try { bytesRead = logEDP.fileHandle.readVector(readVect); } catch( LogException le ) { logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN; throw new LogException(le.errorCode,9, sm.getString("jts.log_readvector_failed"), le); } LogRecordEnding logRE = new LogRecordEnding(readVect[1],0); logEDP.cursorPosition += bytesRead; // IF the LSN contained in the record ending != lsn parameter // Unlock the log file latch // Return LOG_CORRUPTED if( !logRE.currentLSN.equals(lsn) ) throw new LogException(null,LogException.LOG_CORRUPTED,10); // Copy the returned number of bytes into the recordLengthP parameter and // the record type value into the recordTypeP parameter. type[0] = logRH.recordType; return readVect[0]; } /**Writes the restart record. * * @param buffer The record to be written. * * @return * * @exception LogException The write failed. * * @see */ synchronized void writeRestart( byte[] buffer ) throws LogException { // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // IF ReadOnly log // Return LOG_READ_ONLY_ACCESS if( logControl.logReadOnly ) throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3); // IF the bufferLength parameter is greater than LOG_MAX_RESTART_RECORD_SIZE // Return LOG_RECORD_TOO_LARGE if( buffer.length > MAX_RESTART_SIZE ) throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,4); // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is still valid if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,5); // Use the value in ActiveRestartVersion field showing which is the active // to determine which is the alternate restart record // Use LSEEK to move the file pointer to its offset int alternate = alternateRestart(activeRestartVersion); int restartOffset = restartPosition(alternate); logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE); // Initialise a Log_RestartDescriptor block with // - the current file pointer offset (copied into RestartValid field) // - the length of the restart data (DataLength field) // - a timestamp obtained from the seconds field of a gettimer call LogRestartDescriptor logRD = new LogRestartDescriptor(); logRD.restartDataLength = buffer.length; logRD.timeStamp = (int)new Date().getTime(); logRD.restartValid = restartOffset; // Set up a 3-element iovec array with the first element 'containing' // the Log_RestartDescriptor block, the second, the supplied // restart data and the third, the Log_RestartDescriptor block again. byte[] writeBytes = new byte[LogRestartDescriptor.SIZEOF*2+buffer.length]; logRD.toBytes(writeBytes,0); System.arraycopy(buffer,0,writeBytes,LogRestartDescriptor.SIZEOF,buffer.length); logRD.toBytes(writeBytes,LogRestartDescriptor.SIZEOF+buffer.length); // Issue a WRITEV request to copy the restart data to the control file // IF successful // Data has now been written to permanent storage, so update // RestartDataLength field in Log_FileDescriptor with bufferLength // and indicate (value 1 or 2) in ActiveRestartVersion field that the // alternate has now become the active // Return LOG_SUCCESS // ELSE let the error pass to the caller. logFileHandle.fileWrite(writeBytes); activeRestartVersion = alternate; } /**Reads the restart record. * * @param * * @return The record read in. * * @exception LogException The read failed. * * @see */ synchronized byte[] readRestart() throws LogException { // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is still valid if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,3); // IF there is no restart data (restart length in Log_FileDescriptor // block is zero) // Return LOG_NO_RESTART_RECORD if( restartDataLength == 0 ) return new byte[0]; // Use the ActiveRestartVersion field in the Log_FileDescriptor block // to find out which restart record is currently the active one and // determine its offset within the control file // Use LSEEK to move the file pointer to the start of the restart record // Allow any error to pass to the caller. int restartOffset = restartPosition(activeRestartVersion); logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE); // Initialise an iovec array with the first element containing details of // a Log_RestartDescriptor block, the second containing details of // the callers buffer (bufferP and restart data length) and the third also // pointing to a Log_RestartDescriptor block byte[][] readVect = new byte[3][]; readVect[0] = new byte[LogRestartDescriptor.SIZEOF]; readVect[1] = new byte[restartDataLength]; readVect[2] = new byte[LogRestartDescriptor.SIZEOF]; // Issue a READV for the restart data // IF not successful let the error pass to the caller. logFileHandle.readVector(readVect); LogRestartDescriptor logRD = new LogRestartDescriptor(readVect[0],0); LogRestartDescriptor logRDEnd = new LogRestartDescriptor(readVect[2],0); // IF the offset value stored in the returned Log_RestartDescriptor // block is not equal to the offset of the record just read OR // the length held in the Log_RestartDescriptor block is not equal to // the restart data length held in the Log_FileDescriptor block OR // the first Log_RestartDescriptor block is not equal to the second // Return LOG_CORRUPTED if( logRD.restartValid != restartOffset || logRD.restartDataLength != restartDataLength || !logRD.equals(logRDEnd) ) throw new LogException(null,LogException.LOG_CORRUPTED,7); // Copy the restart data length from Log_RestartDescriptor block into // the callers recordLengthP parameter // Return LOG_SUCCESS return readVect[1]; } /**Closes (and optionally deletes) the log file. * * @param deleteFile Indicates whether file should be deleted. * * @return * * @exception LogException The close failed. * * @see */ synchronized void closeFile( boolean deleteFile ) throws LogException { // Check BlockValid field in Log_FileDescriptor block and // ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // Set the block valid to NULL blockValid = null; // LOOP for each of the 16 elements in the log file's extent hash table boolean forced = false; Enumeration extents = extentTable.elements(); while( extents.hasMoreElements() ) { LogExtent logEDP = (LogExtent)extents.nextElement(); // IF extent has been written since last force // Issue FSYNC for the extent's file descriptor // IF not successful // Return LOG_WRITE_FAILURE if( logEDP.writtenSinceLastForce ) { logEDP.fileHandle.fileSync(); logEDP.writtenSinceLastForce = false; forced = true; } // Issue a close for the extent file. // Allow any error to pass to the caller. logEDP.fileHandle.fileClose(); // If deletion of the logfile was requested, delete it. //Start IASRI 4720539 if( deleteFile ){ //if( !logEDP.file.delete() ) final LogExtent tmplogEDP = logEDP; Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return tmplogEDP.file.delete(); } } ); if(!isdeleted.booleanValue()) throw new LogException(null,LogException.LOG_CLOSE_FAILURE,6); } //End IASRI 4720539 // Address next block in chain // Clear the signature in the Log_ExtentDescriptor block // Deallocate the Log_ExtentDescriptor block extentTable.remove(logEDP.extentNumber); logEDP.doFinalize(); } // IF any log extents were forced (FSYNC'ed) // WRITE the Log_ControlDescriptor block to the control file (with // implied sync) // IF not successful allow the error to pass to the caller. // Return LOG_WRITE_FAILURE if( forced && !logControl.logReadOnly ) writeControlFile(); // Issue CLOSE for the control file // IF not successful allow the error to pass to the caller. logFileHandle.fileClose(); // logFileHandle.destroy(); // If deletion of the logfile was requested, delete it's // control File and the cushion file. if( deleteFile ) { // Delete the control file. // Start IASRI 4720539 //if( !logControl.controlFile.delete() ) Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return logControl.controlFile.delete(); } } ); if( !isdeleted.booleanValue() ) throw new LogException(null,LogException.LOG_CLOSE_FAILURE,7); // End IASRI 4720539 freeCushion(); // Finally remove the directory. // Start IASRI 4720539 //LogControl.directory(logFileName,logControl.directoryPath).delete(); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return LogControl.directory(logFileName,logControl.directoryPath).delete(); } } ); // End IASRI 4720539 } // Unchain the Log_FileDescriptor block from the RCA chain // the latch will be unset and terminated by Log_RemoveFileDescriptor logControl.removeFile(this); } /**Truncates the log at the given point. * * @param truncLSN The LSN of the truncation point. * @param inclusive Indicates whether truncation includes the LSN. * * @return * * @exception LogException The operation failed. * * @see */ synchronized void truncate( LogLSN truncLSN, int inclusive ) throws LogException { // Check BlockValid field in Log_FileDescriptor block and // ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // IF ReadOnly log // Return LOG_READ_ONLY_ACCESS if( logControl.logReadOnly ) throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3); // IF the log file is empty (head LSN = LOG_NULL_LSN) && // the lsn value specified is not equal to LOG_HEAD_LSN // Unlock the Log_FileDescriptor latch // Return LOG_NEW_TAIL_TOO_HIGH if( logControlDescriptor.headLSN.isNULL() ) { if( truncLSN.equals(LogLSN.HEAD_LSN) ) { return; } else throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,6); } // IF the lsn parameter is equal to the symbolic LOG_HEAD_LSN or // the lsn parameter is equal to the actual log head LSN // Copy head LSN from Log_FileDescriptor into lsn // Remember that head of log is being truncated // ELSE IF the lsn parameter is equal to LOG_TAIL_LSN // Copy tail LSN from Log_FileDescriptor into lsn // ELSE Copy lsn parameter into lsn LogLSN lsn; boolean truncateHead = false; if( truncLSN.equals(LogLSN.HEAD_LSN) || truncLSN.equals(logControlDescriptor.headLSN) ) { lsn = new LogLSN(logControlDescriptor.headLSN); truncateHead = true; } else if( truncLSN.equals(LogLSN.TAIL_LSN) ) lsn = new LogLSN(logControlDescriptor.tailLSN); else lsn = new LogLSN(truncLSN); // Check the lsn parameter to ensure it is within the range of log records // IF lsn < log tail LSN (in Log_FileDescriptor) // Unlock the Log_FileDescriptor latch // Return LOG_NEW_TAIL_TOO_LOW // ELSE // IF lsn > log head LSN // Unlock the Log_FileDescriptor latch // Return LOG_NEW_TAIL_TOO_HIGH if( lsn.lessThan(logControlDescriptor.tailLSN) ) throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_LOW,7); else if( lsn.greaterThan(logControlDescriptor.headLSN) ) throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,8); // IF log head is being truncated && // inclusive parameter = LOG_TAIL_NOT_INCLUSIVE // Set Truncation record to the lsn specified (head LSN) // and the New Tail LSN to the next lsn; // ELSE // set truncation record and new log tail LSN depending // on whether or not LOG_TAIL_INCLUSIVE was set. Either way the // record pointed to by the current lsn must be read first LogLSN truncationRecord; LogLSN newTailRecord; boolean truncLastExtent = false; if( truncateHead && inclusive == TAIL_NOT_INCLUSIVE ) { truncationRecord = new LogLSN(lsn); newTailRecord = new LogLSN(logControlDescriptor.nextLSN); } else { // IF inclusive parameter = LOG_TAIL_INCLUSIVE and // lsn parameter = log tail LSN (in Log_FileDescriptor) // (then there is nothing to truncate) // Unlock the Log_FileDescriptor latch // Return LOG_SUCCESS if( inclusive == TAIL_INCLUSIVE && lsn.equals(logControlDescriptor.tailLSN) ) { return; } // Call Log_PositionFilePointer to position file pointer at the // start of the record specified by the lsn parameter // Allow any error to pass to the caller. LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ); // Issue READ for the log record header // IF not successful return LOG_READ_ERROR byte[] headerBytes = new byte[LogRecordHeader.SIZEOF]; int bytesRead = 0; try { bytesRead = logEDP.fileHandle.fileRead(headerBytes); } catch (LogException le) { logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN; throw new LogException(LogException.LOG_READ_FAILURE, 11, sm.getString("jts.log_read_header_failed"), le); } logEDP.cursorPosition += bytesRead; LogRecordHeader recordHeader = new LogRecordHeader(headerBytes,0); // Check that retrieved record is not an extent link record // IF it is // Unlock the Log_FileDescriptor latch // Return LOG_INVALID_TAIL if( recordHeader.recordType == LINK ) throw new LogException(null,LogException.LOG_INVALID_TAIL,12); // Now set truncation record, and new tail LSN according to whether // or not LOG_TAIL_INCLUSIVE was specified if( inclusive == TAIL_INCLUSIVE ) { // The specified LSN is to be retained in the logfile so // set the truncation record to the previous LSN and the // new tail to the specified LSN truncationRecord = new LogLSN(recordHeader.previousLSN); newTailRecord = new LogLSN(lsn); // IF the current LSN is the first record in an extent file // Remember that previous extent file is to be truncated if( lsn.offset == 0 ) truncLastExtent = true; } else { // The specified LSN is to be truncated from the logfile so // set the truncation record to the specified LSN and the // new tail to the next LSN truncationRecord = new LogLSN(lsn); newTailRecord = new LogLSN(recordHeader.nextLSN); } } // Now that the true truncation point in the log file is known, work out // how many extent files (if any) can be unlinked // - Set first_extent to extent number from log tail LSN // - Set last_extent to extent number from truncation point LSN int firstExtent = logControlDescriptor.tailLSN.extent; int lastExtent = truncationRecord.extent; // IF log head is being truncated && // inclusive parameter = LOG_TAIL_NOT_INCLUSIVE // Set log tail LSN to current log head LSN // Set log head LSN in Log_ControlDescriptor structure to LOG_NULL_LSN // ELSE // Set log tail LSN in Log_ControlDescriptor structure // to truncation point LSN if( truncateHead && inclusive == TAIL_NOT_INCLUSIVE ) { logControlDescriptor.tailLSN.copy(newTailRecord); logControlDescriptor.headLSN.copy(LogLSN.NULL_LSN); } else logControlDescriptor.tailLSN.copy(newTailRecord); // Write (and implicitly sync) the Log_ControlDescriptor structure // to the control file. Allow any error to pass to the caller. writeControlFile(); // Now unlink any extent files no longer required // This involves processing each of the extent files in the range // FirstExtent to LastExtent-1. // Note: If the TruncationRecord is a link record (last in the extent // file), then the LastExtent must also be processed. if( truncLastExtent ) lastExtent++; for( int extent = firstExtent; extent <= lastExtent-1; extent++ ) { // IF extent is currently open // Issue CLOSE for extent file // IF not successful allow the error to pass to the caller. LogExtent logEDP = (LogExtent)extentTable.get(extent); if( logEDP != null ) logEDP.fileHandle.fileClose(); // Issue UNLINK for extent file // IF not successful // Return LOG_CLOSE_FAILURE //Start IASRI 4720539 //if( !logEDP.file.delete() ) final LogExtent tmplogEDP = logEDP; Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return tmplogEDP.file.delete(); } } ); if(!isdeleted.booleanValue()) throw new LogException(null,LogException.LOG_CLOSE_FAILURE,15); //End IASRI 4720539 // Unchain the Log_ExtentDescriptor block, set its BlockValid // field to binary zeroes and deallocate it. extentTable.remove(extent); logEDP.doFinalize(); } // If the cushion file does not exist and at least one extents has // just been removed, now is a good time to try and restore the // cushion file. // Call RestoreCushion but specify that the upcall should not // be called if the restore fails, as it should already have // been called when the cushion was first freed. if( !cushionExists && firstExtent <= lastExtent - 1 ) restoreCushion(false); // Call the platform specific SUPOS_LOG_FREE_FILE_STORAGE macro to // release any unwanted areas of the extent file containing the TAIL LSN // Allow any error to pass to the caller. if( logControlDescriptor.tailLSN.offset > 0 ) freeFileStorage(logControlDescriptor.tailLSN); // If the log head has been set to 00000000.00000000, then ensure that // the next record which is written to the log causes the log control // data to be forced. Otherwise, should a crash occur AFTER writing the // record and BEFORE writing the control file, when the log is re-opened // during restart, it will be assumed that the log is empty and no // scanning for records 'beyond the end of the log' will take place. if( logControlDescriptor.headLSN.isNULL() ) recordsWritten = CONTROL_FORCE_INTERVAL; } /**Ensures that the log is written up to the given point. * * @param chkLSN The last LSN which must be written. * * @return * * @exception LogException The operation failed. * * @see */ synchronized void checkLSN( LogLSN chkLSN ) throws LogException { // Check BlockValid field in Log_FileDescriptor block pointed to // by logHandle parameter, and ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // IF ReadOnly log // Return LOG_READ_ONLY_ACCESS if( logControl.logReadOnly ) throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3); // IF the lsn parameter is equal to LOG_HEAD_LSN // Copy head LSN from Log_FileDescriptor into lsn // ELSE IF the lsn parameter is equal to LOG_TAIL_LSN // Copy tail LSN from Log_FileDescriptor into lsn // ELSE Copy lsn parameter into lsn LogLSN lsn; if( chkLSN.equals(LogLSN.HEAD_LSN) ) lsn = new LogLSN(logControlDescriptor.headLSN); else if( chkLSN.equals(LogLSN.TAIL_LSN) ) lsn = new LogLSN(logControlDescriptor.tailLSN); else lsn = new LogLSN(chkLSN); // IF lsn value is less than log tail LSN // Return LOG_SUCCESS if( lsn.lessThan(logControlDescriptor.tailLSN) ) { return; } // IF log file is empty (log head = LOG_NULL_LSN) // Unlock the Log_FileDescriptor latch // Return LOG_SUCCESS if( logControlDescriptor.headLSN.isNULL() ) { return; } // IF lsn value is greater than log head LSN // Copy head LSN from Log_FileDescriptor into lsn parameter if( lsn.greaterThan(logControlDescriptor.headLSN) ) lsn.copy(logControlDescriptor.headLSN); // Determine the extent which contains the record to be forced (this is // derived from the 'extent' part of the lsn parameter) - remember this // as LAST_EXTENT int lastExtent = lsn.extent; // Determine the extent which contains the log tail LSN, remember this // as FIRST_EXTENT int firstExtent = logControlDescriptor.tailLSN.extent; // Now force each of the extent files (FIRST_EXTENT to LAST_EXTENT // inclusive) for( int extent = firstExtent; extent <= lastExtent; extent++ ) { // IF extent is currently open (Log_ExtentDescriptor block exists) // IF the Written flag in the Log_ExtentDescriptor is TRUE // Issue FSYNC for extent file // IF not successful allow the error to pass to the caller. // ELSE // Set 'extent written' flag to FALSE LogExtent logEDP = (LogExtent)extentTable.get(extent); if( logEDP != null && logEDP.writtenSinceLastForce ) { logEDP.fileHandle.fileSync(); logEDP.writtenSinceLastForce = false; } } // IF 'extent' part of head LSN is same as LAST_EXTENT // Force the Log_ControlDescriptor structure to the control file // by issuing WRITE (implied sync) // IF not successful allow the error to pass to the caller. // ELSE // Don't force control data, since we do not want control data to // be 'ahead' of extent data // The following block of code will no longer be executed as a result // of a performance suggestion. This reduces the number of occasions // when the control data is forced to disk /* if( logControlDescriptor.headLSN.extent == lastExtent ) writeControlFile(); */ } /**Opens a cursor on the log file. * * @param startLSN The start of the browse. * @param endLSN The end of the browse. * * @return The new cursor. * * @exception LogException The browse was not possible. * * @see */ synchronized LogCursor openCursor( LogLSN startLSN, LogLSN endLSN ) throws LogException { // Check BlockValid field in Log_FileDescriptor block and // ensure it is valid // IF not valid Log_FileDescriptor // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,2); // Allocate a Log_CursorDescriptor block // IF allocate fails // Return LOG_INSUFFICIENT_MEMORY LogCursor cursor = new LogCursor(logControl,this,startLSN,endLSN); if( cursor == null ) { throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,4); } // Add the Log_CursorDescriptor block to the chain of similar blocks // hung off the LogFileDescriptor (anchor is CursorDescriptorHead) cursors.add(cursor); return cursor; } /**Closes the cursor. * * @param LogCursor The cursor to be closed. * * @return * * @exception LogException The cursor could not be closed. * * @see */ synchronized void closeCursor( LogCursor cursor ) throws LogException { // Check BlockValid field in Log_CursorDescriptor block and // ensure it is valid // IF not valid Log_CursorDescriptor // Return LOG_INVALID_CURSOR if( cursor == null || cursor.blockValid != cursor ) throw new LogException(null,LogException.LOG_INVALID_CURSOR,1); // Check BlockValid field in Log_FileDescriptor block pointed to // by field in Log_CursorDescriptor block and ensure it is valid if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_CURSOR,2); // IF not LogInitialised // Return LOG_NOT_INITIALISED if( !logControl.logInitialised ) throw new LogException(null,LogException.LOG_NOT_INITIALISED,3); // Now we know the blocks are valid. // Remove the Log_CursorDescriptor block from the chain hung off // the Log_FileDescriptor (CursorDescriptorHead) cursors.remove(cursor); } /**Positions the file pointer to the given position in the log. * This internal method does not need to be synchronized. * * @param lsn The base position sought. * @param extra An extra offset for the position. * @param accessType The type of access. * * @return The extent containing the given position. * * @exception LogException The operation failed. * * @see */ LogExtent positionFilePointer( LogLSN currentLSN, int extra, int accessType ) throws LogException { boolean extentJustOpened = false; // Remember open operation // Run the extent chain to see if extent file is already open LogExtent extent = (LogExtent)extentTable.get(currentLSN.extent); // Open the extent file if it was not found in the extent chain if( extent == null ) { // Open the new extent file extent = openExtent(currentLSN.extent); extentJustOpened = true; } // If the current cursor position for the extent is not in the // required position, seek to the correct position if( extent.cursorPosition != currentLSN.offset + extra || extent.lastAccess != accessType ) { // lseek to the correct offset in the open extent file // if the last cursor position is unknown or the distance // of the seek from the start of the file is closer to the // required position than the current position do a seek // from the start of the file, otherwise do a seek from the // current position. int seekDist = ((currentLSN.offset + extra) > extent.cursorPosition) ? (currentLSN.offset + extra - extent.cursorPosition) : (extent.cursorPosition - currentLSN.offset - extra); try { if( extent.lastAccess == LogExtent.ACCESSTYPE_UNKNOWN || currentLSN.offset + extra < seekDist ) extent.fileHandle.fileSeek(currentLSN.offset+extra,LogFileHandle.SEEK_ABSOLUTE); else extent.fileHandle.fileSeek(currentLSN.offset+extra-extent.cursorPosition,LogFileHandle.SEEK_RELATIVE); } catch( LogException le ) { if( extentJustOpened ) { extentTable.remove(currentLSN.extent); extent.doFinalize(); } throw new LogException(LogException.LOG_READ_FAILURE,3, null, le); } extent.cursorPosition = currentLSN.offset + extra; extent.lastAccess = accessType; } // Return the file descriptor for the extent file return extent; } /**Frees the cushion file. * <p> * This internal method does not need to be synchronized. * * @param * * @return * * @see */ private void freeCushion() { // If the cushion file exists, remove it. if( cushionExists ) { // Delete the cushion file. // Start IASRI 4720539 //logControl.cushionFile.delete(); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return logControl.cushionFile.delete(); } } ); // End IASRI 4720539 cushionExists = false; } } /**Restores the cushion file * <p> * This internal method does not need to be synchronized. * * @param callUpcall Indicates whether the upcall should be called. * * @return * * @exception LogException The operation failed. * * @see */ void restoreCushion( boolean callUpcall ) throws LogException { // IF the LOG_CUSHION_SIZE > 0 if( CUSHION_SIZE > 0 ) { // if the cushion file already exists, set the flag in the // File Descriptor block to say so. if( !logControl.cushionFile.exists() ) { LogFileHandle cushionFH; // Cushion file descriptor int openOptions = LogFileHandle.OPEN_RDWR | LogFileHandle.OPEN_CREAT | LogFileHandle.OPEN_SYNC; // Create an empty log file as a storage cushion // Issue OPEN request for $REGIONDIR/log/cushion // IF OPEN fails // Call function whose address is stored in UpcallFunction, // passing a reason value of LOG_CALLBACK_REASON_SOS // Unlock the Log_ProcessSharedLock // Return try { cushionFH = new LogFileHandle(logControl.cushionFile,openOptions); } catch( LogException le ) { if( callUpcall && !upcallInProgress ) { upcallInProgress = true; upcallTarget.upcall(CALLBACK_REASON_SOS); upcallInProgress = false; } throw new LogException(LogException.LOG_OPEN_FAILURE,3, null, le); } // Use Log_AllocFileStorage to create a file the // size LOG_CUSHION_SIZE // IF Log_AllocFileStorage fails // Call function whose address is stored in UpcallFunction, // passing a reason value of LOG_CALLBACK_REASON_SOS // CLOSE & Unlink the cushion file // Unlock the Log_ProcessSharedLock // Return try { cushionFH.allocFileStorage(CUSHION_SIZE); } catch( LogException le ) { cushionFH.destroy(); // Start IASRI 4720539 //logControl.cushionFile.delete(); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run(){ return logControl.cushionFile.delete(); } } ); // End IASRI 4720539 if( callUpcall && !upcallInProgress ) { upcallInProgress = true; upcallTarget.upcall(CALLBACK_REASON_SOS); upcallInProgress = false; } cushionExists = false; throw new LogException(LogException.LOG_OPEN_FAILURE,4, null, le); } // CLOSE the cushion file cushionFH.destroy(); } cushionExists = true; } } /**Writes the control file. * This internal method does not need to be synchronized. * * @param * * @return * * @exception LogException The write failed. * * @see */ void writeControlFile() throws LogException { // BUGFIX (Ram J) This fixes the log corruption problem. // The log extents have to be forced every time control // information is written. If not, there is a chance that // the control information (particularly headLSN) will go // inconsistent. The extent log that the headLSN in controlFile // points to, may not exist in the extent log, if it is // not forced. So, if JTS crashes, during recovery/reconstruction // there will be no log in the extents corresponding to the // headLSN stored in the control file. The fix is to force // all the dirty extents, everytime the control information // is updated. Enumeration extents = extentTable.elements(); while (extents.hasMoreElements()) { LogExtent nextEDP = (LogExtent) extents.nextElement(); if (nextEDP.writtenSinceLastForce) { try { nextEDP.fileHandle.fileSync(); nextEDP.writtenSinceLastForce = false; } catch (LogException le) { throw new LogException(LogException.LOG_ERROR_FORCING_LOG, 14, null, le); } } } // Move the file pointer to the beginning of the control file logFileHandle.fileSeek(0,LogFileHandle.SEEK_ABSOLUTE); // Write out the control data to the control file byte[] controlBytes = new byte[LogControlDescriptor.SIZEOF]; logControlDescriptor.toBytes(controlBytes,0); logFileHandle.fileWrite(controlBytes); } /**Opens the given extent. * <p> * This internal method does not need to be synchronized. * * @param extent The extent to open. * * @return The extent opened. * * @exception LogException The open failed. * * @see */ LogExtent openExtent( int extent ) throws LogException { // Build the extent file name from the CurrentLSN parameter File extentFile = logControl.extentFile(logFileName,LogExtent.modExtent(extent)); // Issue an OPEN request for the file // IF not successful (rc == -1 and not EOF) // Return LOG_OPEN_FAILURE int openOptions = LogFileHandle.OPEN_RDWR | LogFileHandle.OPEN_CREAT; if( logControl.logReadOnly ) openOptions = LogFileHandle.OPEN_RDONLY; LogFileHandle extentFH = new LogFileHandle(extentFile,openOptions); // Allocate a Log_ExtentDescriptor block and initialise it LogExtent logEDP = new LogExtent(extent,extentFH,extentFile); // Use the already hashed extent number to find the position in the // hash table and add it to the chain extentTable.put(extent,logEDP); logEDP.blockValid = logEDP; return logEDP; } /**Frees file storage for the file. * This internal method does not need to be synchronized. * * @param tailLSN The point from which storage is not required. * * @return * * @exception LogException The operation failed. * * @see */ void freeFileStorage( LogLSN tailLSN ) throws LogException { // Using the extent containing the tail LSN, calculate the number of // bytes up to but not including the log tail record // Zero this space by issuing an FCLEAR request for the extent file // IF not successful // Unlock the Log_FileDescriptor mutex // Return LOG_WRITE_FAILURE int bytesToClear = tailLSN.offset; if( bytesToClear == 0 ) { return; } // Build LSN which will cause Log_PositionFilePointer to // position the file pointer at the start of the extent file LogLSN startOfExtent = new LogLSN(tailLSN.extent,0); LogExtent logEDP = positionFilePointer(startOfExtent,0,LogExtent.ACCESSTYPE_UNKNOWN); // Write the change to permanent storage via an FSYNC request logEDP.fileHandle.fileSync(); } /**Checks restart record information. * This internal method does not need to be synchronized. * * @param fileHandle The handle of the file. * @param restartNumber The restart number. * @param restartInfo An array which will contain the length and timestamp. * * @return * * @exception LogException The operation failed. * * @see */ static void checkRestart( LogFileHandle fileHandle, int restartNumber, int[/*2*/] restartInfo ) throws LogException { // Initialise callers output parameters restartInfo[0] = 0; // length restartInfo[1] = 0; // time stamp // Calculate the offsets within control file for both restart records // Use LSEEK to move to the first record and issue a READ for its // Log_RestartDescriptor block byte[] restartBytes = new byte[LogRestartDescriptor.SIZEOF]; int offset = restartPosition(restartNumber); fileHandle.fileSeek(offset,LogFileHandle.SEEK_ABSOLUTE); int bytesRead = fileHandle.fileRead(restartBytes); LogRestartDescriptor logRD = new LogRestartDescriptor(restartBytes,0); // IF the READ is successful and it return the restart data if( bytesRead > 0 ) { // Check that the RestartValid value in the // Log_RestartDescriptor block matches the record offset // IF it matches // Use LSEEK to move to the end of the restart data and read // in the matching Log_RestartDescriptor block if( logRD.restartValid == restartPosition(restartNumber) ) { fileHandle.fileSeek(logRD.restartDataLength, LogFileHandle.SEEK_RELATIVE); fileHandle.fileRead(restartBytes); LogRestartDescriptor logRDEnd = new LogRestartDescriptor(restartBytes,0); // Check that the two Log_RestartDescriptor blocks are the same // IF they are identical // Assume the first version of restart data is valid // Remember the timestamp contained in the block if( logRD.equals(logRDEnd) ) { restartInfo[0] = logRD.restartDataLength; restartInfo[1] = logRD.timeStamp; } else throw new LogException(null,LogException.LOG_CORRUPTED,1); } } } /**Dumps the state of the object. * * @param * * @return * * @exception LogException The operation failed. * * @see */ void dump() throws LogException { /** LogExtent logEDP; // Extent file descriptor LogCursor logCuDP; // ptr to cursor descriptor // Check that the LogHandle passed points to a genuine File // Descriptor block using the BlockValid field. // IF the block is not genuine // Return LOG_INVALID_FILE_DESCRIPTOR if( blockValid != this ) throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1); // LOOP for each of the elements in the log file's extent hash table Enumeration extents = extentTable.elements(); while( extents.hasMoreElements() ) { logEDP = (LogExtent)extents.nextElement(); } java.util.Iterator curs = cursors.iterator(); while( curs.hasNext() ) { logCuDP = (LogCursor)curs.next(); } **/ } /**Removes all extent information from the log file. * This internal method does not need to be synchronized. * * @param * * @return * * @see */ void cleanUpExtents() { Enumeration extents = extentTable.elements(); while( extents.hasMoreElements() ) { LogExtent logEDP = (LogExtent)extents.nextElement(); extentTable.remove(logEDP.extentNumber); logEDP.doFinalize(); } extentTable = null; } /**Returns the alternate restart number. * * @param restart The current restart number. * * @return The new restart number. * * @see */ final static int alternateRestart( int restart) { return (restart == 1) ? 2 : 1; } /**Returns the offset of the specified restart number. * * @param restart The restart number. * * @return The restart position. * * @see */ final static int restartPosition( int restart ) { return (restart == 1) ? RESTART_OFFSET_1 : RESTART_OFFSET_2; } /**Returns the log file name. * * @param * * @return The log file name. * * @see */ final String logFileName() { return logFileName; } }