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 using namespace std::chrono_literals;
42 using namespace android::dm;
43 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
44
Usage(void)45 static int Usage(void) {
46 std::cerr << "usage: dmctl <command> [command options]" << std::endl;
47 std::cerr << " dmctl -f file" << std::endl;
48 std::cerr << "commands:" << std::endl;
49 std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
50 std::cerr << " delete <dm-name>" << std::endl;
51 std::cerr << " list <devices | targets> [-v]" << std::endl;
52 std::cerr << " getpath <dm-name>" << std::endl;
53 std::cerr << " getuuid <dm-name>" << std::endl;
54 std::cerr << " info <dm-name>" << std::endl;
55 std::cerr << " status <dm-name>" << std::endl;
56 std::cerr << " resume <dm-name>" << std::endl;
57 std::cerr << " suspend <dm-name>" << std::endl;
58 std::cerr << " table <dm-name>" << std::endl;
59 std::cerr << " help" << std::endl;
60 std::cerr << std::endl;
61 std::cerr << "-f file reads command and all parameters from named file" << std::endl;
62 std::cerr << std::endl;
63 std::cerr << "Target syntax:" << std::endl;
64 std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
65 return -EINVAL;
66 }
67
68 class TargetParser final {
69 public:
TargetParser(int argc,char ** argv)70 TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
71
More() const72 bool More() const { return arg_index_ < argc_; }
Next()73 std::unique_ptr<DmTarget> Next() {
74 if (!HasArgs(3)) {
75 std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
76 return nullptr;
77 }
78
79 std::string target_type = NextArg();
80 uint64_t start_sector, num_sectors;
81 if (!android::base::ParseUint(NextArg(), &start_sector)) {
82 std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
83 return nullptr;
84 }
85 if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
86 std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
87 return nullptr;
88 }
89
90 if (target_type == "zero") {
91 return std::make_unique<DmTargetZero>(start_sector, num_sectors);
92 } else if (target_type == "linear") {
93 if (!HasArgs(2)) {
94 std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
95 return nullptr;
96 }
97
98 std::string block_device = NextArg();
99 uint64_t physical_sector;
100 if (!android::base::ParseUint(NextArg(), &physical_sector)) {
101 std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
102 return nullptr;
103 }
104 return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
105 physical_sector);
106 } else if (target_type == "android-verity") {
107 if (!HasArgs(2)) {
108 std::cerr << "Expected \"android-verity\" <public-key-id> <block_device>"
109 << std::endl;
110 return nullptr;
111 }
112 std::string keyid = NextArg();
113 std::string block_device = NextArg();
114 return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
115 block_device);
116 } else if (target_type == "bow") {
117 if (!HasArgs(1)) {
118 std::cerr << "Expected \"bow\" <block_device>" << std::endl;
119 return nullptr;
120 }
121 std::string block_device = NextArg();
122 return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
123 } else if (target_type == "snapshot-origin") {
124 if (!HasArgs(1)) {
125 std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
126 return nullptr;
127 }
128 std::string block_device = NextArg();
129 return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
130 block_device);
131 } else if (target_type == "snapshot") {
132 if (!HasArgs(4)) {
133 std::cerr
134 << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
135 << std::endl;
136 return nullptr;
137 }
138 std::string base_device = NextArg();
139 std::string cow_device = NextArg();
140 std::string mode_str = NextArg();
141 std::string chunk_size_str = NextArg();
142
143 SnapshotStorageMode mode;
144 if (mode_str == "P") {
145 mode = SnapshotStorageMode::Persistent;
146 } else if (mode_str == "N") {
147 mode = SnapshotStorageMode::Transient;
148 } else {
149 std::cerr << "Unrecognized mode: " << mode_str << "\n";
150 return nullptr;
151 }
152
153 uint32_t chunk_size;
154 if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
155 std::cerr << "Chunk size must be an unsigned integer.\n";
156 return nullptr;
157 }
158 return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
159 cow_device, mode, chunk_size);
160 } else if (target_type == "snapshot-merge") {
161 if (!HasArgs(3)) {
162 std::cerr
163 << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
164 << std::endl;
165 return nullptr;
166 }
167 std::string base_device = NextArg();
168 std::string cow_device = NextArg();
169 std::string chunk_size_str = NextArg();
170 SnapshotStorageMode mode = SnapshotStorageMode::Merge;
171
172 uint32_t chunk_size;
173 if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
174 std::cerr << "Chunk size must be an unsigned integer.\n";
175 return nullptr;
176 }
177 return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
178 cow_device, mode, chunk_size);
179 } else if (target_type == "user") {
180 if (!HasArgs(1)) {
181 std::cerr << "Expected \"user\" <control_device_name>" << std::endl;
182 return nullptr;
183 }
184 std::string control_device = NextArg();
185 return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
186 } else {
187 std::cerr << "Unrecognized target type: " << target_type << std::endl;
188 return nullptr;
189 }
190 }
191
192 private:
HasArgs(int count)193 bool HasArgs(int count) { return arg_index_ + count <= argc_; }
NextArg()194 const char* NextArg() {
195 CHECK(arg_index_ < argc_);
196 return argv_[arg_index_++];
197 }
PreviousArg()198 const char* PreviousArg() {
199 CHECK(arg_index_ >= 0);
200 return argv_[arg_index_ - 1];
201 }
202
203 private:
204 int arg_index_;
205 int argc_;
206 char** argv_;
207 };
208
parse_table_args(DmTable * table,int argc,char ** argv)209 static bool parse_table_args(DmTable* table, int argc, char** argv) {
210 // Parse extended options first.
211 int arg_index = 1;
212 while (arg_index < argc && argv[arg_index][0] == '-') {
213 if (strcmp(argv[arg_index], "-ro") == 0) {
214 table->set_readonly(true);
215 arg_index++;
216 } else {
217 std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
218 return -EINVAL;
219 }
220 }
221
222 // Parse everything else as target information.
223 TargetParser parser(argc - arg_index, argv + arg_index);
224 while (parser.More()) {
225 std::unique_ptr<DmTarget> target = parser.Next();
226 if (!target || !table->AddTarget(std::move(target))) {
227 return -EINVAL;
228 }
229 }
230
231 if (table->num_targets() == 0) {
232 std::cerr << "Must define at least one target." << std::endl;
233 return -EINVAL;
234 }
235 return 0;
236 }
237
DmCreateCmdHandler(int argc,char ** argv)238 static int DmCreateCmdHandler(int argc, char** argv) {
239 if (argc < 1) {
240 std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
241 return -EINVAL;
242 }
243 std::string name = argv[0];
244
245 DmTable table;
246 int ret = parse_table_args(&table, argc, argv);
247 if (ret) {
248 return ret;
249 }
250
251 std::string ignore_path;
252 DeviceMapper& dm = DeviceMapper::Instance();
253 if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
254 std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
255 return -EIO;
256 }
257 return 0;
258 }
259
DmDeleteCmdHandler(int argc,char ** argv)260 static int DmDeleteCmdHandler(int argc, char** argv) {
261 if (argc < 1) {
262 std::cerr << "Usage: dmctl delete <name>" << std::endl;
263 return -EINVAL;
264 }
265
266 std::string name = argv[0];
267 DeviceMapper& dm = DeviceMapper::Instance();
268 if (!dm.DeleteDevice(name)) {
269 std::cerr << "Failed to delete [" << name << "]" << std::endl;
270 return -EIO;
271 }
272
273 return 0;
274 }
275
DmReplaceCmdHandler(int argc,char ** argv)276 static int DmReplaceCmdHandler(int argc, char** argv) {
277 if (argc < 1) {
278 std::cerr << "Usage: dmctl replace <dm-name> <targets...>" << std::endl;
279 return -EINVAL;
280 }
281 std::string name = argv[0];
282
283 DmTable table;
284 int ret = parse_table_args(&table, argc, argv);
285 if (ret) {
286 return ret;
287 }
288
289 DeviceMapper& dm = DeviceMapper::Instance();
290 if (!dm.LoadTableAndActivate(name, table)) {
291 std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
292 return -EIO;
293 }
294 return 0;
295 }
296
DmListTargets(DeviceMapper & dm,int argc,char ** argv)297 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
298 [[maybe_unused]] char** argv) {
299 std::vector<DmTargetTypeInfo> targets;
300 if (!dm.GetAvailableTargets(&targets)) {
301 std::cerr << "Failed to read available device mapper targets" << std::endl;
302 return -errno;
303 }
304
305 std::cout << "Available Device Mapper Targets:" << std::endl;
306 if (targets.empty()) {
307 std::cout << " <empty>" << std::endl;
308 return 0;
309 }
310
311 for (const auto& target : targets) {
312 std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
313 << std::endl;
314 }
315
316 return 0;
317 }
318
DmListDevices(DeviceMapper & dm,int argc,char ** argv)319 static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
320 std::vector<DmBlockDevice> devices;
321 if (!dm.GetAvailableDevices(&devices)) {
322 std::cerr << "Failed to read available device mapper devices" << std::endl;
323 return -errno;
324 }
325 std::cout << "Available Device Mapper Devices:" << std::endl;
326 if (devices.empty()) {
327 std::cout << " <empty>" << std::endl;
328 return 0;
329 }
330
331 bool verbose = (argc && (argv[0] == "-v"s));
332 for (const auto& dev : devices) {
333 std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
334 << dev.Minor() << std::endl;
335 if (verbose) {
336 std::vector<DeviceMapper::TargetInfo> table;
337 if (!dm.GetTableInfo(dev.name(), &table)) {
338 std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
339 << std::endl;
340 return -EINVAL;
341 }
342
343 uint32_t target_num = 1;
344 for (const auto& target : table) {
345 std::cout << " target#" << target_num << ": ";
346 std::cout << target.spec.sector_start << "-"
347 << (target.spec.sector_start + target.spec.length) << ": "
348 << target.spec.target_type;
349 if (!target.data.empty()) {
350 std::cout << ", " << target.data;
351 }
352 std::cout << std::endl;
353 target_num++;
354 }
355 }
356 }
357
358 return 0;
359 }
360
361 static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
362 {"targets", DmListTargets},
363 {"devices", DmListDevices},
364 };
365
DmListCmdHandler(int argc,char ** argv)366 static int DmListCmdHandler(int argc, char** argv) {
367 if (argc < 1) {
368 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
369 return -EINVAL;
370 }
371
372 DeviceMapper& dm = DeviceMapper::Instance();
373 for (const auto& l : listmap) {
374 if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
375 }
376
377 std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
378 return -EINVAL;
379 }
380
HelpCmdHandler(int,char **)381 static int HelpCmdHandler(int /* argc */, char** /* argv */) {
382 Usage();
383 return 0;
384 }
385
GetPathCmdHandler(int argc,char ** argv)386 static int GetPathCmdHandler(int argc, char** argv) {
387 if (argc != 1) {
388 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
389 return -EINVAL;
390 }
391
392 DeviceMapper& dm = DeviceMapper::Instance();
393 std::string path;
394 if (!dm.GetDmDevicePathByName(argv[0], &path)) {
395 std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
396 return -EINVAL;
397 }
398 std::cout << path << std::endl;
399 return 0;
400 }
401
GetUuidCmdHandler(int argc,char ** argv)402 static int GetUuidCmdHandler(int argc, char** argv) {
403 if (argc != 1) {
404 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
405 return -EINVAL;
406 }
407
408 DeviceMapper& dm = DeviceMapper::Instance();
409 std::string uuid;
410 if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
411 std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
412 return -EINVAL;
413 }
414 std::cout << uuid << std::endl;
415 return 0;
416 }
417
InfoCmdHandler(int argc,char ** argv)418 static int InfoCmdHandler(int argc, char** argv) {
419 if (argc != 1) {
420 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
421 return -EINVAL;
422 }
423
424 DeviceMapper& dm = DeviceMapper::Instance();
425 auto info = dm.GetDetailedInfo(argv[0]);
426 if (!info) {
427 std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
428 return -EINVAL;
429 }
430
431 constexpr int spacing = 14;
432 std::cout << std::left << std::setw(spacing) << "device"
433 << ": " << argv[0] << std::endl;
434 std::cout << std::left << std::setw(spacing) << "active"
435 << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
436 std::cout << std::left << std::setw(spacing) << "access"
437 << ": ";
438 if (info->IsReadOnly()) {
439 std::cout << "ro ";
440 } else {
441 std::cout << "rw ";
442 }
443 std::cout << std::endl;
444 std::cout << std::left << std::setw(spacing) << "activeTable"
445 << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
446 std::cout << std::left << std::setw(spacing) << "inactiveTable"
447 << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
448 std::cout << std::left << std::setw(spacing) << "bufferFull"
449 << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
450 return 0;
451 }
452
DumpTable(const std::string & mode,int argc,char ** argv)453 static int DumpTable(const std::string& mode, int argc, char** argv) {
454 if (argc != 1) {
455 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
456 return -EINVAL;
457 }
458
459 DeviceMapper& dm = DeviceMapper::Instance();
460 std::vector<DeviceMapper::TargetInfo> table;
461 if (mode == "status") {
462 if (!dm.GetTableStatus(argv[0], &table)) {
463 std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
464 << std::endl;
465 return -EINVAL;
466 }
467 } else if (mode == "table") {
468 if (!dm.GetTableInfo(argv[0], &table)) {
469 std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
470 << std::endl;
471 return -EINVAL;
472 }
473 }
474 std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
475 for (const auto& target : table) {
476 std::cout << target.spec.sector_start << "-"
477 << (target.spec.sector_start + target.spec.length) << ": "
478 << target.spec.target_type;
479 if (!target.data.empty()) {
480 std::cout << ", " << target.data;
481 }
482 std::cout << std::endl;
483 }
484 return 0;
485 }
486
TableCmdHandler(int argc,char ** argv)487 static int TableCmdHandler(int argc, char** argv) {
488 return DumpTable("table", argc, argv);
489 }
490
StatusCmdHandler(int argc,char ** argv)491 static int StatusCmdHandler(int argc, char** argv) {
492 return DumpTable("status", argc, argv);
493 }
494
ResumeCmdHandler(int argc,char ** argv)495 static int ResumeCmdHandler(int argc, char** argv) {
496 if (argc != 1) {
497 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
498 return -EINVAL;
499 }
500
501 DeviceMapper& dm = DeviceMapper::Instance();
502 if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
503 std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
504 return -EINVAL;
505 }
506 return 0;
507 }
508
SuspendCmdHandler(int argc,char ** argv)509 static int SuspendCmdHandler(int argc, char** argv) {
510 if (argc != 1) {
511 std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
512 return -EINVAL;
513 }
514
515 DeviceMapper& dm = DeviceMapper::Instance();
516 if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
517 std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
518 return -EINVAL;
519 }
520 return 0;
521 }
522
523 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
524 // clang-format off
525 {"create", DmCreateCmdHandler},
526 {"delete", DmDeleteCmdHandler},
527 {"replace", DmReplaceCmdHandler},
528 {"list", DmListCmdHandler},
529 {"help", HelpCmdHandler},
530 {"getpath", GetPathCmdHandler},
531 {"getuuid", GetUuidCmdHandler},
532 {"info", InfoCmdHandler},
533 {"table", TableCmdHandler},
534 {"status", StatusCmdHandler},
535 {"resume", ResumeCmdHandler},
536 {"suspend", SuspendCmdHandler},
537 // clang-format on
538 };
539
ReadFile(const char * filename,std::vector<std::string> * args,std::vector<char * > * arg_ptrs)540 static bool ReadFile(const char* filename, std::vector<std::string>* args,
541 std::vector<char*>* arg_ptrs) {
542 std::ifstream file(filename);
543 if (!file) return false;
544
545 std::string arg;
546 while (file >> arg) args->push_back(arg);
547
548 for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
549 return true;
550 }
551
main(int argc,char ** argv)552 int main(int argc, char** argv) {
553 android::base::InitLogging(argv, &android::base::StderrLogger);
554 if (argc < 2) {
555 return Usage();
556 }
557
558 std::vector<std::string> args;
559 std::vector<char*> arg_ptrs;
560 if (std::string("-f") == argv[1]) {
561 if (argc != 3) {
562 return Usage();
563 }
564
565 args.push_back(argv[0]);
566 if (!ReadFile(argv[2], &args, &arg_ptrs)) {
567 return Usage();
568 }
569
570 argc = arg_ptrs.size();
571 argv = &arg_ptrs[0];
572 }
573
574 for (const auto& cmd : cmdmap) {
575 if (cmd.first == argv[1]) {
576 return cmd.second(argc - 2, argv + 2);
577 }
578 }
579
580 return Usage();
581 }
582