1131 lines
25 KiB
C++
1131 lines
25 KiB
C++
// UnbuffIO.cpp -- Implements the class CUnbufferedIO
|
|
|
|
#include "stdafx.h"
|
|
#include "UnbuffIO.h"
|
|
#include "MemEx.h"
|
|
#include <stdlib.h>
|
|
#include "ftsrch.h" // for uOpSys
|
|
#include "globals.h"
|
|
#include "Except.h"
|
|
|
|
static HWND hwndWhereIsIt = NULL;
|
|
|
|
extern "C" void APIENTRY SetDirectoryLocator(HWND hwndLocator)
|
|
{
|
|
hwndWhereIsIt= hwndLocator;
|
|
}
|
|
|
|
HFONT GetDefaultFont()
|
|
{
|
|
if (!hwndWhereIsIt) return NULL;
|
|
|
|
HFONT hFont = (HFONT) ::SendMessage(hwndWhereIsIt, MSG_GET_DEFFONT, 0, 0);
|
|
HFONT hFontClone = NULL;
|
|
|
|
#ifdef _DEBUG
|
|
UINT uErrCode= 0;
|
|
#endif // _DEBUG
|
|
|
|
if (hFont)
|
|
{
|
|
LOGFONT lf;
|
|
|
|
UINT cbObj= ::GetObject(hFont, sizeof(lf), &lf);
|
|
|
|
if (cbObj)
|
|
hFontClone= ::CreateFontIndirect(&lf);
|
|
#ifdef _DEBUG
|
|
else uErrCode= ::GetLastError();
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
return hFontClone;
|
|
}
|
|
|
|
BOOL FindFile(FILENAMEBUFFER pszFile, BOOL *pfStartEnumeration)
|
|
{
|
|
ASSERT(pszFile && pszFile[0]);
|
|
|
|
if (!hwndWhereIsIt) return FALSE;
|
|
|
|
FILENAMEBUFFER szPath;
|
|
|
|
lstrcpy(szPath, pszFile);
|
|
|
|
::SendMessage(hwndWhereIsIt, MSG_FTS_WHERE_IS_IT,
|
|
(WPARAM) *pfStartEnumeration,
|
|
(LPARAM) pszFile
|
|
);
|
|
|
|
*pfStartEnumeration= FALSE;
|
|
|
|
/*
|
|
* 23-Jan-1995 [ralphw] I switched this to lstrcmpi from stricmp().
|
|
* stricmp won't recognize locales, so this function would fail with
|
|
* filenames on international release.
|
|
*/
|
|
|
|
if (lstrcmpi(pszFile, szPath))
|
|
return (*pszFile)? TRUE : FALSE;
|
|
|
|
::SendMessage(hwndWhereIsIt, MSG_FTS_WHERE_IS_IT,
|
|
(WPARAM) FALSE,
|
|
(LPARAM) pszFile
|
|
);
|
|
|
|
if (!*pszFile) return FALSE;
|
|
|
|
return BOOL(lstrcmpi(pszFile, szPath));
|
|
}
|
|
|
|
CUnbufferedIO::CUnbufferedIO()
|
|
{
|
|
m_fInitialed = FALSE;
|
|
m_fFileAttached = FALSE;
|
|
m_hFile = NULL;
|
|
m_cbSector = 0;
|
|
m_cbCluster = 0;
|
|
m_cActiveIOTransactions = 0;
|
|
m_cWaitingForLull = 0;
|
|
m_fWaitingForLullEnd = FALSE;
|
|
m_pfnCompletion = NULL;
|
|
m_hEventLull = NULL;
|
|
m_hEventLullEnd = NULL;
|
|
m_hMapFile = NULL;
|
|
m_pvMemoryImage = NULL;
|
|
m_fAlready_Out_of_Space = FALSE;
|
|
}
|
|
|
|
void CUnbufferedIO::Initial()
|
|
{
|
|
InitializeCriticalSection(&m_cs);
|
|
|
|
m_hEventLull = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
m_hEventLullEnd = CreateEvent(NULL, TRUE , TRUE , NULL);
|
|
|
|
if (!m_hEventLull || !m_hEventLullEnd)
|
|
{
|
|
#ifdef _DEBUG
|
|
MessageBox(NULL, "In CUnbufferedIO::Initial; Failure: !m_hEventLull || !m_hEventLullEnd", "Search System Failure", MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
#endif // _DEBUG
|
|
RaiseException(STATUS_SYSTEM_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
}
|
|
|
|
void CUnbufferedIO::BeginLull()
|
|
{
|
|
EnterCriticalSection(&m_cs);
|
|
|
|
m_cWaitingForLull++; ResetEvent(m_hEventLullEnd);
|
|
|
|
while (m_cActiveIOTransactions)
|
|
{
|
|
LeaveCriticalSection(&m_cs);
|
|
|
|
UINT uiResult;
|
|
|
|
do uiResult= WaitForSingleObjectEx(m_hEventLull, INFINITE, TRUE);
|
|
while (uiResult != WAIT_OBJECT_0);
|
|
|
|
EnterCriticalSection(&m_cs);
|
|
}
|
|
}
|
|
|
|
void CUnbufferedIO::EndLull()
|
|
{
|
|
ASSERT(m_cWaitingForLull);
|
|
|
|
if (--m_cWaitingForLull) SetEvent(m_hEventLull);
|
|
else
|
|
if (m_fWaitingForLullEnd) SetEvent(m_hEventLullEnd);
|
|
|
|
LeaveCriticalSection(&m_cs);
|
|
}
|
|
|
|
void CUnbufferedIO::BeginTransaction()
|
|
{
|
|
EnterCriticalSection(&m_cs);
|
|
|
|
if (m_cWaitingForLull)
|
|
{
|
|
m_fWaitingForLullEnd= TRUE;
|
|
|
|
while(m_cWaitingForLull)
|
|
{
|
|
LeaveCriticalSection(&m_cs);
|
|
|
|
UINT uiResult;
|
|
|
|
do uiResult= WaitForSingleObjectEx(m_hEventLullEnd, INFINITE, TRUE);
|
|
while (uiResult != WAIT_OBJECT_0);
|
|
|
|
EnterCriticalSection(&m_cs);
|
|
}
|
|
}
|
|
|
|
++m_cActiveIOTransactions;
|
|
}
|
|
|
|
void CUnbufferedIO::ReleaseTransaction()
|
|
{
|
|
// ASSERT(m_cActiveIOTransactions);
|
|
|
|
LeaveCriticalSection(&m_cs);
|
|
}
|
|
|
|
void CUnbufferedIO::AbortTransaction()
|
|
{
|
|
ASSERT(m_cActiveIOTransactions);
|
|
|
|
--m_cActiveIOTransactions;
|
|
|
|
ReleaseTransaction();
|
|
}
|
|
|
|
void CUnbufferedIO::FinishTransaction()
|
|
{
|
|
EnterCriticalSection(&m_cs);
|
|
|
|
ASSERT(m_cActiveIOTransactions);
|
|
|
|
if (!--m_cActiveIOTransactions && m_cWaitingForLull) SetEvent(m_hEventLull);
|
|
|
|
LeaveCriticalSection(&m_cs);
|
|
}
|
|
|
|
CUnbufferedIO::~CUnbufferedIO()
|
|
{
|
|
if (m_fFileAttached)
|
|
{
|
|
if (m_pvMemoryImage)
|
|
{
|
|
UnmapViewOfFile(m_pvMemoryImage); m_pvMemoryImage= NULL;
|
|
|
|
CloseHandle(m_hMapFile); m_hMapFile= NULL;
|
|
}
|
|
|
|
BeginLull();
|
|
|
|
CloseHandle(m_hFile); m_fFileAttached= FALSE;
|
|
|
|
EndLull();
|
|
|
|
if (m_fTemporary)
|
|
{
|
|
BOOL fDeleted= DeleteFile( m_szFile);
|
|
|
|
ASSERT(fDeleted);
|
|
}
|
|
}
|
|
|
|
DeleteCriticalSection(&m_cs);
|
|
|
|
if (m_hEventLull ) { CloseHandle(m_hEventLull ); m_hEventLull = NULL; }
|
|
if (m_hEventLullEnd) { CloseHandle(m_hEventLullEnd); m_hEventLullEnd = NULL; }
|
|
}
|
|
|
|
BOOL CUnbufferedIO::EmptyFile()
|
|
{
|
|
ASSERT(m_fFileAttached);
|
|
|
|
BeginLull();
|
|
|
|
LONG zero= 0;
|
|
|
|
UINT ibLow= SetFilePointer(m_hFile, 0, &zero, FILE_BEGIN);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
UINT uiLastError= GetLastError();
|
|
|
|
#endif // _DEBUG
|
|
|
|
ASSERT(ibLow != -1 || !GetLastError());
|
|
|
|
BOOL fResult= SetEndOfFile(m_hFile);
|
|
|
|
EndLull();
|
|
|
|
return fResult;
|
|
}
|
|
|
|
CUnbufferedIO *CUnbufferedIO::NewTempFile(PSZ pszSource, BOOL fPersistent)
|
|
{
|
|
FILENAMEBUFFER szPath;
|
|
FILENAMEBUFFER szFile;
|
|
FILENAMEBUFFER szDirectory;
|
|
FILENAMEBUFFER szCandidate;
|
|
|
|
BYTE szFName[_MAX_FNAME];
|
|
BYTE szEXT [_MAX_EXT ];
|
|
BYTE szDrive[_MAX_DRIVE];
|
|
|
|
HANDLE hFile = NULL;
|
|
CUnbufferedIO *puio = NULL;
|
|
UINT UErrCode = SetErrorMode(0);
|
|
|
|
SetErrorMode(UErrCode || SEM_NOOPENFILEERRORBOX || SEM_FAILCRITICALERRORS );
|
|
|
|
if (!pszSource || !*pszSource) fPersistent= FALSE;
|
|
|
|
__try
|
|
{
|
|
for (UINT iPhase= SourceDirectory; iPhase < PhaseLimit; ++iPhase)
|
|
{
|
|
switch (iPhase)
|
|
{
|
|
// BugBug! Need to make this directory sequence settable by for each Indexer
|
|
|
|
case SourceDirectory: // See if we were passed a source file name.
|
|
|
|
if (!pszSource) continue; // If not go to the next phase.
|
|
|
|
_splitpath(pszSource, PSZ(szDrive), PSZ(szDirectory), PSZ(szFName), PSZ(szEXT));
|
|
|
|
if (GetDriveType(PSZ(szDrive)) == DRIVE_CDROM) continue; // Can't write to a CD-Rom!
|
|
|
|
if (!szDirectory[0]) GetCurrentDirectory(MAX_PATH, PSZ(szPath));
|
|
else
|
|
{
|
|
lstrcpy(PSZ(szPath), PSZ(szDrive));
|
|
lstrcat(PSZ(szPath), PSZ(szDirectory));
|
|
}
|
|
|
|
break;
|
|
|
|
case HelpDirectory:
|
|
|
|
GetWindowsDirectory((PSZ) szPath,MAX_PATH);
|
|
|
|
lstrcat(PSZ(szPath), "\\Help\\");
|
|
|
|
break;
|
|
|
|
|
|
case WindowsDirectory:
|
|
|
|
GetWindowsDirectory((PSZ) szPath, MAX_PATH);
|
|
lstrcat(PSZ(szPath), "\\");
|
|
|
|
break;
|
|
}
|
|
|
|
if (fPersistent)
|
|
{
|
|
// When we're creating a tmp file which will be eventually made into
|
|
// a permanent file, we must look to see if a read-only file already
|
|
// exists with the permanent name we plan to use.
|
|
//
|
|
// Note that the final rename operation can still fail if some program
|
|
// creates such a read-only file after this test.
|
|
|
|
lstrcpy(szCandidate, PSZ(szPath ));
|
|
lstrcat(szCandidate, PSZ(szFName));
|
|
lstrcat(szCandidate, PSZ(szEXT ));
|
|
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE hfd;
|
|
|
|
if ((hfd = FindFirstFile(szCandidate, &fd)) != INVALID_HANDLE_VALUE)
|
|
{
|
|
FindClose(hfd);
|
|
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) continue;
|
|
}
|
|
}
|
|
|
|
if (!GetTempFileName((char *) szPath, "~ft", 0, (char *) szFile)) continue;
|
|
|
|
hFile = CreateFile((char *) szFile, GENERIC_READ | GENERIC_WRITE, 0,
|
|
(LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING
|
|
| ((uOpSys != WIN40) ? FILE_FLAG_OVERLAPPED : 0)
|
|
, (HANDLE) NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) continue;
|
|
|
|
puio= New CUnbufferedIO();
|
|
|
|
puio->Initial();
|
|
|
|
HANDLE hfTemp= hFile; hFile= NULL;
|
|
|
|
if (!puio->SetupFile((char *) szPath, (char *) szFile, hfTemp, TRUE))
|
|
{
|
|
delete puio; puio= NULL;
|
|
|
|
continue;
|
|
}
|
|
|
|
__leave;
|
|
}
|
|
|
|
// Couldn't create the temp file anywhere...
|
|
|
|
RaiseException(STATUS_DISK_CREATE_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination())
|
|
{
|
|
if (puio) { delete puio; puio = NULL; }
|
|
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile); hFile = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetErrorMode(UErrCode);
|
|
return puio;
|
|
}
|
|
|
|
BOOL CUnbufferedIO::CbFile(PUINT pibFileLow, PUINT pibFileHigh)
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION bhfi;
|
|
|
|
if (!GetFileInformationByHandle(m_hFile, &bhfi)) return FALSE;
|
|
|
|
if (pibFileHigh) *pibFileHigh= bhfi.nFileSizeHigh;
|
|
|
|
ASSERT(pibFileLow);
|
|
|
|
*pibFileLow= bhfi.nFileSizeLow;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUnbufferedIO::SetupFile(PSZ pszPath, PSZ pszFile, HANDLE hFile, BOOL fTemporary)
|
|
{
|
|
m_hFile = hFile;
|
|
m_fFileAttached = TRUE;
|
|
m_fTemporary = fTemporary;
|
|
|
|
lstrcpy((char *) m_szPath, pszPath);
|
|
lstrcpy((char *) m_szFile, pszFile);
|
|
|
|
if (GetStatistics()) m_fInitialed = TRUE;
|
|
|
|
return m_fInitialed;
|
|
|
|
#if 0
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
MessageBox(NULL, "In CUnbufferedIO::SetupFile; Failure: !GetStatistics()", "Search System Failure", MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
#endif // _DEBUG
|
|
RaiseException(STATUS_SYSTEM_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
#endif // 0
|
|
}
|
|
|
|
CUnbufferedIO *CUnbufferedIO::OpenExistingFile(PSZ pszFile, BOOL fAllowWrites)
|
|
{
|
|
HANDLE hFile = NULL;
|
|
CUnbufferedIO *puio = NULL;
|
|
|
|
__try
|
|
{
|
|
FILENAMEBUFFER szPath;
|
|
FILENAMEBUFFER szFile;
|
|
|
|
PSZ pszFileName;
|
|
|
|
if (!GetFullPathName(pszFile, MAX_PATH, szFile, &pszFileName))
|
|
RaiseException(STATUS_DISK_OPEN_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
hFile= CreateFile(szFile,
|
|
fAllowWrites? GENERIC_READ | GENERIC_WRITE : GENERIC_READ,
|
|
fAllowWrites? 0 : FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING
|
|
| ((uOpSys != WIN40) ? FILE_FLAG_OVERLAPPED : 0)
|
|
,(HANDLE) NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
RaiseException(STATUS_DISK_OPEN_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
if (!GetFullPathName(szFile, MAX_PATH, szPath, &pszFileName))
|
|
RaiseException(STATUS_DISK_OPEN_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
lstrcmp(szFile, szPath);
|
|
|
|
*pszFileName = 0;
|
|
|
|
puio= New CUnbufferedIO();
|
|
|
|
puio->Initial();
|
|
|
|
HANDLE hfTemp= hFile; hFile= NULL;
|
|
|
|
puio->SetupFile(szPath, szFile, hfTemp, FALSE);
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination())
|
|
{
|
|
if (puio) { delete puio; puio = NULL; }
|
|
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile); hFile = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return puio;
|
|
}
|
|
|
|
CUnbufferedIO *CUnbufferedIO::CreateNewFile(PSZ pszFile, BOOL fOverwriteExistingFile)
|
|
{
|
|
HANDLE hFile = NULL;
|
|
CUnbufferedIO *puio = NULL;
|
|
|
|
__try
|
|
{
|
|
hFile = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, 0,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
fOverwriteExistingFile? CREATE_ALWAYS : CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING
|
|
| ((uOpSys != WIN40) ? FILE_FLAG_OVERLAPPED : 0)
|
|
,(HANDLE) NULL
|
|
);
|
|
|
|
FILENAMEBUFFER szPath;
|
|
PSZ pszFileName;
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE || !GetFullPathName(pszFile, MAX_PATH, (char *) szPath, &pszFileName))
|
|
RaiseException(STATUS_DISK_CREATE_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
CUnbufferedIO *puio= New CUnbufferedIO();
|
|
|
|
puio->Initial();
|
|
|
|
HANDLE hfTemp= hFile; hFile= NULL;
|
|
|
|
puio->SetupFile((char *) szPath, (char *) pszFile, hfTemp, TRUE);
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination())
|
|
{
|
|
if (puio) { delete puio; puio = NULL; }
|
|
|
|
if (hFile && hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile); hFile = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return puio;
|
|
}
|
|
|
|
|
|
|
|
BOOL CUnbufferedIO::GetStatistics()
|
|
{
|
|
FILENAMEBUFFER szDrive;
|
|
|
|
_splitpath((char *) m_szFile, (char *) &szDrive, NULL, NULL, NULL);
|
|
|
|
UINT cbDrive = 0;
|
|
BOOL fUNCPath = FALSE;
|
|
|
|
if (*szDrive)
|
|
cbDrive = lstrlen(szDrive);
|
|
else
|
|
{
|
|
// Maybe this is a UNC path. Scan for the pattern \\MachineName\Share\...
|
|
|
|
char c, *pszSrc= m_szFile;
|
|
|
|
c= *pszSrc;
|
|
|
|
if (c != '\\' && c != '/')
|
|
return FALSE;
|
|
|
|
c= *(pszSrc= CharNext(pszSrc));
|
|
|
|
if (c != '\\' && c != '/')
|
|
return FALSE;
|
|
|
|
for ( ; ; )
|
|
{
|
|
c= *(pszSrc= CharNext(pszSrc));
|
|
|
|
if (!c || c == '\\' || c == '/')
|
|
break;
|
|
}
|
|
|
|
if (!c)
|
|
return FALSE;
|
|
|
|
for ( ; ; )
|
|
{
|
|
c= *(pszSrc= CharNext(pszSrc));
|
|
|
|
if (!c || c == '\\' || c == '/')
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Note that we specifically do NOT check for !c here -- if there
|
|
* is no trailing backslash (theoretically impossible) we will add one
|
|
* anyway, get the drive, and fail elsewhere because there is no
|
|
* filename.
|
|
*/
|
|
|
|
cbDrive= pszSrc - m_szFile;
|
|
|
|
CopyMemory(szDrive, m_szFile, cbDrive);
|
|
|
|
fUNCPath= TRUE;
|
|
}
|
|
|
|
szDrive[cbDrive ]= '\\';
|
|
szDrive[cbDrive + 1] = 0;
|
|
|
|
ULONG cbSector, cSectorsPerCluster, cFreeClusters, cTotalClusters;
|
|
|
|
BOOL fGotInfo=GetDiskFreeSpace((char *) szDrive, &cSectorsPerCluster, &cbSector,
|
|
&cFreeClusters, &cTotalClusters
|
|
);
|
|
|
|
|
|
if (!fGotInfo && fUNCPath) {
|
|
if (!hMPRLib) {
|
|
hMPRLib = LoadLibrary("MPR.dll");
|
|
|
|
if (hMPRLib)
|
|
{
|
|
pWNetAddConnection2 = (PWNETADDCONNECTION2A ) GetProcAddress(hMPRLib, "WNetAddConnection2A" );
|
|
pWNetCancelConnection2 = (PWNETCANCELCONNECTION2A) GetProcAddress(hMPRLib, "WNetCancelConnection2A");
|
|
|
|
if (!pWNetAddConnection2 || !pWNetCancelConnection2)
|
|
{
|
|
// If we failed to get both proc addresses, we set the pointer variables back to NULL.
|
|
|
|
pWNetAddConnection2 = NULL;
|
|
pWNetCancelConnection2 = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pWNetAddConnection2 && pWNetCancelConnection2) {
|
|
|
|
// GetDiskFreeSpace failed on a UNC path. We'll try to bind the
|
|
// UNC share to a drive letter and then ask again.
|
|
|
|
DWORD afDrives= GetLogicalDrives();
|
|
|
|
UINT iDrive;
|
|
|
|
for (iDrive= 26; iDrive--; )
|
|
if (!(afDrives & (1 << iDrive))) break; // Found a drive letter not in use.
|
|
else
|
|
if (!iDrive) return FALSE; // No drives letters available!
|
|
|
|
NETRESOURCE nr;
|
|
BYTE abDriveLetter[4];
|
|
|
|
abDriveLetter[0]= 'A' + iDrive;
|
|
abDriveLetter[1]= ':';
|
|
abDriveLetter[2]= 0;
|
|
|
|
nr.dwType = RESOURCETYPE_DISK;
|
|
nr.lpLocalName = (char *) abDriveLetter;
|
|
nr.lpRemoteName = szDrive;
|
|
nr.lpProvider = NULL;
|
|
|
|
DWORD dwResult= pWNetAddConnection2(&nr, NULL, NULL, 0);
|
|
|
|
if (dwResult != NO_ERROR) return FALSE;
|
|
|
|
abDriveLetter[2] = '\\';
|
|
abDriveLetter[3] = 0;
|
|
|
|
fGotInfo= GetDiskFreeSpace((char *) abDriveLetter, &cSectorsPerCluster, &cbSector,
|
|
&cFreeClusters, &cTotalClusters
|
|
);
|
|
|
|
abDriveLetter[2] = 0;
|
|
|
|
#ifdef _DEBUG
|
|
dwResult=
|
|
#endif // _DEBUG
|
|
pWNetCancelConnection2((LPSTR) abDriveLetter, 0, TRUE);
|
|
|
|
ASSERT(dwResult == NO_ERROR);
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
UINT uiLastError=GetLastError();
|
|
|
|
#endif // _DEBUG
|
|
|
|
if (!fGotInfo) return FALSE;
|
|
|
|
m_cbSector = cbSector;
|
|
m_cbCluster = cbSector * cSectorsPerCluster;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
PVOID CUnbufferedIO::_GetBuffer(PUINT pcbBuffer, PSZ pszWhichFile, UINT iWhichLine)
|
|
#else // _DEBUG
|
|
PVOID CUnbufferedIO::GetBuffer(PUINT pcbBuffer)
|
|
#endif // _DEBUG
|
|
{
|
|
// This routine creates a buffer aligned correctly for read and write operations.
|
|
// On entry *pcbBuffer defines the minimum size for the buffer. If the buffer is
|
|
// successfully allocated, *pcbBuffer will be set to the largest usable size
|
|
// for the buffer, and the explicit result will be the base address of the buffer.
|
|
// If the allocation fails, we return NULL.
|
|
|
|
PVOID pv = NULL;
|
|
PVOID pv2 = NULL;
|
|
|
|
__try
|
|
{
|
|
if (!m_fFileAttached)
|
|
{
|
|
#ifdef _DEBUG
|
|
MessageBox(NULL, "In CUnbufferedIO::GetBuffer; Failure: !m_fFileAttached", "Search System Failure", MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
#endif // _DEBUG
|
|
RaiseException(STATUS_SYSTEM_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
|
|
UINT cbBuffer= *pcbBuffer;
|
|
|
|
cbBuffer= m_cbSector * ((cbBuffer + m_cbSector - 1) / m_cbSector);
|
|
|
|
SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si);
|
|
|
|
UINT cbPage = si.dwPageSize;
|
|
UINT cbAlloc = si.dwAllocationGranularity;
|
|
|
|
ASSERT(!(cbAlloc % m_cbSector)); // BugBug! Can this ever be false??
|
|
|
|
UINT cbReserve = cbAlloc * ((cbBuffer + cbAlloc - 1) / cbAlloc);
|
|
UINT cbCommit = cbPage * ((cbBuffer + cbPage - 1) / cbPage );
|
|
|
|
pv= VirtualAlloc(0, cbReserve, MEM_RESERVE, PAGE_NOACCESS);
|
|
|
|
ASSERT(!(UINT(pv) % m_cbSector));
|
|
|
|
if (!pv || !(pv2= VirtualAlloc(pv, cbCommit, MEM_COMMIT, PAGE_READWRITE)))
|
|
RaiseException(STATUS_NO_MEMORY, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CreateVARecord(pv, PBYTE(pv) + cbCommit, PBYTE(pv) + cbReserve, pszWhichFile, iWhichLine);
|
|
|
|
#endif // _DEBUG
|
|
|
|
*pcbBuffer= cbBuffer;
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination() && pv)
|
|
{
|
|
VirtualFree(pv, 0, MEM_RELEASE); pv= pv2= NULL;
|
|
}
|
|
}
|
|
|
|
return pv2;
|
|
}
|
|
|
|
void CUnbufferedIO::FreeBuffer(PVOID pvBuffer)
|
|
{
|
|
// This routine deallocates a buffer constructed by the GetBuffer routine.
|
|
|
|
ASSERT(!(UINT(pvBuffer) % m_cbSector));
|
|
|
|
#ifdef _DEBUG
|
|
|
|
DestroyVARecord(pvBuffer);
|
|
|
|
#endif // _DEBUG
|
|
|
|
VirtualFree(pvBuffer, 0, MEM_RELEASE);
|
|
}
|
|
|
|
typedef struct _TransactionControl
|
|
{
|
|
CUnbufferedIO *puio;
|
|
PFNCompletion pfnCompletionRoutine;
|
|
PVOID pvEnvironment;
|
|
PVOID pvTransaction;
|
|
HANDLE hEvent;
|
|
PUINT puiCompletionCode;
|
|
OVERLAPPED ov;
|
|
|
|
} TransactionControl;
|
|
|
|
static VOID WINAPI UnbufferedIOCompletionRoutine(DWORD fdwError, DWORD cbTransferred, LPOVERLAPPED lpo)
|
|
{
|
|
// We assume that lpo points to an OVERLAPPED structure within a TransactionControl object.
|
|
|
|
TransactionControl *ptc= (TransactionControl *)lpo;
|
|
|
|
ptc= (TransactionControl *) (PBYTE(ptc) - (PBYTE(&(ptc->ov)) - PBYTE(ptc)));
|
|
|
|
TransactionControl tc= *ptc; // Copy the transaction record to a stack frame variable
|
|
|
|
VFree(ptc); // Release heap storage for the transaction
|
|
|
|
tc.puio->FinishTransaction();
|
|
|
|
if (tc.pfnCompletionRoutine)
|
|
{
|
|
ASSERT(!tc.hEvent && !tc.puiCompletionCode);
|
|
|
|
tc.pfnCompletionRoutine(tc.pvEnvironment, tc.pvTransaction, fdwError, cbTransferred);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!tc.pvEnvironment && !tc.pvTransaction);
|
|
|
|
if (tc.puiCompletionCode) *(tc.puiCompletionCode)= fdwError;
|
|
|
|
ASSERT(tc.hEvent);
|
|
|
|
SetEvent(tc.hEvent);
|
|
}
|
|
}
|
|
|
|
void CUnbufferedIO::IOTransaction(BOOL fWrite, PVOID pv, UINT ibFileLow, UINT ibFileHigh,
|
|
UINT cb, PUINT puiCompletionCode, HANDLE hEvent
|
|
)
|
|
{
|
|
ASSERT(sizeof(PVOID) == sizeof(UINT));
|
|
|
|
ASSERT(m_fInitialed);
|
|
|
|
ASSERT(m_fFileAttached);
|
|
|
|
BeginTransaction();
|
|
|
|
TransactionControl *ptc = NULL;
|
|
BOOL fSynchronous = FALSE;
|
|
|
|
__try
|
|
{
|
|
ASSERT(!(ibFileLow % m_cbSector));
|
|
ASSERT(!(cb % m_cbSector));
|
|
ASSERT(!(UINT(pv) % m_cbSector));
|
|
|
|
ptc= (TransactionControl *) VAlloc(TRUE, sizeof(TransactionControl));
|
|
|
|
fSynchronous = !hEvent;
|
|
|
|
UINT uiCompletionCode = 0;
|
|
|
|
if (fSynchronous) hEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
else ResetEvent(hEvent);
|
|
|
|
ptc->hEvent = hEvent;
|
|
ptc->puio = this;
|
|
ptc->puiCompletionCode = fSynchronous? &uiCompletionCode : puiCompletionCode;
|
|
ptc->ov.Offset = ibFileLow;
|
|
ptc->ov.OffsetHigh = ibFileHigh;
|
|
|
|
ptc->pfnCompletionRoutine = NULL;
|
|
ptc->pvEnvironment = NULL;
|
|
ptc->pvTransaction = NULL;
|
|
|
|
BOOL fResult;
|
|
ULONG cbXfered= 0;
|
|
|
|
if (uOpSys == WIN40)
|
|
{
|
|
SetFilePointer(m_hFile,ibFileLow,(LONG *)&(ptc->ov.OffsetHigh),FILE_BEGIN);
|
|
if (fWrite) fResult= WriteFile(m_hFile, pv, cb, &cbXfered, NULL /* &(ptc->ov) */);
|
|
else fResult= ReadFile(m_hFile, pv, cb, &cbXfered, NULL /* &(ptc->ov) */);
|
|
}
|
|
else
|
|
{
|
|
if (fWrite) fResult= WriteFileEx(m_hFile, pv, cb, &(ptc->ov), UnbufferedIOCompletionRoutine);
|
|
else fResult= ReadFileEx(m_hFile, pv, cb, &(ptc->ov), UnbufferedIOCompletionRoutine);
|
|
}
|
|
|
|
if (!fResult && GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
#ifdef _DEBUG
|
|
|
|
UINT uiLastError= GetLastError();
|
|
|
|
#endif // _DEBUG
|
|
|
|
if (puiCompletionCode) *puiCompletionCode= GetLastError();
|
|
|
|
RaiseException(fWrite? STATUS_DISK_WRITE_ERROR : STATUS_DISK_READ_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
|
|
if (uOpSys == WIN40)
|
|
UnbufferedIOCompletionRoutine(fResult? NO_ERROR : GetLastError(), cbXfered, &(ptc->ov));
|
|
|
|
ReleaseTransaction();
|
|
|
|
if (!fSynchronous) __leave;
|
|
|
|
UINT uiResult;
|
|
|
|
do uiResult= WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
|
|
while (uiResult != WAIT_OBJECT_0);
|
|
|
|
if (puiCompletionCode) *puiCompletionCode= uiCompletionCode;
|
|
}
|
|
__finally
|
|
{
|
|
if (fSynchronous && hEvent) CloseHandle(hEvent);
|
|
|
|
if (_abnormal_termination())
|
|
{
|
|
AbortTransaction();
|
|
|
|
if (ptc) { VFree(ptc); ptc= NULL; }
|
|
}
|
|
}
|
|
}
|
|
|
|
void CUnbufferedIO::StartIOTransaction(BOOL fWrite, PVOID pvData, UINT ibFileLow, UINT ibFileHigh, UINT cb,
|
|
PVOID pvEnvironment, PVOID pvTransaction
|
|
)
|
|
{
|
|
ASSERT(m_pfnCompletion);
|
|
|
|
ASSERT(sizeof(PVOID) == sizeof(UINT));
|
|
|
|
ASSERT(m_fInitialed);
|
|
|
|
ASSERT(m_fFileAttached);
|
|
|
|
BeginTransaction();
|
|
|
|
TransactionControl *ptc= NULL;
|
|
|
|
__try
|
|
{
|
|
ASSERT(!(ibFileLow % m_cbSector));
|
|
ASSERT(!(cb % m_cbSector));
|
|
ASSERT(!(UINT(pvData) % m_cbSector));
|
|
|
|
ptc= (TransactionControl *) VAlloc(TRUE, sizeof(TransactionControl));
|
|
|
|
ptc->hEvent = NULL;
|
|
ptc->puiCompletionCode = NULL;
|
|
ptc->puio = this;
|
|
ptc->pfnCompletionRoutine = m_pfnCompletion;
|
|
ptc->pvEnvironment = pvEnvironment;
|
|
ptc->pvTransaction = pvTransaction;
|
|
ptc->ov.Offset = ibFileLow;
|
|
ptc->ov.OffsetHigh = ibFileHigh;
|
|
|
|
BOOL fResult;
|
|
ULONG cbXfered= 0;
|
|
|
|
if (uOpSys == WIN40)
|
|
{
|
|
SetFilePointer(m_hFile,ibFileLow,(LONG *)&(ptc->ov.OffsetHigh),FILE_BEGIN);
|
|
|
|
if (fWrite) fResult= WriteFile(m_hFile, pvData, cb, &cbXfered, NULL /* &(ptc->ov) */);
|
|
else fResult= ReadFile(m_hFile, pvData, cb, &cbXfered, NULL /* &(ptc->ov) */);
|
|
}
|
|
else
|
|
{
|
|
if (fWrite) fResult= WriteFileEx(m_hFile, pvData, cb, &(ptc->ov), UnbufferedIOCompletionRoutine);
|
|
else fResult= ReadFileEx(m_hFile, pvData, cb, &(ptc->ov), UnbufferedIOCompletionRoutine);
|
|
}
|
|
|
|
if (!fResult && GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
#ifdef _DEBUG
|
|
|
|
UINT uiLastError= GetLastError();
|
|
|
|
#endif // _DEBUG
|
|
|
|
RaiseException(fWrite? STATUS_DISK_WRITE_ERROR : STATUS_DISK_READ_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
|
|
if (uOpSys == WIN40)
|
|
UnbufferedIOCompletionRoutine(fResult? NO_ERROR : GetLastError(), cbXfered, &(ptc->ov));
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination())
|
|
{
|
|
AbortTransaction();
|
|
|
|
if (ptc) { VFree(ptc); ptc= NULL; }
|
|
}
|
|
}
|
|
|
|
ReleaseTransaction();
|
|
}
|
|
|
|
void CUnbufferedIO::UnmapImage()
|
|
{
|
|
ASSERT(m_pvMemoryImage && m_hMapFile);
|
|
|
|
UnmapViewOfFile(m_pvMemoryImage); m_pvMemoryImage= NULL;
|
|
|
|
CloseHandle(m_hMapFile); m_hMapFile= NULL;
|
|
}
|
|
|
|
PVOID CUnbufferedIO::MappedImage( )
|
|
{
|
|
ASSERT(m_fInitialed);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
DWORD iLastError= 0;
|
|
|
|
#endif // _DEBUG
|
|
|
|
ASSERT(m_fFileAttached);
|
|
|
|
if (m_pvMemoryImage) return m_pvMemoryImage;
|
|
|
|
__try
|
|
{
|
|
ASSERT(!m_hMapFile);
|
|
|
|
m_hMapFile = CreateFileMapping(m_hFile, (LPSECURITY_ATTRIBUTES) NULL,
|
|
PAGE_READONLY, 0, 0, NULL
|
|
);
|
|
|
|
if (!m_hMapFile || !(m_pvMemoryImage = MapViewOfFile(m_hMapFile, FILE_MAP_READ, 0, 0, 0)))
|
|
{
|
|
UINT ec= GetLastError();
|
|
|
|
#ifdef _DEBUG
|
|
UINT cbFile = GetFileSize(m_hFile, 0);
|
|
UINT iResult = SetFilePointer(m_hFile, 0, 0, FILE_BEGIN);
|
|
#endif // _DEBUG
|
|
|
|
#ifdef _DEBUG
|
|
MessageBox(NULL, "In CUnbufferedIO::MappedImage; Failure: !m_hMapFile || !m_pvMemoryImage", "Search System Failure", MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
#endif // _DEBUG
|
|
|
|
RaiseException((ec == ERROR_NOT_ENOUGH_MEMORY)? STATUS_NO_MEMORY : STATUS_SYSTEM_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (_abnormal_termination() && m_hMapFile)
|
|
{
|
|
CloseHandle(m_hMapFile); m_hMapFile= NULL;
|
|
}
|
|
}
|
|
|
|
return m_pvMemoryImage;
|
|
}
|
|
|
|
|
|
void CUnbufferedIO::MakePermanent(PSZ pszFileName, BOOL fAllowOverwrite, int cbSize)
|
|
{
|
|
// BugBug! If we have outstanding I/O transactions, will this call fail?
|
|
// Or will the CloseHandle call simply wait until the last transaction
|
|
// finishes? If not we'll have to add code to track the number of
|
|
// active I/O transactions.
|
|
|
|
BOOL fMoved = FALSE;
|
|
|
|
ASSERT(m_fTemporary);
|
|
|
|
FILENAMEBUFFER szFileName ;
|
|
FILENAMEBUFFER szName ;
|
|
FILENAMEBUFFER szExtension;
|
|
|
|
_splitpath(pszFileName, NULL, NULL, szName, szExtension);
|
|
|
|
lstrcpy(szFileName, m_szPath);
|
|
|
|
UINT cbPath= lstrlen(szFileName);
|
|
|
|
if (!cbPath || szFileName[cbPath - 1] != '\\')
|
|
lstrcat(szFileName, "\\");
|
|
|
|
lstrcat(szFileName, szName);
|
|
lstrcat(szFileName, szExtension);
|
|
|
|
CloseHandle(m_hFile);
|
|
|
|
m_fFileAttached = FALSE;
|
|
m_fTemporary = FALSE;
|
|
|
|
m_hFile = NULL;
|
|
m_cbSector = 0;
|
|
m_cbCluster = 0;
|
|
|
|
// REVIEW: Win95 supports MoveFileEx so why not use the same code for
|
|
// all three operating systems?
|
|
|
|
if (uOpSys != WIN40)
|
|
{
|
|
fMoved= MoveFileEx(m_szFile, szFileName,
|
|
fAllowOverwrite? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
|
|
: MOVEFILE_COPY_ALLOWED
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (fAllowOverwrite && lstrcmp(m_szFile, szFileName))
|
|
DeleteFile(szFileName);
|
|
|
|
fMoved= MoveFile(m_szFile, szFileName);
|
|
}
|
|
|
|
if (!fMoved)
|
|
{
|
|
BOOL fCopied= CopyFile(m_szFile, szFileName, !fAllowOverwrite);
|
|
|
|
DeleteFile(m_szFile);
|
|
|
|
if (!fCopied)
|
|
RaiseException(STATUS_DISK_WRITE_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
UINT uiLastError= GetLastError();
|
|
|
|
#endif // _DEBUG
|
|
|
|
CUnbufferedIO *puio= OpenExistingFile(fMoved? szFileName : m_szFile, TRUE);
|
|
|
|
if (!puio) RaiseException(STATUS_DISK_OPEN_ERROR, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
|
|
m_hFile= puio->m_hFile;
|
|
|
|
if (fMoved && cbSize >= 0)
|
|
{
|
|
cbSize= puio->m_cbSector * ((cbSize + puio->m_cbSector - 1) / puio->m_cbSector);
|
|
|
|
SetFilePointer(m_hFile, cbSize, NULL, FILE_BEGIN);
|
|
SetEndOfFile (m_hFile);
|
|
}
|
|
|
|
m_fFileAttached = TRUE;
|
|
m_fTemporary = !fMoved;
|
|
m_cbSector = puio->m_cbSector;
|
|
m_cbCluster = puio->m_cbCluster;
|
|
|
|
lstrcpy(m_szFile, puio->m_szFile);
|
|
lstrcpy(m_szPath, puio->m_szPath);
|
|
|
|
puio->m_fFileAttached= FALSE;
|
|
|
|
delete puio;
|
|
}
|