• 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 
42 using DeviceMapper = ::android::dm::DeviceMapper;
43 using DmTable = ::android::dm::DmTable;
44 using DmTarget = ::android::dm::DmTarget;
45 using DmTargetLinear = ::android::dm::DmTargetLinear;
46 using DmTargetZero = ::android::dm::DmTargetZero;
47 using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
48 using DmTargetBow = ::android::dm::DmTargetBow;
49 using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
50 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
51 
Usage(void)52 static int Usage(void) {
53     std::cerr << "usage: dmctl <command> [command options]" << std::endl;
54     std::cerr << "       dmctl -f file" << std::endl;
55     std::cerr << "commands:" << std::endl;
56     std::cerr << "  create <dm-name> [-ro] <targets...>" << std::endl;
57     std::cerr << "  delete <dm-name>" << std::endl;
58     std::cerr << "  list <devices | targets> [-v]" << std::endl;
59     std::cerr << "  getpath <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 {
126             std::cerr << "Unrecognized target type: " << target_type << std::endl;
127             return nullptr;
128         }
129     }
130 
131   private:
HasArgs(int count)132     bool HasArgs(int count) { return arg_index_ + count <= argc_; }
NextArg()133     const char* NextArg() {
134         CHECK(arg_index_ < argc_);
135         return argv_[arg_index_++];
136     }
PreviousArg()137     const char* PreviousArg() {
138         CHECK(arg_index_ >= 0);
139         return argv_[arg_index_ - 1];
140     }
141 
142   private:
143     int arg_index_;
144     int argc_;
145     char** argv_;
146 };
147 
DmCreateCmdHandler(int argc,char ** argv)148 static int DmCreateCmdHandler(int argc, char** argv) {
149     if (argc < 1) {
150         std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
151         return -EINVAL;
152     }
153     std::string name = argv[0];
154 
155     // Parse extended options first.
156     DmTable table;
157     int arg_index = 1;
158     while (arg_index < argc && argv[arg_index][0] == '-') {
159         if (strcmp(argv[arg_index], "-ro") == 0) {
160             table.set_readonly(true);
161             arg_index++;
162         } else {
163             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
164             return -EINVAL;
165         }
166     }
167 
168     // Parse everything else as target information.
169     TargetParser parser(argc - arg_index, argv + arg_index);
170     while (parser.More()) {
171         std::unique_ptr<DmTarget> target = parser.Next();
172         if (!target || !table.AddTarget(std::move(target))) {
173             return -EINVAL;
174         }
175     }
176 
177     if (table.num_targets() == 0) {
178         std::cerr << "Must define at least one target." << std::endl;
179         return -EINVAL;
180     }
181 
182     DeviceMapper& dm = DeviceMapper::Instance();
183     if (!dm.CreateDevice(name, table)) {
184         std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
185         return -EIO;
186     }
187     return 0;
188 }
189 
DmDeleteCmdHandler(int argc,char ** argv)190 static int DmDeleteCmdHandler(int argc, char** argv) {
191     if (argc < 1) {
192         std::cerr << "Usage: dmctl delete <name>" << std::endl;
193         return -EINVAL;
194     }
195 
196     std::string name = argv[0];
197     DeviceMapper& dm = DeviceMapper::Instance();
198     if (!dm.DeleteDevice(name)) {
199         std::cerr << "Failed to delete [" << name << "]" << std::endl;
200         return -EIO;
201     }
202 
203     return 0;
204 }
205 
DmListTargets(DeviceMapper & dm,int argc,char ** argv)206 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
207                          [[maybe_unused]] char** argv) {
208     std::vector<DmTargetTypeInfo> targets;
209     if (!dm.GetAvailableTargets(&targets)) {
210         std::cerr << "Failed to read available device mapper targets" << std::endl;
211         return -errno;
212     }
213 
214     std::cout << "Available Device Mapper Targets:" << std::endl;
215     if (targets.empty()) {
216         std::cout << "  <empty>" << std::endl;
217         return 0;
218     }
219 
220     for (const auto& target : targets) {
221         std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
222                   << std::endl;
223     }
224 
225     return 0;
226 }
227 
DmListDevices(DeviceMapper & dm,int argc,char ** argv)228 static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
229     std::vector<DmBlockDevice> devices;
230     if (!dm.GetAvailableDevices(&devices)) {
231         std::cerr << "Failed to read available device mapper devices" << std::endl;
232         return -errno;
233     }
234     std::cout << "Available Device Mapper Devices:" << std::endl;
235     if (devices.empty()) {
236         std::cout << "  <empty>" << std::endl;
237         return 0;
238     }
239 
240     bool verbose = (argc && (argv[0] == "-v"s));
241     for (const auto& dev : devices) {
242         std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
243                   << dev.Minor() << std::endl;
244         if (verbose) {
245             std::vector<DeviceMapper::TargetInfo> table;
246             if (!dm.GetTableInfo(dev.name(), &table)) {
247                 std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
248                           << std::endl;
249                 return -EINVAL;
250             }
251 
252             uint32_t target_num = 1;
253             for (const auto& target : table) {
254                 std::cout << "  target#" << target_num << ": ";
255                 std::cout << target.spec.sector_start << "-"
256                           << (target.spec.sector_start + target.spec.length) << ": "
257                           << target.spec.target_type;
258                 if (!target.data.empty()) {
259                     std::cout << ", " << target.data;
260                 }
261                 std::cout << std::endl;
262                 target_num++;
263             }
264         }
265     }
266 
267     return 0;
268 }
269 
270 static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
271         {"targets", DmListTargets},
272         {"devices", DmListDevices},
273 };
274 
DmListCmdHandler(int argc,char ** argv)275 static int DmListCmdHandler(int argc, char** argv) {
276     if (argc < 1) {
277         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
278         return -EINVAL;
279     }
280 
281     DeviceMapper& dm = DeviceMapper::Instance();
282     for (const auto& l : listmap) {
283         if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
284     }
285 
286     std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
287     return -EINVAL;
288 }
289 
HelpCmdHandler(int,char **)290 static int HelpCmdHandler(int /* argc */, char** /* argv */) {
291     Usage();
292     return 0;
293 }
294 
GetPathCmdHandler(int argc,char ** argv)295 static int GetPathCmdHandler(int argc, char** argv) {
296     if (argc != 1) {
297         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
298         return -EINVAL;
299     }
300 
301     DeviceMapper& dm = DeviceMapper::Instance();
302     std::string path;
303     if (!dm.GetDmDevicePathByName(argv[0], &path)) {
304         std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
305         return -EINVAL;
306     }
307     std::cout << path << std::endl;
308     return 0;
309 }
310 
TableCmdHandler(int argc,char ** argv)311 static int TableCmdHandler(int argc, char** argv) {
312     if (argc != 1) {
313         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
314         return -EINVAL;
315     }
316 
317     DeviceMapper& dm = DeviceMapper::Instance();
318     std::vector<DeviceMapper::TargetInfo> table;
319     if (!dm.GetTableInfo(argv[0], &table)) {
320         std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
321         return -EINVAL;
322     }
323     std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
324     for (const auto& target : table) {
325         std::cout << target.spec.sector_start << "-"
326                   << (target.spec.sector_start + target.spec.length) << ": "
327                   << target.spec.target_type;
328         if (!target.data.empty()) {
329             std::cout << ", " << target.data;
330         }
331         std::cout << std::endl;
332     }
333     return 0;
334 }
335 
336 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
337         // clang-format off
338         {"create", DmCreateCmdHandler},
339         {"delete", DmDeleteCmdHandler},
340         {"list", DmListCmdHandler},
341         {"help", HelpCmdHandler},
342         {"getpath", GetPathCmdHandler},
343         {"table", TableCmdHandler},
344         // clang-format on
345 };
346 
ReadFile(const char * filename,std::vector<std::string> * args,std::vector<char * > * arg_ptrs)347 static bool ReadFile(const char* filename, std::vector<std::string>* args,
348                      std::vector<char*>* arg_ptrs) {
349     std::ifstream file(filename);
350     if (!file) return false;
351 
352     std::string arg;
353     while (file >> arg) args->push_back(arg);
354 
355     for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
356     return true;
357 }
358 
main(int argc,char ** argv)359 int main(int argc, char** argv) {
360     android::base::InitLogging(argv, &android::base::StderrLogger);
361     if (argc < 2) {
362         return Usage();
363     }
364 
365     std::vector<std::string> args;
366     std::vector<char*> arg_ptrs;
367     if (std::string("-f") == argv[1]) {
368         if (argc != 3) {
369             return Usage();
370         }
371 
372         args.push_back(argv[0]);
373         if (!ReadFile(argv[2], &args, &arg_ptrs)) {
374             return Usage();
375         }
376 
377         argc = arg_ptrs.size();
378         argv = &arg_ptrs[0];
379     }
380 
381     for (const auto& cmd : cmdmap) {
382         if (cmd.first == argv[1]) {
383             return cmd.second(argc - 2, argv + 2);
384         }
385     }
386 
387     return Usage();
388 }
389