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