Bug 1626887 Followup - Don't leave a task folder around when we fail to create a scheduled task. r=bytesized

This is needed to prevent a failed attempt to create the task from blocking
out the ability to try again later if the two attempts are run under different
user contexts, where the second is more limited than the first, because the
less privileged attempt is unlikely to have permission to write to the folder
created by the more privileged one. In particular the MSI installer runs into
this scenario, because it makes one attempt to register the task as SYSTEM,
followed by a second attempt as the unelevated interactive user.

This is also nice because it keeps us from leaving an empty folder behind
under any circumstances, not just this specific situation.

Differential Revision: https://phabricator.services.mozilla.com/D77184
This commit is contained in:
Molly Howell 2020-05-27 23:37:05 +00:00
parent 43b37c090e
commit ff022d25ab

View File

@ -12,6 +12,7 @@
#include <taskschd.h>
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WinHeaderOnlyUtils.h"
@ -43,6 +44,12 @@ HRESULT RegisterTask(const wchar_t* uniqueToken,
// Make sure we don't try to register a task that already exists.
RemoveTask(uniqueToken);
// If we create a folder and then fail to create the task, we need to
// remember to delete the folder so that whatever set of permissions it ends
// up with doesn't interfere with trying to create the task again later, and
// so that we don't just leave an empty folder behind.
bool createdFolder = false;
HRESULT hr = S_OK;
RefPtr<ITaskService> scheduler;
ENSURE(CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER,
@ -61,13 +68,24 @@ HRESULT RegisterTask(const wchar_t* uniqueToken,
getter_AddRefs(taskFolder)))) {
hr = rootFolder->CreateFolder(vendorBStr.get(), VARIANT{},
getter_AddRefs(taskFolder));
// The folder already existing isn't an error.
if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) {
if (SUCCEEDED(hr)) {
createdFolder = true;
} else if (hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) {
// The folder already existing isn't an error, but anything else is.
LOG_ERROR(hr);
return hr;
}
}
auto cleanupFolder =
mozilla::MakeScopeExit([hr, createdFolder, &rootFolder, &vendorBStr] {
if (createdFolder && FAILED(hr)) {
// If this fails, we can't really handle that intelligently, so
// don't even bother to check the return code.
rootFolder->DeleteFolder(vendorBStr.get(), 0);
}
});
RefPtr<ITaskDefinition> newTask;
ENSURE(scheduler->NewTask(0, getter_AddRefs(newTask)));