• 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 <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