windows-nt/Source/XPSP1/NT/base/cluster/service/cp/store.c

525 lines
15 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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