/********************************************************************** * * File.cpp * Copyright (C) 2003 UNESCO * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ //////////////////////////////////////////////////////////// // File.cpp File object methods //////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "CRC32.h" #include "File.h" static TCHAR closed_file_name[] = _T("closed-file"); static const int MaxExceptionMessages = 34; static const TCHAR *fileExceptionMsg[MaxExceptionMessages] = { _T("File exception: No exception reported"), // FILE_NO_ERROR _T("File exception: Checksum error"), // FILE_CHECKSUM_ERROR _T("File exception: Error closing file"), // FILE_CLOSE_ERROR _T("File exception: File is corrupted"), _T("File exception: Error creating file"), // FILE_CREATION_ERROR _T("File exception: File already exists"), // FILE_EXISTS _T("File exception: Trying to use a closed file"), // FILE_NOT_OPEN_ERROR _T("File exception: File not ready for reading/writing"), // FILE_NOT_READY _T("File exception: Could not write to file"), // FILE_NOT_WRITEABLE _T("File exception: Error opening file"), // FILE_OPEN_ERROR _T("File exception: Cannot obtain a file position"), // FILE_POSITION_ERROR _T("File exception: Error reading from file"), // FILE_READ_ERROR _T("File exception: Error seeking in file"), // FILE_SEEK_ERROR _T("File exception: Error writing to file"), // FILE_WRITE_ERROR _T("Fmgr exception: No database open"), // FMGR_NO_DATABASE_OPEN _T("Fmgr exception: No such file exists"), // FMGR_NO_FILE_EXISTS _T("Fmgr exception: No objects exist"), // FMGR_NO_OBJECTS_EXIST _T("Fmgr exception: Accessing a null pointer"), // FMGR_NULL_PTR _T("Fmgr exception: Object already exists"), // FMGR_OBJECT_EXISTS _T("Fmgr exception: Another object is referencing this file"), // FMGR_OBJECT_EXISTS _T("Fmgr exception: Math overflow error"), // FMGR_OVERFLOW _T("Fmgr exception: Parse error"), // FMGR_PARSE_ERROR _T("Fmgr exception: Invalid path"), // FMGR_PATH_ERROR _T("Fmgr exception: Trying to write to a read-only file"), // FMGR_READ_ONLY_FILE _T("Fmgr exception: Stack empty"), // FMGR_STACK_EMPTY _T("Fmgr exception: Stack full"), // FMGR_STACK_FULL _T("Fmgr exception: Synchronization Error"), // FMGR_SYNC_ERROR _T("Fmgr exception: Math under-flow error"), // FMGR_UNDERFLOW _T("Fmgr exception: Wrong file type"), // FMGR_WRONG_FILE_TYPE // Persistent lock error messages _T("Fmgr exception: Invalid lock type specified"), // FMGR_INVALID_LOCK_TYPE _T("Fmgr exception: Cannot access the file lock header"), // FMGR_FILELOCK_ACCESS_ERROR _T("Fmgr exception: Cannot lock the file"), // FMGR_FILELOCK_ERROR _T("Fmgr exception: Cannot access the record lock header"), // FMGR_RECORDLOCK_ACCESS_ERROR _T("Fmgr exception: Cannot lock the specified record") // FMGR_RECORDLOCK_ERROR }; //--------------------------------------------------------------------------- // CFileBase::CFileBase() // // Default constructor, makes a File object that isn't attached to a file //--------------------------------------------------------------------------- CFileBase::CFileBase() { _tcscpy(file_name, closed_file_name); // Set the initial file name fp = 0; // Reset the last reported error and the file status members file_error = FILE_NO_ERROR; is_open = 0; is_ok = 0; ready_for_reading = 0; ready_for_writing = 0; } //--------------------------------------------------------------------------- // CFileBase::~CFileBase() // // Destructor for CFileBase file object //--------------------------------------------------------------------------- CFileBase::~CFileBase() { Close(); } //--------------------------------------------------------------------------- // const char* CFileBase::FileExceptionMessage() // // Returns a null terminated string that can / be used to log or print a // file exception. //--------------------------------------------------------------------------- const TCHAR *CFileBase::FileExceptionMessage() { assert(file_error pos) { // Seek forward to the specified address FileSystem::StreamPos offset = file_address - pos; Seek(offset, FileSystem::FILE_SEEK_CUR); } else if (file_address < pos) { // Seek backward to the specified address Seek(file_address, FileSystem::FILE_SEEK_BEG); } else { // Current file position equals the specified address // Find current the position Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR); } return FilePosition(); // Return current file position after seeking } //------------------------------------------------------------------------------- // FileError Store(const void* buf, isis::uint32_t nBytes, // FAU file_addres = CFileBase::CURRADDR, // int flush = 1, int bit_test = 1); // // Write a specific number of bytes from a memory buffer to a specified file // offset. If the "flush" variable is true, the file buffers will be flushed to // disk with each write operation. If the bit_test variable if true, the CRC of // the buffer will be compared to the CRC of the actual bytes written to disk. // Returns a non-zero value to indicate an error condition or zero if successful. // // void* buf - Pointer to the user-supplied buffer that is to receive // the data read from the file. // isis::uint32_t nBytes - The maximum number of bytes to be read from the // file. Note that an CFileBase is always a binary file, // so a CR/LF pair will always be read or written as two bytes. // FAU file_address - The file byte address. The address is always interpreted to // be from the beginning of the file, unless dwAddr is equal // to CURRADDR, which means from the current position. //------------------------------------------------------------------------------- FileError CFileBase::Store(const void* buf, isis::uint32_t nBytes, FileSystem::FAU_t file_address /*= CURRADDR */, int flush_flag /* = 1 */, int bit_test /* = 1 */) { FileSystem::FAU_t buf_address; if (ReadyForWriting()) { if (file_address == CURRADDR) { if (last_operation == IO_READ) { if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR) return file_error; } } else { if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR) return file_error; } buf_address = FilePosition(); if (file_error != FILE_NO_ERROR) return file_error; if (FileSystem::Write(fp, buf, nBytes) != nBytes) { file_error = FILE_WRITE_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } last_operation = IO_WRITE; } else { file_error = FILE_NOT_WRITEABLE; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } // Allow application to flush disk buffers after each write // operation to ensure the file data stays in sync during multiple // file access. if (flush_flag) { if (FileSystem::Flush(fp) != FILE_NO_ERROR) { file_error = FILE_WRITE_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR) return file_error; } if (bit_test) { isis::uint32_t w_csum = CalcCRC32((char* ) buf, nBytes); isis::uint32_t r_csum = CalcChecksum(nBytes, buf_address); // Check for file errors if (file_error != FILE_NO_ERROR) return file_error; if (w_csum ^ r_csum) { file_error = FILE_CHECKSUM_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } } return file_error = FILE_NO_ERROR; } //------------------------------------------------------------------------------- // FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes, FAU file_addres // /* = CFileBase::CURRADDR */) // // Read a specified number of bytes from the specified file offset into a memory // buffer. Returns a non-zero value to indicate an error condition or zero if // successful. // void* buf - Pointer to the user-supplied buffer that is to receive // the data read from the file. // isis::uint32_t nBytes - The maximum number of bytes to be read from the file. // Note that an CFileBase is always a binary file, so a // CR/LF pair will always be read or written as two bytes. // FAU file_addres - The file byte address. The address is always interpreted // to be from the beginning of the file, unless dwAddr is // equal to CURRADDR, which means from the current position. //------------------------------------------------------------------------------- FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes, FileSystem::FAU_t file_address /* = CFileBase::CURRADDR */) { if (IsOK()) { if (file_address == CURRADDR) { if (last_operation == IO_WRITE) { if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR) return file_error; } } else { if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR) return file_error; } //TRACE("\nFetchfile_address=%ld nBytes=%u",file_address, nBytes); if (FileSystem::Read(fp, buf, nBytes) != nBytes) { file_error = FILE_READ_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } last_operation = IO_READ; } else { file_error = FILE_NOT_READY; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } return file_error = FILE_NO_ERROR; } //------------------------------------------------------------------------------- // FileError Flush() // // Flush any open disk buffers. Returns a non-zero value to indicate an error // condition or zero if successful. //------------------------------------------------------------------------------- FileError CFileBase::Flush() { PRECONDITION(ReadyForWriting()); if (FileSystem::Flush(fp) != FILE_NO_ERROR) { file_error = FILE_WRITE_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return file_error; #endif } Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR); return file_error = FILE_NO_ERROR; } //------------------------------------------------------------------------------- // StreamPos CFileBase::FilePosition() // // Returns the current file position or -1 to indicate an error condition. //------------------------------------------------------------------------------- FileSystem::StreamPos CFileBase::FilePosition() { if (!IsOK()) { file_error = FILE_NOT_READY; #ifdef __CPP_EXCEPTIONS__ throw CFileBase(); #else return (StreamPos) - 1; #endif } FileSystem::StreamPos pos = FileSystem::Tell(fp); if (pos < 0) { file_error = FILE_POSITION_ERROR; #ifdef __CPP_EXCEPTIONS__ throw CFileBaseException(file_error); #else return (StreamPos) - 1; #endif } return pos; } //------------------------------------------------------------------------------- // isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FAU file_address, // int mem_alloc) // // Calculate a 32-bit CRC checksum for a given number of bytes starting at the // specified address. Returns a 32-bit CRC value. If the mem_alloc variable is // true, a buffer equal to the specified number of bytes will be created in // memory. If the mem_alloc variable is false or memory allocation fails, the CRC // will be calculated byte by byte starting at the specified address. // NOTE: the calling function must check for disk file errors. // Returns the CRC value. //------------------------------------------------------------------------------- isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FileSystem::FAU_t file_address, int mem_alloc) { isis::uint32_t CRC; isis::uint32_t len = bytes; unsigned char data; char *buf = 0; // Create a buffer equal to the object length if (mem_alloc) buf = new char[bytes]; if (buf) { if (IsOK()) { if (Fetch(buf, bytes, file_address) != FILE_NO_ERROR) return 0; CRC = CalcCRC32(buf, bytes); delete[] buf; } } else { if (IsOK()) { // Seek to the specified file address SeekTo(file_address); if (file_error != FILE_NO_ERROR) return 0; CRC = 0xffffffffL; while (len--) { if (Fetch(&data, sizeof(data)) != FILE_NO_ERROR) return 0; CRC = CalcCRC32(data, CRC); } CRC ^= 0xffffffffL; } } return CRC; } /////////////////////////////////////////////////////////////////////////// // General purpose file utilites // int CFileBase::Exists(const TCHAR *fname) // Returns true if the file exists { return FileSystem::Exists(fname); } FileSystem::FAU_t CFileBase::FileSize(const TCHAR *fname) // Returns the file size. Use after file has been closed // and re-opened to ensure that all the buffers are flushed // to disk. Returns -1 to indicate an error condition. { return (FileSystem::FAU_t) FileSystem::FileSize(fname); } int CFileBase::CanOpenForWriting(const TCHAR *fname) // Returns true if the file is opened { return 1; //return FileSystem::CanOpenForWriting(fname); } int CFileBase::CanOpenReadOnly(const TCHAR *fname) // Returns true if the file is opened { return 1; //return FileSystem::CanOpenReadOnly(fname); }