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