525 lines
15 KiB
C
525 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
store.c
|
||
|
||
Abstract:
|
||
|
||
Interface for storing and retrieving checkpoint data on the quorum
|
||
disk.
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 1/14/1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "cpp.h"
|
||
#include <ntddvol.h>
|
||
|
||
|
||
DWORD
|
||
CppGetCheckpointFile(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD dwId,
|
||
OUT OPTIONAL LPWSTR *pDirectoryName,
|
||
OUT LPWSTR *pFileName,
|
||
IN OPTIONAL LPCWSTR lpszQuorumDir,
|
||
IN BOOLEAN fCryptoCheckpoint
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs the correct directory and file names for the checkpoint
|
||
file on the quorum disk.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the quorum resource.
|
||
|
||
dwId - Supplies the checkpoint ID
|
||
|
||
DirectoryName - if present, returns the full name of the directory the
|
||
checkpoint file should be created in. This buffer must be
|
||
freed by the caller with LocalFree
|
||
|
||
FileName - Returns the full pathname of the checkpoint file. This buffer must
|
||
be freed by the caller with LocalFree
|
||
|
||
lpszQuorumDir - If present, supplies the quorum directory to use.
|
||
If not present, the current quorum directory is used.
|
||
|
||
fCryptoCheckpoint - Indicates if the checkpoint is a crypto checkpoint.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status;
|
||
LPCWSTR ResourceId;
|
||
LPWSTR QuorumDir=NULL;
|
||
DWORD QuorumDirLength=0;
|
||
LPWSTR Dir;
|
||
DWORD DirLen;
|
||
LPWSTR File;
|
||
DWORD FileLen;
|
||
WCHAR Buff[13]; // 8.3 + NULL
|
||
|
||
if (lpszQuorumDir == NULL) {
|
||
Status = DmQuerySz( DmQuorumKey,
|
||
cszPath,
|
||
&QuorumDir,
|
||
&QuorumDirLength,
|
||
&QuorumDirLength);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppGetCheckpointFile failed to get quorum path %1!d!\n",
|
||
Status);
|
||
return(Status);
|
||
}
|
||
} else {
|
||
QuorumDir = (LPWSTR)lpszQuorumDir;
|
||
QuorumDirLength = (lstrlenW(QuorumDir)+1)*sizeof(WCHAR);
|
||
}
|
||
|
||
ResourceId = OmObjectId(Resource);
|
||
DirLen = QuorumDirLength + sizeof(WCHAR) + (lstrlenW(ResourceId)*sizeof(WCHAR));
|
||
Dir = LocalAlloc(LMEM_FIXED, DirLen);
|
||
if (Dir == NULL) {
|
||
if (lpszQuorumDir == NULL) {
|
||
LocalFree(QuorumDir);
|
||
}
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
|
||
lstrcpyW(Dir, QuorumDir);
|
||
lstrcatW(Dir, L"\\");
|
||
lstrcatW(Dir, ResourceId);
|
||
if (lpszQuorumDir == NULL) {
|
||
LocalFree(QuorumDir);
|
||
}
|
||
|
||
//
|
||
// Now construct the file name
|
||
//
|
||
FileLen = DirLen + sizeof(WCHAR) + sizeof(Buff);
|
||
File = LocalAlloc(LMEM_FIXED, FileLen);
|
||
if (File == NULL) {
|
||
LocalFree(Dir);
|
||
return(ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
if (fCryptoCheckpoint) {
|
||
wsprintfW(Buff, L"%08lx.CPR", dwId);
|
||
} else {
|
||
wsprintfW(Buff, L"%08lx.CPT", dwId);
|
||
}
|
||
lstrcpyW(File, Dir);
|
||
lstrcatW(File, L"\\");
|
||
lstrcatW(File, Buff);
|
||
|
||
if (ARGUMENT_PRESENT(pDirectoryName)) {
|
||
*pDirectoryName = Dir;
|
||
} else {
|
||
LocalFree(Dir);
|
||
}
|
||
*pFileName = File;
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
DWORD
|
||
CppReadCheckpoint(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD dwCheckpointId,
|
||
IN LPCWSTR lpszFileName,
|
||
IN BOOLEAN fCryptoCheckpoint
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads a checkpoint off the quorum disk.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource associated with this data.
|
||
|
||
dwCheckpointId - Supplies the unique checkpoint ID describing this data. The caller is
|
||
responsible for ensuring the uniqueness of the checkpoint ID.
|
||
|
||
lpszFileName - Supplies the filename where the data should be retrieved.
|
||
|
||
fCryptoCheckpoint - Indicates if the checkpoint is a crypto checkpoint.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status = ERROR_SUCCESS;
|
||
LPWSTR FileName = NULL;
|
||
BOOL Success;
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 8/2/99
|
||
//
|
||
// Remove gQuoLock acquisition from this function also following
|
||
// the reasoning outlined in CppWriteCheckpoint function. Note that
|
||
// the caller of this function will retry on specific errors.
|
||
// [We have to play these hacks to survive !]
|
||
//
|
||
Status = CppGetCheckpointFile(Resource,
|
||
dwCheckpointId,
|
||
NULL,
|
||
&FileName,
|
||
NULL,
|
||
fCryptoCheckpoint);
|
||
if (Status != ERROR_SUCCESS) {
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppReadCheckpoint - CppGetCheckpointFile failed %1!d!\n",
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[CP] CppReadCheckpoint restoring checkpoint from file %1!ws! to %2!ws!\n",
|
||
FileName,
|
||
lpszFileName);
|
||
|
||
Success = CopyFile(FileName, lpszFileName, FALSE);
|
||
if (!Success) {
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppReadCheckpoint unable to copy file %1!ws! to %2!ws!, error %3!d!\n",
|
||
FileName,
|
||
lpszFileName,
|
||
Status);
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppReadCheckpoint - Was that due to quorum resource not being up ???\n");
|
||
} else {
|
||
Status = ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
FnExit:
|
||
if (FileName) LocalFree(FileName);
|
||
//
|
||
// Adjust the return status if the quorum volume is truly offline and that is why this
|
||
// call failed.
|
||
//
|
||
if ( ( Status != ERROR_SUCCESS ) && ( CppIsQuorumVolumeOffline() == TRUE ) ) Status = ERROR_NOT_READY;
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
DWORD
|
||
CppWriteCheckpoint(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD dwCheckpointId,
|
||
IN LPCWSTR lpszFileName,
|
||
IN BOOLEAN fCryptoCheckpoint
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Writes a checkpoint to the quorum disk.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource associated with this data.
|
||
|
||
dwCheckpointId - Supplies the unique checkpoint ID describing this data. The caller is responsible
|
||
for ensuring the uniqueness of the checkpoint ID.
|
||
|
||
lpszFileName - Supplies the name of the file with the checkpoint data.
|
||
|
||
fCryptoCheckpoint - Indicates if the checkpoint is a crypto checkpoint.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD Status = ERROR_SUCCESS;
|
||
LPWSTR DirectoryName = NULL;
|
||
LPWSTR FileName = NULL;
|
||
BOOL Success;
|
||
HANDLE hDirectory = INVALID_HANDLE_VALUE;
|
||
|
||
//
|
||
// Chittur Subbaraman (chitturs) - 8/2/99
|
||
//
|
||
// Remove gQuoLock acquisition from this function. This is necessary
|
||
// since this function could get invoked indirectly from
|
||
// FmpRmDoInterlockedDecrement (as a part of the synchronous
|
||
// notification - Consider a case where the resource is failing
|
||
// or going offline and you have to rundown the checkpoints as
|
||
// a part of the synchronous notification. The rundown function
|
||
// CppRundownCheckpoints needs to wait until the CppRegNotifyThread
|
||
// completes and the latter could be stuck trying to write a
|
||
// checkpoint by calling this function) before the "blockingres" count is
|
||
// decremented. Now the quorum resource offline operation could
|
||
// be waiting inside FmpRmOfflineResource waiting for this count
|
||
// to go down to zero and this holds the gQuoLock (so as not to
|
||
// let any more resources to bump up this count). So if we want
|
||
// to get the gQuoLock here, we have an easy deadlock. Note that
|
||
// the caller of this function will retry on specific errors.
|
||
// [We have to play these hacks to survive !]
|
||
//
|
||
Status = CppGetCheckpointFile(Resource,
|
||
dwCheckpointId,
|
||
&DirectoryName,
|
||
&FileName,
|
||
NULL,
|
||
fCryptoCheckpoint);
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppWriteCheckpoint - CppGetCheckpointFile failed %1!d!\n",
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[CP] CppWriteCheckpoint checkpointing file %1!ws! to file %2!ws!\n",
|
||
lpszFileName,
|
||
FileName);
|
||
|
||
//
|
||
// Create the directory.
|
||
//
|
||
if (!CreateDirectory(DirectoryName, NULL))
|
||
{
|
||
Status = GetLastError();
|
||
if (Status != ERROR_ALREADY_EXISTS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppWriteCheckpoint unable to create directory %1!ws!, error %2!d!\n",
|
||
DirectoryName,
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
else
|
||
{
|
||
//the directory exists, it is alright, set Status to ERROR_SUCCESS
|
||
Status = ERROR_SUCCESS;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// The directory was newly created. Put the appropriate ACL on it
|
||
// so that only ADMINs can read it.
|
||
//
|
||
hDirectory = CreateFile(DirectoryName,
|
||
GENERIC_READ | WRITE_DAC | READ_CONTROL,
|
||
0,
|
||
NULL,
|
||
OPEN_ALWAYS,
|
||
FILE_FLAG_BACKUP_SEMANTICS,
|
||
NULL);
|
||
if (hDirectory == INVALID_HANDLE_VALUE)
|
||
{
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppWriteCheckpoint unable to open directory %1!ws!, error %2!d!\n",
|
||
DirectoryName,
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
Status = ClRtlSetObjSecurityInfo(hDirectory,
|
||
SE_FILE_OBJECT,
|
||
GENERIC_ALL,
|
||
GENERIC_ALL,
|
||
0);
|
||
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppWriteCheckpoint unable to set ACL on directory %1!ws!, error %2!d!\n",
|
||
DirectoryName,
|
||
Status);
|
||
goto FnExit;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Copy the file
|
||
//
|
||
Success = CopyFile(lpszFileName, FileName, FALSE);
|
||
if (!Success)
|
||
{
|
||
Status = GetLastError();
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppWriteCheckpoint unable to copy file %1!ws! to %2!ws!, error %3!d!\n",
|
||
lpszFileName,
|
||
FileName,
|
||
Status);
|
||
}
|
||
|
||
FnExit:
|
||
|
||
//clean up
|
||
if (DirectoryName) LocalFree(DirectoryName);
|
||
if (FileName) LocalFree(FileName);
|
||
if (hDirectory != INVALID_HANDLE_VALUE) CloseHandle(hDirectory);
|
||
|
||
//
|
||
// Adjust the return status if the quorum volume is truly offline and that is why this
|
||
// call failed.
|
||
//
|
||
if ( ( Status != ERROR_SUCCESS ) && ( CppIsQuorumVolumeOffline() == TRUE ) ) Status = ERROR_NOT_READY;
|
||
|
||
return(Status);
|
||
}
|
||
|
||
BOOL
|
||
CppIsQuorumVolumeOffline(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check the state of the quorum volume.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE - Quorum volume is offline.
|
||
|
||
FALSE - Quorum volume is online OR it is not possible to determine the quorum volume state
|
||
|
||
Notes:
|
||
|
||
This function is called in private CP functions to check if the quorum volume is offline or not.
|
||
This is necessary since the error codes returned in those functions when they try to
|
||
access the quorum disk when it is offline does not deterministically point out the state
|
||
of the disk. Note that this function will only perform its job when the quorum volume is
|
||
a physical disk since the storage stack drivers alone implement the IOCTL_IS_VOLUME_OFFLINE
|
||
at the time of this implementation.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
DWORD dwStatus;
|
||
DWORD cbBytesReturned = 0;
|
||
WCHAR szFileName[10];
|
||
WCHAR szQuorumLogPath[MAX_PATH];
|
||
WCHAR szQuorumDriveLetter[4];
|
||
BOOL fOffline = FALSE;
|
||
|
||
//
|
||
// Get the quorum log path so that we can get the quorum drive letter off it.
|
||
//
|
||
dwStatus = DmGetQuorumLogPath( szQuorumLogPath, sizeof( szQuorumLogPath ) );
|
||
|
||
if ( dwStatus != ERROR_SUCCESS )
|
||
{
|
||
ClRtlLogPrint(LOG_CRITICAL,
|
||
"[CP] CppIsQuorumVolumeOffline: DmGetQuorumLogPath failed, Status = %1!u!...\n",
|
||
dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Create a file name of the form \\.\Q:
|
||
//
|
||
lstrcpyn( szQuorumDriveLetter, szQuorumLogPath, 3 );
|
||
|
||
//
|
||
// See if the drive letter looks syntactically valid. We don't want to proceed further
|
||
// if the quorum is a network share.
|
||
//
|
||
if ( !ClRtlIsPathValid( szQuorumDriveLetter ) )
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[CP] CppIsQuorumVolumeOffline: Quorum path %1!ws! does not have a drive letter, returning...\n",
|
||
szQuorumLogPath);
|
||
goto FnExit;
|
||
}
|
||
|
||
lstrcpy( szFileName, L"\\\\.\\" );
|
||
lstrcat( szFileName, szQuorumDriveLetter );
|
||
|
||
//
|
||
// Open a handle to the quorum volume
|
||
//
|
||
hFile = CreateFileW( szFileName,
|
||
GENERIC_READ,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
0,
|
||
NULL );
|
||
|
||
if ( hFile == INVALID_HANDLE_VALUE )
|
||
{
|
||
dwStatus = GetLastError();
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[CP] CppIsQuorumVolumeOffline: CreateFile for file %1!ws! failed, Status = %2!u!...\n",
|
||
szFileName,
|
||
dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Check if the volume is offline or not
|
||
//
|
||
if ( !DeviceIoControl( hFile, // Device handle
|
||
IOCTL_VOLUME_IS_OFFLINE, // IOCTL code
|
||
NULL, // In buffer
|
||
0, // In buffer size
|
||
NULL, // Out buffer
|
||
0, // Out buffer size
|
||
&cbBytesReturned, // Bytes returned
|
||
NULL ) ) // Overlapped
|
||
{
|
||
dwStatus = GetLastError();
|
||
if ( dwStatus != ERROR_GEN_FAILURE )
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[CP] CppIsQuorumVolumeOffline: IOCTL_VOLUME_IS_OFFLINE failed, Status = %1!u!...\n",
|
||
dwStatus);
|
||
goto FnExit;
|
||
}
|
||
|
||
//
|
||
// Volume is offline, adjust return status
|
||
//
|
||
fOffline = TRUE;
|
||
|
||
ClRtlLogPrint(LOG_NOISE, "[CP] CppIsQuorumVolumeOffline: Quorum volume IS offline...\n");
|
||
|
||
FnExit:
|
||
if ( hFile != INVALID_HANDLE_VALUE ) CloseHandle( hFile );
|
||
|
||
return ( fOffline );
|
||
}// CppIsQuorumVolumeOffline
|
||
|