1 : // Copyright 2011 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // A command line utility that'll create snapshot of a given volume, and map it
16 : // to a drive letter, then run a command while the snapshot is mounted. This
17 : // is handy to simulate cold-start conditions, as a newly created and mounted
18 : // snapshot will be as cold as cold gets.
19 :
20 : #include <atlbase.h>
21 : #include <vss.h>
22 : #include <vswriter.h>
23 : #include <vsbackup.h> // NOLINT: This has to be after vss.h and vswriter.h.
24 : #include <iostream>
25 :
26 : #include "base/at_exit.h"
27 : #include "base/command_line.h"
28 : #include "base/logging.h"
29 : #include "base/process/launch.h"
30 : #include "base/strings/utf_string_conversions.h"
31 :
32 m : const char kHelp[] =
33 m : "Available options:\n"
34 m : " --volume=<volume> the volume to mount, e.g. C:\\\n"
35 m : " --snapshot=<drive letter> the drive letter to mount the snapshot on, "
36 m : "e.g. M:\n"
37 m : "\n"
38 m : "Example:\n"
39 m : " run_in_snapshot --volume=C:\\ --snapshot=M: -- cmd.exe /c echo no way\n";
40 :
41 :
42 m : int Usage() {
43 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
44 :
45 m : std::cout << "Usage: " << cmd_line->GetProgram().BaseName().value().c_str()
46 m : << " [options] -- [command and argument]\n" << std::endl;
47 m : std::cout << kHelp;
48 :
49 m : return 1;
50 m : }
51 :
52 m : int main(int argc, char** argv) {
53 m : base::AtExitManager at_exit;
54 m : base::CommandLine::Init(argc, argv);
55 :
56 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
57 :
58 m : std::wstring volume = cmd_line->GetSwitchValueNative("volume");
59 m : std::wstring snapshot = cmd_line->GetSwitchValueNative("snapshot");
60 m : base::CommandLine::StringVector args = cmd_line->GetArgs();
61 m : if (volume.empty() || snapshot.empty() || args.size() == 0) {
62 m : return Usage();
63 m : }
64 :
65 : // Initialize COM and open ourselves wide for callbacks by
66 : // CoInitializeSecurity.
67 m : HRESULT hr = ::CoInitialize(NULL);
68 m : if (SUCCEEDED(hr)) {
69 m : hr = ::CoInitializeSecurity(
70 m : NULL, // Allow *all* VSS writers to communicate back!
71 m : -1, // Default COM authentication service
72 m : NULL, // Default COM authorization service
73 m : NULL, // reserved parameter
74 m : RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Strongest COM authentication level
75 m : RPC_C_IMP_LEVEL_IDENTIFY, // Minimal impersonation abilities
76 m : NULL, // Default COM authentication settings
77 m : EOAC_NONE, // No special options
78 m : NULL); // Reserved parameter
79 m : }
80 :
81 m : DCHECK(SUCCEEDED(hr));
82 m : if (FAILED(hr)) {
83 m : LOG(ERROR) << "Failed to initialize COM";
84 m : return 1;
85 m : }
86 :
87 m : CComPtr<IVssBackupComponents> comp;
88 m : hr = ::CreateVssBackupComponents(&comp);
89 m : if (SUCCEEDED(hr))
90 m : hr = comp->InitializeForBackup(NULL);
91 m : if (SUCCEEDED(hr))
92 m : hr = comp->SetBackupState(true, true, VSS_BT_COPY, false);
93 :
94 m : DCHECK(SUCCEEDED(hr));
95 m : if (FAILED(hr)) {
96 m : LOG(ERROR) << "Failed to initialize snapshot, error " << hr;
97 m : return 1;
98 m : }
99 :
100 m : CComPtr<IVssAsync> async;
101 m : hr = comp->GatherWriterMetadata(&async);
102 m : if (SUCCEEDED(hr))
103 m : hr = async->Wait();
104 m : if (FAILED(hr)) {
105 m : LOG(ERROR) << "Failed to gather write data, error " << hr;
106 m : return 1;
107 m : }
108 :
109 m : VSS_ID id = {};
110 m : hr = comp->StartSnapshotSet(&id);
111 :
112 m : VSS_ID dummy = {};
113 m : if (SUCCEEDED(hr)) {
114 m : hr = comp->AddToSnapshotSet(const_cast<LPWSTR>(volume.c_str()),
115 m : GUID_NULL,
116 m : &dummy);
117 m : }
118 :
119 m : if (FAILED(hr)) {
120 m : LOG(ERROR) << "Failed to start snapshot, error " << hr;
121 m : return 1;
122 m : }
123 :
124 m : async.Release();
125 m : hr = comp->PrepareForBackup(&async);
126 m : if (SUCCEEDED(hr))
127 m : hr = async->Wait();
128 :
129 m : if (FAILED(hr)) {
130 m : LOG(ERROR) << "Failed to prepare for backup, error " << hr;
131 m : return 1;
132 m : }
133 :
134 m : async.Release();
135 m : hr = comp->DoSnapshotSet(&async);
136 m : if (SUCCEEDED(hr))
137 m : hr = async->Wait();
138 :
139 m : if (FAILED(hr)) {
140 m : LOG(ERROR) << "Failed to do snapshot, error " << hr;
141 m : return 1;
142 m : }
143 :
144 m : CComPtr<IVssEnumObject> enum_snapshots;
145 m : hr = comp->Query(GUID_NULL,
146 m : VSS_OBJECT_NONE,
147 m : VSS_OBJECT_SNAPSHOT,
148 m : &enum_snapshots);
149 m : if (FAILED(hr)) {
150 m : LOG(ERROR) << "Failed to query snapshot, error " << hr;
151 m : return 1;
152 m : }
153 :
154 m : VSS_OBJECT_PROP prop;
155 m : ULONG fetched = 0;
156 m : hr = enum_snapshots->Next(1, &prop, &fetched);
157 m : if (FAILED(hr) || hr == S_FALSE) {
158 m : LOG(ERROR) << "Failed to retrieve snapshot volume, error " << hr;
159 m : return 1;
160 m : }
161 :
162 : // Bind the snapshot to a drive letter.
163 m : BOOL defined = ::DefineDosDevice(0,
164 m : snapshot.c_str(),
165 m : prop.Obj.Snap.m_pwszSnapshotDeviceObject);
166 m : if (!defined) {
167 m : LOG(ERROR) << "Failed to assign a drive letter to snapshot";
168 m : return 1;
169 m : }
170 m : ::VssFreeSnapshotProperties(&prop.Obj.Snap);
171 :
172 m : base::FilePath cmd_path(args[0]);
173 m : base::CommandLine cmd(cmd_path);
174 m : for (size_t i = 1; i < args.size(); ++i)
175 m : cmd.AppendArgNative(args[i]);
176 :
177 m : int ret = 0;
178 : // Launch the command line we were given, and wait on it to complete.
179 m : base::LaunchOptions options;
180 m : options.wait = true;
181 m : base::Process process = base::LaunchProcess(cmd, options);
182 m : if (!process.IsValid()) {
183 m : LOG(ERROR) << "Unable to launch application";
184 m : ret = 1;
185 m : }
186 :
187 : // Remove the drive mapping.
188 m : ::DefineDosDevice(DDD_REMOVE_DEFINITION, snapshot.c_str(), NULL);
189 :
190 m : return ret;
191 m : }
|