1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <linux/dm-ioctl.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #include <android-base/logging.h>
26 #include <android-base/parseint.h>
27 #include <android-base/unique_fd.h>
28 #include <libdm/dm.h>
29
30 #include <fstream>
31 #include <functional>
32 #include <iomanip>
33 #include <ios>
34 #include <iostream>
35 #include <map>
36 #include <sstream>
37 #include <string>
38 #include <vector>
39
40 using namespace std::literals::string_literals;
41
42 using DeviceMapper = ::android::dm::DeviceMapper;
43 using DmTable = ::android::dm::DmTable;
44 using DmTarget = ::android::dm::DmTarget;
45 using DmTargetLinear = ::android::dm::DmTargetLinear;
46 using DmTargetZero = ::android::dm::DmTargetZero;
47 using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
48 using DmTargetBow = ::android::dm::DmTargetBow;
49 using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
50 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
51
Usage(void)52 static int Usage(void) {
53 std::cerr << "usage: dmctl <command> [command options]" << std::endl;
54 std::cerr << " dmctl -f file" << std::endl;
55 std::cerr << "commands:" << std::endl;
56 std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
57 std::cerr << " delete <dm-name>" << std::endl;
58 std::cerr << " list <devices | targets> [-v]" << std::endl;
59 std::cerr << " getpath <dm-name>" << std::endl;
60 std::cerr << " table <dm-name>" << std::endl;
61 std::cerr << " help" << std::endl;
62 std::cerr << std::endl;
63 std::cerr << "-f file reads command and all parameters from named file" << std::endl;
64 std::cerr << std::endl;
65 std::cerr << "Target syntax:" << std::endl;
66 std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
67 return -EINVAL;
68 }
69
70 class TargetParser final {
71 public:
TargetParser(int argc,char ** argv)72 TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
73
More() const74 bool More() const { return arg_index_ < argc_; }
Next()75 std::unique_ptr<DmTarget> Next() {
76 if (!HasArgs(3)) {
77 std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
78 return nullptr;
79 }
80
81 std::string target_type = NextArg();
82 uint64_t start_sector, num_sectors;
83 if (!android::base::ParseUint(NextArg(), &start_sector)) {
84 std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
85 return nullptr;
86 }
87 if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
88 std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
89 return nullptr;
90 }
91
92 if (target_type == "zero") {
93 return std::make_unique<DmTargetZero>(start_sector, num_sectors);
94 } else if (target_type == "linear") {
95 if (!HasArgs(2)) {
96 std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
97 return nullptr;
98 }
99
100 std::string block_device = NextArg();
101 uint64_t physical_sector;
102 if (!android::base::ParseUint(NextArg(), &physical_sector)) {
103 std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
104 return nullptr;
105 }
106 return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
107 physical_sector);
108 } else if (target_type == "android-verity") {
109 if (!HasArgs(2)) {
110 std::cerr << "Expected \"android-verity\" <public-key-id> <block_device>"
111 << std::endl;
112 return nullptr;
113 }
114 std::string keyid = NextArg();
115 std::string block_device = NextArg();
116 return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
117 block_device);
118 } else if (target_type == "bow") {
119 if (!HasArgs(1)) {
120 std::cerr << "Expected \"bow\" <block_device>" << std::endl;
121 return nullptr;
122 }
123 std::string block_device = NextArg();
124 return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
125 } else {
126 std::cerr << "Unrecognized target type: " << target_type << std::endl;
127 return nullptr;
128 }
129 }
130
131 private:
HasArgs(int count)132 bool HasArgs(int count) { return arg_index_ + count <= argc_; }
NextArg()133 const char* NextArg() {
134 CHECK(arg_index_ < argc_);
135 return argv_[arg_index_++];
136 }
PreviousArg()137 const char* PreviousArg() {
138 CHECK(arg_index_ >= 0);
139 return argv_[arg_index_ - 1];
140 }
141
142 private:
143 int arg_index_;
144 int argc_;
145 char** argv_;
146 };
147
DmCreateCmdHandler(int argc,char ** argv)148 static int DmCreateCmdHandler(int argc, char** argv) {
149 if (argc < 1) {
150 std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
151 return -EINVAL;
152 }
153 std::string name = argv[0];
154
155 // Parse extended options first.
156 DmTable table;
157 int arg_index = 1;
158 while (arg_index < argc && argv[arg_index][0] == '-') {
159 if (strcmp(argv[arg_index], "-ro") == 0) {
160 table.set_readonly(true);
161 arg_index++;
162 } else {
163 std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
164 return -EINVAL;
165 }
166 }
167
168 // Parse everything else as target information.
169 TargetParser parser(argc - arg_index, argv + arg_index);
170 while (parser.More()) {
171 std::unique_ptr<DmTarget> target = parser.Next();
172 if (!target || !table.AddTarget(std::move(target))) {
173 return -EINVAL;
174 }
175 }
176
177 if (table.num_targets() == 0) {
178 std::cerr << "Must define at least one target." << std::endl;
179 return -EINVAL;
180 }
181
182 DeviceMapper& dm = DeviceMapper::Instance();
183 if (!dm.CreateDevice(name, table)) {
184 std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
185 return -EIO;
186 }
187 return 0;
188 }
189
DmDeleteCmdHandler(int argc,char ** argv)190 static int DmDeleteCmdHandler(int argc, char** argv) {
191 if (argc < 1) {
192 std::cerr << "Usage: dmctl delete <name>" << std::endl;
193 return -EINVAL;
194 }
195
196 std::string name = argv[0];
197 DeviceMapper& dm = DeviceMapper::Instance();
198 if (!dm.DeleteDevice(name)) {
199 std::cerr << "Failed to delete [" << name << "]" << std::endl;
200 return -EIO;
201 }
202
203 return 0;
204 }
205
DmListTargets(DeviceMapper & dm,int argc,char ** argv)206 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
207 [[maybe_unused]] char** argv) {
208 std::vector<DmTargetTypeInfo> targets;
209 if (!dm.GetAvailableTargets(&targets)) {
210 std::cerr << "Failed to read available device mapper targets" << std::endl;
211 return -errno;
212 }
213
214 std::cout << "Available Device Mapper Targets:" << std::endl;
215 if (targets.empty()) {
216 std::cout << " <empty>" << std::endl;
217 return 0;
218 }
219
220 for (const auto& target : targets) {
221 std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
222 << std::endl;
223 }
224
225 return 0;
226 }
227
DmListDevices(DeviceMapper & dm,int argc,char ** argv)228 static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
229 std::vector<DmBlockDevice> devices;
230 if (!dm.GetAvailableDevices(&devices)) {
231 std::cerr << "Failed to read available device mapper devices" << std::endl;
232 return -errno;
233 }
234 std::cout << "Available Device Mapper Devices:" << std::endl;
235 if (devices.empty()) {
236 std::cout << " <empty>" << std::endl;
237 return 0;
238 }
239
240 bool verbose = (argc && (argv[0] == "-v"s));
241 for (const auto& dev : devices) {
242 std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
243 << dev.Minor() << std::endl;
244 if (verbose) {
245 std::vector<DeviceMapper::TargetInfo> table;
246 if (!dm.GetTableInfo(dev.name(), &table)) {
247 std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
248 << std::endl;
249 return -EINVAL;
250 }
251
252 uint32_t target_num = 1;
253 for (const auto& target : table) {
254 std::cout << " target#" << target_num << ": ";
255 std::cout << target.spec.sector_start << "-"
256 << (target.spec.sector_start + target.spec.length) << ": "
257 << target.spec.target_type;
258 if (!target.data.empty()) {
259 std::cout << ", " << target.data;
260 }
261 std::cout << std::endl;
262 target_num++;
263 }
264 }
265 }
266
267 return 0;
268 }
269
270 static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
271 {"targets", DmListTargets},
272 {"devices", DmListDevices},
273 };
274
DmListCmdHandler(int argc,char ** argv)275 static int DmListCmdHandler(int argc, char** argv) {
276 if (argc < 1) {
277 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
278 return -EINVAL;
279 }
280
281 DeviceMapper& dm = DeviceMapper::Instance();
282 for (const auto& l : listmap) {
283 if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
284 }
285
286 std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
287 return -EINVAL;
288 }
289
HelpCmdHandler(int,char **)290 static int HelpCmdHandler(int /* argc */, char** /* argv */) {
291 Usage();
292 return 0;
293 }
294
GetPathCmdHandler(int argc,char ** argv)295 static int GetPathCmdHandler(int argc, char** argv) {
296 if (argc != 1) {
297 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
298 return -EINVAL;
299 }
300
301 DeviceMapper& dm = DeviceMapper::Instance();
302 std::string path;
303 if (!dm.GetDmDevicePathByName(argv[0], &path)) {
304 std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
305 return -EINVAL;
306 }
307 std::cout << path << std::endl;
308 return 0;
309 }
310
TableCmdHandler(int argc,char ** argv)311 static int TableCmdHandler(int argc, char** argv) {
312 if (argc != 1) {
313 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
314 return -EINVAL;
315 }
316
317 DeviceMapper& dm = DeviceMapper::Instance();
318 std::vector<DeviceMapper::TargetInfo> table;
319 if (!dm.GetTableInfo(argv[0], &table)) {
320 std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
321 return -EINVAL;
322 }
323 std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
324 for (const auto& target : table) {
325 std::cout << target.spec.sector_start << "-"
326 << (target.spec.sector_start + target.spec.length) << ": "
327 << target.spec.target_type;
328 if (!target.data.empty()) {
329 std::cout << ", " << target.data;
330 }
331 std::cout << std::endl;
332 }
333 return 0;
334 }
335
336 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
337 // clang-format off
338 {"create", DmCreateCmdHandler},
339 {"delete", DmDeleteCmdHandler},
340 {"list", DmListCmdHandler},
341 {"help", HelpCmdHandler},
342 {"getpath", GetPathCmdHandler},
343 {"table", TableCmdHandler},
344 // clang-format on
345 };
346
ReadFile(const char * filename,std::vector<std::string> * args,std::vector<char * > * arg_ptrs)347 static bool ReadFile(const char* filename, std::vector<std::string>* args,
348 std::vector<char*>* arg_ptrs) {
349 std::ifstream file(filename);
350 if (!file) return false;
351
352 std::string arg;
353 while (file >> arg) args->push_back(arg);
354
355 for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
356 return true;
357 }
358
main(int argc,char ** argv)359 int main(int argc, char** argv) {
360 android::base::InitLogging(argv, &android::base::StderrLogger);
361 if (argc < 2) {
362 return Usage();
363 }
364
365 std::vector<std::string> args;
366 std::vector<char*> arg_ptrs;
367 if (std::string("-f") == argv[1]) {
368 if (argc != 3) {
369 return Usage();
370 }
371
372 args.push_back(argv[0]);
373 if (!ReadFile(argv[2], &args, &arg_ptrs)) {
374 return Usage();
375 }
376
377 argc = arg_ptrs.size();
378 argv = &arg_ptrs[0];
379 }
380
381 for (const auto& cmd : cmdmap) {
382 if (cmd.first == argv[1]) {
383 return cmd.second(argc - 2, argv + 2);
384 }
385 }
386
387 return Usage();
388 }
389