/******************************************************************** RmuRegisterResources - Registers resources for the Restart Manager. This should be called rarely because it is expensive to run. Call functions like RmuAddFile for multiple resources then commit them as a batch of updates to RmuRegisterResources. Duplicate resources appear to be handled by Restart Manager. Only one WM_QUERYENDSESSION is being sent for each top-level window. ********************************************************************/ extern "C" HRESULT DAPI RmuRegisterResources( __in PRMU_SESSION pSession ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; HMODULE hModule = NULL; PFNRMREGISTERRESOURCES pfnRmRegisterResources = NULL; AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); ::EnterCriticalSection(&pSession->cs); er = vpfnRmRegisterResources( pSession->dwSessionHandle, pSession->cFilenames, pSession->rgsczFilenames, pSession->cApplications, pSession->rgApplications, pSession->cServiceNames, pSession->rgsczServiceNames ); ExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session."); // Empty the arrays if registered in case additional resources are added later. ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); LExit: ::LeaveCriticalSection(&pSession->cs); return hr; }
/******************************************************************** RmuEndSession - Ends the session. If the session was joined by RmuJoinSession, any remaining resources are registered before the session is ended (left). ********************************************************************/ extern "C" HRESULT DAPI RmuEndSession( __in PRMU_SESSION pSession ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); // Make sure all resources are registered if we joined the session. if (!pSession->fStartedSessionHandle) { hr = RmuRegisterResources(pSession); ExitOnFailure(hr, "Failed to register remaining resources."); } er = vpfnRmEndSession(pSession->dwSessionHandle); ExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); LExit: if (pSession->fInitialized) { ::DeleteCriticalSection(&pSession->cs); } ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); ReleaseNullMem(pSession); RmuUninitialize(); return hr; }
void HandleUnlock( __inout CFGDB_STRUCT *pcdb ) { HRESULT hr = S_OK; FILETIME ftRemote = { }; LPWSTR sczTempRemotePath = NULL; BOOL fCopyRemoteBack = FALSE; if (1 < pcdb->dwLockRefCount) { ExitFunction1(hr = S_OK); } Assert(0 < pcdb->dwLockRefCount); // Disconnect from database, if it's a connected remote database if (pcdb->fRemote && NULL != pcdb->psceDb) { fCopyRemoteBack = SceDatabaseChanged(pcdb->psceDb); hr = SceCloseDatabase(pcdb->psceDb); ExitOnFailure(hr, "Failed to close remote database"); pcdb->psceDb = NULL; if (fCopyRemoteBack) { hr = FileGetTime(pcdb->sczDbPath, NULL, NULL, &ftRemote); ExitOnFailure(hr, "Failed to get modified time of actual remote %ls", &ftRemote); // Since DB file wasn't locked, we have to verify that nobody changed it in the meantime. // Do it once before we try uploading to the remote (because uploading could be a lengthy operation on a slow connection to remote path) if (0 != ::CompareFileTime(&ftRemote, &pcdb->ftBeforeModify)) { hr = HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION); ExitOnFailure(hr, "database %ls was modified (before copy), we can't overwrite it!", pcdb->sczDbPath); } // Get it on the volume first which may take time // TODO: in some cases such as crashes or connection lost to network remote, // we'll leave a file behind here. We need a feature to look for and cleanup old files. hr = PathConcat(pcdb->sczDbDir, pcdb->sczGuid, &sczTempRemotePath); ExitOnFailure(hr, "Failed to get temp path in remote directory"); hr = FileEnsureCopy(pcdb->sczDbCopiedPath, sczTempRemotePath, TRUE); ExitOnFailure(hr, "Failed to copy remote database back to remote location (from %ls to %ls) due to changes", pcdb->sczDbCopiedPath, pcdb->sczDbPath); // Now do it again after the upload right before we do the actual move hr = FileGetTime(pcdb->sczDbPath, NULL, NULL, &ftRemote); ExitOnFailure(hr, "Failed to get modified time of original remote (again) %ls", &ftRemote); if (0 != ::CompareFileTime(&ftRemote, &pcdb->ftBeforeModify)) { hr = HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION); ExitOnFailure(hr, "database %ls was modified (after copy), we can't overwrite it!", pcdb->sczDbPath); } // Use MoveFile to ensure it's done as an atomic operation, so remote can never be left not existing. // There is a tiny chance we're reverting someone else's changes here if some other machine just moved the file between // the last timestamp check and this MoveFile call. I don't believe there is a way to fix that (we could open a lock on the file, // but then we can't use atomic MoveFile() API, meaning we could leave a partial file around in some cases, a HUGE no-no) // However, inadvertently overwriting a just-written db file is not a problem - Autosync on all machines will notice the fact // that the DB changed, re-sync it, at which time we will try again to re-propagate the changes. if (!::MoveFileExW(sczTempRemotePath, pcdb->sczDbPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { ExitWithLastError(hr, "Failed to move uploaded database path back to original remote location %ls", pcdb->sczDbPath); } } if (pcdb->fUpdateLastModified) { hr = FileGetTime(pcdb->sczDbCopiedPath, NULL, NULL, &pcdb->ftLastModified); if (E_FILENOTFOUND == hr || E_NOTFOUND == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to get modified time of copied db: %ls", pcdb->sczDbCopiedPath); } hr = FileEnsureDelete(pcdb->sczDbCopiedPath); ExitOnFailure(hr, "Failed to delete copied remote database from %ls", pcdb->sczDbCopiedPath); ReleaseNullStr(pcdb->sczDbCopiedPath); } for (DWORD i = 0; i < pcdb->cStreamsToDelete; ++i) { DeleteStream(pcdb->rgsczStreamsToDelete[i]); } ReleaseNullStrArray(pcdb->rgsczStreamsToDelete, pcdb->cStreamsToDelete); pcdb->fUpdateLastModified = FALSE; LExit: --pcdb->dwLockRefCount; ::LeaveCriticalSection(&pcdb->cs); if (FAILED(hr) && sczTempRemotePath) { // In case we left a temp file around, try to delete it before exiting (ignoring failure) FileEnsureDelete(sczTempRemotePath); } ReleaseStr(sczTempRemotePath); }