Esempio n. 1
0
/********************************************************************
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;
}
Esempio n. 2
0
/********************************************************************
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;
}
Esempio n. 3
0
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);
}