1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "gptdisk.hpp"
4#include "layer-gpt.hpp"
5
6#include <cassert>
7#include <filesystem>
8#include <iostream>
9#include <ranges>
10#include <type_traits>
11#include <unistd.h>
12
13static std::shared_ptr<GPTDisk> open_gpt_disk(const std::string_view disk_path)
14{
15 std::cout << "Opening GPT disk '" << disk_path << "'..." << std::endl;
16 std::filesystem::path disk_path_fs = disk_path;
17 const auto disk_name = disk_path_fs.filename().string();
18
19 open_device::request req = { .device_name = strdup(string: disk_name.c_str()) };
20 open_device::response resp;
21 const auto result = manager->open_device(request: &req, response: &resp);
22 if (result != RPC_RESULT_OK || !resp.result.success)
23 {
24 std::cerr << "Error: failed to open device" << std::endl;
25 return nullptr;
26 }
27
28 free(pointer: req.device_name);
29 return std::make_shared<GPTDisk>(args&: resp.device, args: disk_name);
30}
31
32static void do_gpt_scan()
33{
34 using dentry = std::filesystem::directory_entry;
35 using diter = std::filesystem::directory_iterator;
36
37 std::cout << "Scanning for GPT partitions..." << std::endl;
38
39 const auto cond = [](const dentry &e) { return !e.is_directory() && e.path().filename() != "." && e.path().filename() != ".." && e.is_block_file(); };
40 for (const auto &entry : diter("/dev/block/") | std::ranges::views::filter(cond))
41 {
42 const std::string name = entry.path().filename();
43 std::cout << "Checking for '" << name << "'...";
44
45 const auto disk = open_gpt_disk(disk_path: name);
46 if (!disk)
47 {
48 std::cout << " (failed to open disk)" << std::endl;
49 continue;
50 }
51
52 if (!disk->initialise_gpt())
53 {
54 std::cout << " (not a valid GPT disk)" << std::endl;
55 continue;
56 }
57 }
58}
59
60static void do_gpt_serve(const std::string_view disk_path)
61{
62 const auto disk = open_gpt_disk(disk_path);
63 if (!disk)
64 {
65 std::cerr << "Error: failed to open disk" << std::endl;
66 return;
67 }
68
69 if (!disk->initialise_gpt())
70 {
71 std::cerr << "Error: not a valid GPT disk" << std::endl;
72 return;
73 }
74
75 GPTLayerServer server(disk, disk->name() + ".gpt");
76
77 std::cout << "Serving GPT partition layer for device " << disk->name() << std::endl;
78 server.run();
79}
80
81int main(int argc, char *argv[])
82{
83 enum working_mode
84 {
85 scan,
86 serve
87 } mode;
88
89 manager = std::make_unique<BlockdevManagerStub>(BLOCKDEV_MANAGER_RPC_SERVER_NAME);
90 std::string disk_path;
91
92 if (argc == 2 && std::string_view(argv[1]) == "--scan")
93 {
94 mode = scan;
95 do_gpt_scan();
96 return 0;
97 }
98 else if (argc == 2)
99 {
100 mode = serve;
101 disk_path = argv[1];
102 }
103 else if (argc == 3 && std::string_view(argv[1]) == "--")
104 {
105 mode = serve;
106 disk_path = argv[2];
107 }
108 else
109 {
110 std::cout << "Usage: " << argv[0] << " [--] <disk>" << std::endl;
111 std::cout << " " << argv[0] << " --scan" << std::endl;
112 std::cout << "Example: " << std::endl;
113 std::cout << " " << argv[0] << " /dev/disk1" << std::endl;
114 std::cout << " " << argv[0] << " disk1" << std::endl;
115 return 1;
116 }
117
118 switch (mode)
119 {
120 case serve:
121 {
122 // now resolve disk_path to a real path
123 if (!disk_path.starts_with(x: "/dev/"))
124 disk_path = "/dev/block/" + disk_path;
125
126 std::is_constant_evaluated();
127
128 if (access(path: disk_path.c_str(), F_OK) != 0)
129 {
130 std::cerr << "Error: " << disk_path << " does not exist" << std::endl;
131 return 1;
132 }
133
134 do_gpt_serve(disk_path);
135 break;
136 }
137
138 case scan: do_gpt_scan(); break;
139 default: assert(!"Invalid working mode"); // should never happen
140 }
141
142 return 0;
143}
144