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