1 : // Copyright 2011 Google Inc.
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 : #include <iostream>
20 : #include <vss.h>
21 : #include <vswriter.h>
22 : #include <vsbackup.h>
23 : #include <atlbase.h>
24 : #include "base/at_exit.h"
25 : #include "base/command_line.h"
26 : #include "base/logging.h"
27 : #include "base/process_util.h"
28 : #include "base/utf_string_conversions.h"
29 :
30 m : const char kHelp[] =
31 m : "Available options:\n"
32 m : " --volume=<volume> the volume to mount, e.g. C:\\\n"
33 m : " --snapshot=<drive letter> the drive letter to mount the snapshot on, "
34 m : "e.g. M:\n"
35 m : "\n"
36 m : "Example:\n"
37 m : " run_in_snapshot --volume=C:\\ --snapshot=M: -- cmd.exe /c echo no way\n";
38 :
39 :
40 m : int Usage() {
41 m : CommandLine* cmd_line = CommandLine::ForCurrentProcess();
42 :
43 m : std::cout << "Usage: " << cmd_line->GetProgram().BaseName().value().c_str()
44 m : << " [options] -- [command and argument]\n" << std::endl;
45 m : std::cout << kHelp;
46 :
47 m : return 1;
48 m : }
49 :
50 m : int main(int argc, char** argv) {
51 m : base::AtExitManager at_exit;
52 m : CommandLine::Init(argc, argv);
53 :
54 m : CommandLine* cmd_line = CommandLine::ForCurrentProcess();
55 :
56 m : std::wstring volume = cmd_line->GetSwitchValueNative("volume");
57 m : std::wstring snapshot = cmd_line->GetSwitchValueNative("snapshot");
58 m : CommandLine::StringVector args = cmd_line->GetArgs();
59 m : if (volume.empty() || snapshot.empty() || args.size() == 0) {
60 m : return Usage();
61 m : }
62 :
63 : // Initialize COM and open ourselves wide for callbacks by
64 : // CoInitializeSecurity.
65 m : HRESULT hr = ::CoInitialize(NULL);
66 m : if (SUCCEEDED(hr)) {
67 m : hr = ::CoInitializeSecurity(
68 m : NULL, // Allow *all* VSS writers to communicate back!
69 m : -1, // Default COM authentication service
70 m : NULL, // Default COM authorization service
71 m : NULL, // reserved parameter
72 m : RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Strongest COM authentication level
73 m : RPC_C_IMP_LEVEL_IDENTIFY, // Minimal impersonation abilities
74 m : NULL, // Default COM authentication settings
75 m : EOAC_NONE, // No special options
76 m : NULL); // Reserved parameter
77 m : }
78 :
79 m : DCHECK(SUCCEEDED(hr));
80 m : if (FAILED(hr)) {
81 m : LOG(ERROR) << "Failed to initialize COM";
82 m : return 1;
83 m : }
84 :
85 m : CComPtr<IVssBackupComponents> comp;
86 m : hr = ::CreateVssBackupComponents(&comp);
87 m : if (SUCCEEDED(hr))
88 m : hr = comp->InitializeForBackup(NULL);
89 m : if (SUCCEEDED(hr))
90 m : hr = comp->SetBackupState(true, true, VSS_BT_COPY, false);
91 :
92 m : DCHECK(SUCCEEDED(hr));
93 m : if (FAILED(hr)) {
94 m : LOG(ERROR) << "Failed to initialize snapshot, error " << hr;
95 m : return 1;
96 m : }
97 :
98 m : CComPtr<IVssAsync> async;
99 m : hr = comp->GatherWriterMetadata(&async);
100 m : if (SUCCEEDED(hr))
101 m : hr = async->Wait();
102 m : if (FAILED(hr)) {
103 m : LOG(ERROR) << "Failed to gather write data, error " << hr;
104 m : return 1;
105 m : }
106 :
107 m : VSS_ID id = {};
108 m : hr = comp->StartSnapshotSet(&id);
109 :
110 m : VSS_ID dummy = {};
111 m : if (SUCCEEDED(hr)) {
112 m : hr = comp->AddToSnapshotSet(const_cast<LPWSTR>(volume.c_str()),
113 m : GUID_NULL,
114 m : &dummy);
115 m : }
116 :
117 m : if (FAILED(hr)) {
118 m : LOG(ERROR) << "Failed to start snapshot, error " << hr;
119 m : return 1;
120 m : }
121 :
122 m : async.Release();
123 m : hr = comp->PrepareForBackup(&async);
124 m : if (SUCCEEDED(hr))
125 m : hr = async->Wait();
126 :
127 m : if (FAILED(hr)) {
128 m : LOG(ERROR) << "Failed to prepare for backup, error " << hr;
129 m : return 1;
130 m : }
131 :
132 m : async.Release();
133 m : hr = comp->DoSnapshotSet(&async);
134 m : if (SUCCEEDED(hr))
135 m : hr = async->Wait();
136 :
137 m : if (FAILED(hr)) {
138 m : LOG(ERROR) << "Failed to do snapshot, error " << hr;
139 m : return 1;
140 m : }
141 :
142 m : CComPtr<IVssEnumObject> enum_snapshots;
143 m : hr = comp->Query(GUID_NULL,
144 m : VSS_OBJECT_NONE,
145 m : VSS_OBJECT_SNAPSHOT,
146 m : &enum_snapshots);
147 m : if (FAILED(hr)) {
148 m : LOG(ERROR) << "Failed to query snapshot, error " << hr;
149 m : return 1;
150 m : }
151 :
152 m : VSS_OBJECT_PROP prop;
153 m : ULONG fetched = 0;
154 m : hr = enum_snapshots->Next(1, &prop, &fetched);
155 m : if (FAILED(hr) || hr == S_FALSE) {
156 m : LOG(ERROR) << "Failed to retrieve snapshot volume, error " << hr;
157 m : return 1;
158 m : }
159 :
160 : // Bind the snapshot to a drive letter.
161 m : BOOL defined = ::DefineDosDevice(0,
162 m : snapshot.c_str(),
163 m : prop.Obj.Snap.m_pwszSnapshotDeviceObject);
164 m : if (!defined) {
165 m : LOG(ERROR) << "Failed to assign a drive letter to snapshot";
166 m : return 1;
167 m : }
168 m : ::VssFreeSnapshotProperties(&prop.Obj.Snap);
169 :
170 m : FilePath cmd_path(args[0]);
171 m : CommandLine cmd(cmd_path);
172 m : for (size_t i = 1; i < args.size(); ++i)
173 m : cmd.AppendArgNative(args[i]);
174 :
175 m : int ret = 0;
176 : // Launch the command line we were given, and wait on it to complete.
177 m : base::LaunchOptions options;
178 m : options.wait = true;
179 m : if (!base::LaunchProcess(cmd, options, NULL)) {
180 m : LOG(ERROR) << "Unable to launch application";
181 m : ret = 1;
182 m : }
183 :
184 : // Remove the drive mapping.
185 m : ::DefineDosDevice(DDD_REMOVE_DEFINITION, snapshot.c_str(), NULL);
186 :
187 m : return ret;
188 m : }
|