| // Copyright 2011 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // A command line utility that'll create snapshot of a given volume, and map it |
| // to a drive letter, then run a command while the snapshot is mounted. This |
| // is handy to simulate cold-start conditions, as a newly created and mounted |
| // snapshot will be as cold as cold gets. |
| |
| #include <atlbase.h> |
| #include <vss.h> |
| #include <vswriter.h> |
| #include <vsbackup.h> // NOLINT: This has to be after vss.h and vswriter.h. |
| #include <iostream> |
| |
| #include "base/at_exit.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/process/launch.h" |
| #include "base/strings/utf_string_conversions.h" |
| |
| const char kHelp[] = |
| "Available options:\n" |
| " --volume=<volume> the volume to mount, e.g. C:\\\n" |
| " --snapshot=<drive letter> the drive letter to mount the snapshot on, " |
| "e.g. M:\n" |
| "\n" |
| "Example:\n" |
| " run_in_snapshot --volume=C:\\ --snapshot=M: -- cmd.exe /c echo no way\n"; |
| |
| |
| int Usage() { |
| base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| |
| std::cout << "Usage: " << cmd_line->GetProgram().BaseName().value().c_str() |
| << " [options] -- [command and argument]\n" << std::endl; |
| std::cout << kHelp; |
| |
| return 1; |
| } |
| |
| int main(int argc, char** argv) { |
| base::AtExitManager at_exit; |
| base::CommandLine::Init(argc, argv); |
| |
| base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| |
| std::wstring volume = cmd_line->GetSwitchValueNative("volume"); |
| std::wstring snapshot = cmd_line->GetSwitchValueNative("snapshot"); |
| base::CommandLine::StringVector args = cmd_line->GetArgs(); |
| if (volume.empty() || snapshot.empty() || args.size() == 0) { |
| return Usage(); |
| } |
| |
| // Initialize COM and open ourselves wide for callbacks by |
| // CoInitializeSecurity. |
| HRESULT hr = ::CoInitialize(NULL); |
| if (SUCCEEDED(hr)) { |
| hr = ::CoInitializeSecurity( |
| NULL, // Allow *all* VSS writers to communicate back! |
| -1, // Default COM authentication service |
| NULL, // Default COM authorization service |
| NULL, // reserved parameter |
| RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Strongest COM authentication level |
| RPC_C_IMP_LEVEL_IDENTIFY, // Minimal impersonation abilities |
| NULL, // Default COM authentication settings |
| EOAC_NONE, // No special options |
| NULL); // Reserved parameter |
| } |
| |
| DCHECK(SUCCEEDED(hr)); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to initialize COM"; |
| return 1; |
| } |
| |
| CComPtr<IVssBackupComponents> comp; |
| hr = ::CreateVssBackupComponents(&comp); |
| if (SUCCEEDED(hr)) |
| hr = comp->InitializeForBackup(NULL); |
| if (SUCCEEDED(hr)) |
| hr = comp->SetBackupState(true, true, VSS_BT_COPY, false); |
| |
| DCHECK(SUCCEEDED(hr)); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to initialize snapshot, error " << hr; |
| return 1; |
| } |
| |
| CComPtr<IVssAsync> async; |
| hr = comp->GatherWriterMetadata(&async); |
| if (SUCCEEDED(hr)) |
| hr = async->Wait(); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to gather write data, error " << hr; |
| return 1; |
| } |
| |
| VSS_ID id = {}; |
| hr = comp->StartSnapshotSet(&id); |
| |
| VSS_ID dummy = {}; |
| if (SUCCEEDED(hr)) { |
| hr = comp->AddToSnapshotSet(const_cast<LPWSTR>(volume.c_str()), |
| GUID_NULL, |
| &dummy); |
| } |
| |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to start snapshot, error " << hr; |
| return 1; |
| } |
| |
| async.Release(); |
| hr = comp->PrepareForBackup(&async); |
| if (SUCCEEDED(hr)) |
| hr = async->Wait(); |
| |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to prepare for backup, error " << hr; |
| return 1; |
| } |
| |
| async.Release(); |
| hr = comp->DoSnapshotSet(&async); |
| if (SUCCEEDED(hr)) |
| hr = async->Wait(); |
| |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to do snapshot, error " << hr; |
| return 1; |
| } |
| |
| CComPtr<IVssEnumObject> enum_snapshots; |
| hr = comp->Query(GUID_NULL, |
| VSS_OBJECT_NONE, |
| VSS_OBJECT_SNAPSHOT, |
| &enum_snapshots); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to query snapshot, error " << hr; |
| return 1; |
| } |
| |
| VSS_OBJECT_PROP prop; |
| ULONG fetched = 0; |
| hr = enum_snapshots->Next(1, &prop, &fetched); |
| if (FAILED(hr) || hr == S_FALSE) { |
| LOG(ERROR) << "Failed to retrieve snapshot volume, error " << hr; |
| return 1; |
| } |
| |
| // Bind the snapshot to a drive letter. |
| BOOL defined = ::DefineDosDevice(0, |
| snapshot.c_str(), |
| prop.Obj.Snap.m_pwszSnapshotDeviceObject); |
| if (!defined) { |
| LOG(ERROR) << "Failed to assign a drive letter to snapshot"; |
| return 1; |
| } |
| ::VssFreeSnapshotProperties(&prop.Obj.Snap); |
| |
| base::FilePath cmd_path(args[0]); |
| base::CommandLine cmd(cmd_path); |
| for (size_t i = 1; i < args.size(); ++i) |
| cmd.AppendArgNative(args[i]); |
| |
| int ret = 0; |
| // Launch the command line we were given, and wait on it to complete. |
| base::LaunchOptions options; |
| options.wait = true; |
| base::Process process = base::LaunchProcess(cmd, options); |
| if (!process.IsValid()) { |
| LOG(ERROR) << "Unable to launch application"; |
| ret = 1; |
| } |
| |
| // Remove the drive mapping. |
| ::DefineDosDevice(DDD_REMOVE_DEFINITION, snapshot.c_str(), NULL); |
| |
| return ret; |
| } |