blob: 3191c2be30984b0a4533d8673b445f2a74fa0c74 [file] [log] [blame]
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenko262be3f2014-07-30 15:25:50 -07005#include "debugd/src/storage_tool.h"
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -07006
Alexis Saverye39d3492017-11-07 18:10:08 -08007#include <base/files/file.h>
8#include <base/files/file_path.h>
9#include <base/files/file_util.h>
10#include <base/strings/string_split.h>
11#include <base/strings/string_util.h>
12#include <fstream>
13#include <iostream>
14#include <linux/limits.h>
15#include <mntent.h>
16#include <string>
17#include <unistd.h>
18#include <vector>
19
Hardik Goyalb09d6b02019-08-13 16:15:50 -070020#include "debugd/src/helper_utils.h"
Alex Vakulenko262be3f2014-07-30 15:25:50 -070021#include "debugd/src/process_with_id.h"
22#include "debugd/src/process_with_output.h"
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070023
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070024namespace debugd {
25
Ben Chanaf125862017-02-08 23:11:18 -080026namespace {
27
28const char kSmartctl[] = "/usr/sbin/smartctl";
29const char kBadblocks[] = "/sbin/badblocks";
Kartik Hegde0227a062018-10-03 16:48:04 -060030const char kMountFile[] = "/proc/1/mounts";
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090031const char kSource[] = "/mnt/stateful_partition";
Ting Shen92dd7832018-12-21 17:45:07 +080032const char kMmc[] = "/usr/bin/mmc";
Oleh Lamzinfb868682019-09-03 19:23:28 +020033const char kNvme[] = "/usr/sbin/nvme";
Ben Chanaf125862017-02-08 23:11:18 -080034
35} // namespace
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070036
Alexis Saverye39d3492017-11-07 18:10:08 -080037const std::string StorageTool::GetPartition(const std::string& dst) {
38 std::string::const_reverse_iterator part = dst.rbegin();
39
40 if (!isdigit(*part))
41 return "";
42 part++;
43
44 while (part < dst.rend() && isdigit(*part))
45 part++;
46
47 return std::string(part.base(), dst.end());
48}
49
50void StorageTool::StripPartition(base::FilePath* dstPath) {
51 std::string dst = dstPath->value();
52 std::string part = StorageTool::GetPartition(dst);
53 if (part.empty())
54 return;
55
56 size_t location = dst.rfind(part);
57 size_t part_size = part.length();
58
59 // For devices matching dm-NN, the digits are not a partition.
60 if (dst.at(location - 1) == '-')
61 return;
62
63 // For devices that end with a digit, the kernel uses a 'p'
64 // as a separator. E.g., mmcblk1p2 and loop0p1, but not loop0.
65 if (dst.at(location - 1) == 'p') {
66 location--;
67 part_size++;
68
69 base::FilePath basename = dstPath->BaseName();
70 std::string bname = basename.value();
Oleh Lamzinfb868682019-09-03 19:23:28 +020071 if (bname.compare(0, 4, "loop") == 0 &&
72 bname.compare(bname.size() - part.length(), part.length(), part) == 0)
Alexis Saverye39d3492017-11-07 18:10:08 -080073 return;
74 }
75 dst.erase(location, part_size);
76 *dstPath = base::FilePath(dst);
77}
78
79const base::FilePath StorageTool::GetDevice(const base::FilePath& filesystemIn,
80 const base::FilePath& mountsFile) {
81 base::FilePath device;
82 base::ScopedFILE mountinfo(fopen(mountsFile.value().c_str(), "re"));
83 if (!mountinfo) {
84 PLOG(ERROR) << "Failed to open " << mountsFile.value();
85 return base::FilePath();
86 }
87
88 struct mntent mount_entry;
89 char buffer[PATH_MAX];
90
91 while (getmntent_r(mountinfo.get(), &mount_entry, buffer, sizeof(buffer))) {
92 const std::string mountpoint = mount_entry.mnt_dir;
93 if (mountpoint == filesystemIn.value()) {
94 device = base::FilePath(mount_entry.mnt_fsname);
95 break;
96 }
97 }
98
99 StorageTool::StripPartition(&device);
100 return device;
101}
102
103// This function is called by Smartctl to check for ATA devices.
104// Smartctl is only supported on ATA devices, so this function
105// will return false when other devices are used.
106bool StorageTool::IsSupported(const base::FilePath typeFile,
107 const base::FilePath vendFile,
108 std::string* errorMsg) {
109 base::FilePath r;
110 bool link = base::NormalizeFilePath(typeFile, &r);
111 if (!link) {
112 PLOG(ERROR) << "Failed to read device type link";
113 errorMsg->assign("<Failed to read device type link>");
114 return false;
115 }
116
117 size_t target = r.value().find("target");
118 if (target == -1) {
119 errorMsg->assign("<This feature is not supported>");
120 return false;
121 }
122
123 std::string vend;
124
125 if (!base::ReadFileToString(vendFile, &vend)) {
126 PLOG(ERROR) << "Failed to open " << vendFile.value();
127 errorMsg->assign("<Failed to open vendor file>");
128 return false;
129 }
130
131 if (vend.empty()) {
132 errorMsg->assign("<Failed to find device type>");
133 return false;
134 }
135
136 if (vend.compare(0, 3, "ATA") != 0) {
137 errorMsg->assign("<This feature is not supported>");
138 return false;
139 }
140
141 return true;
142}
143
Eric Carusoc93a15c2017-04-24 16:15:12 -0700144std::string StorageTool::Smartctl(const std::string& option) {
Oleh Lamzinfb868682019-09-03 19:23:28 +0200145 const base::FilePath device =
146 GetDevice(base::FilePath(kSource), base::FilePath(kMountFile));
Alexis Saverye39d3492017-11-07 18:10:08 -0800147
148 if (device.empty()) {
149 LOG(ERROR) << "Failed to find device for " << kSource;
150 return "<Failed to find device>";
151 }
152
153 base::FilePath bname = device.BaseName();
154
Ben Chan297c3c22013-07-17 17:34:12 -0700155 std::string path;
Hardik Goyalb09d6b02019-08-13 16:15:50 -0700156 if (!GetHelperPath("storage", &path))
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700157 return "<path too long>";
158
159 ProcessWithOutput process;
160 // Disabling sandboxing since smartctl requires higher privileges.
161 process.DisableSandbox();
162 if (!process.Init())
163 return "<process init failed>";
164
Alexis Saverye39d3492017-11-07 18:10:08 -0800165 if (bname.value().compare(0, 4, "nvme") == 0) {
166 process.AddArg(kSmartctl);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700167
Alexis Saverye39d3492017-11-07 18:10:08 -0800168 if (option == "attributes")
169 process.AddArg("-A");
170 if (option == "capabilities")
171 process.AddArg("-c");
172 if (option == "error")
173 process.AddStringOption("-l", "error");
Oleh Lamzinfb868682019-09-03 19:23:28 +0200174 if (option == "abort_test" || option == "health" || option == "selftest" ||
175 option == "short_test")
Alexis Saverye39d3492017-11-07 18:10:08 -0800176 return "<Option not supported>";
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700177
Alexis Saverye39d3492017-11-07 18:10:08 -0800178 } else {
Oleh Lamzinfb868682019-09-03 19:23:28 +0200179 const base::FilePath dir =
180 base::FilePath("/sys/block/" + bname.value() + "/device/");
Alexis Saverye39d3492017-11-07 18:10:08 -0800181 const base::FilePath typeFile = dir.Append("type");
182 const base::FilePath vendFile = dir.Append("vendor");
183 std::string message;
184
185 if (!IsSupported(typeFile, vendFile, &message)) {
186 return message;
187 }
188
189 process.AddArg(kSmartctl);
190
191 if (option == "abort_test")
192 process.AddArg("-X");
193 if (option == "attributes")
194 process.AddArg("-A");
195 if (option == "capabilities")
196 process.AddArg("-c");
197 if (option == "error")
198 process.AddStringOption("-l", "error");
199 if (option == "health")
200 process.AddArg("-H");
201 if (option == "selftest")
202 process.AddStringOption("-l", "selftest");
203 if (option == "short_test")
204 process.AddStringOption("-t", "short");
205 }
206
207 process.AddArg(device.value());
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700208 process.Run();
209 std::string output;
210 process.GetOutput(&output);
211 return output;
212}
213
Eric Caruso0b241882018-04-04 13:43:46 -0700214std::string StorageTool::Start(const base::ScopedFD& outfd) {
Oleh Lamzinfb868682019-09-03 19:23:28 +0200215 const base::FilePath device =
216 GetDevice(base::FilePath(kSource), base::FilePath(kMountFile));
Alexis Saverye39d3492017-11-07 18:10:08 -0800217
218 if (device.empty()) {
219 LOG(ERROR) << "Failed to find device for " << kSource;
220 return "<Failed to find device>";
221 }
222
Ben Chan0d840a72018-09-27 00:24:48 -0700223 ProcessWithId* p =
224 CreateProcess(false /* sandboxed */, false /* access_root_mount_ns */);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700225 if (!p)
226 return "";
227
228 p->AddArg(kBadblocks);
229 p->AddArg("-sv");
Alexis Saverye39d3492017-11-07 18:10:08 -0800230 p->AddArg(device.value());
Eric Caruso0b241882018-04-04 13:43:46 -0700231 p->BindFd(outfd.get(), STDOUT_FILENO);
232 p->BindFd(outfd.get(), STDERR_FILENO);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700233 LOG(INFO) << "badblocks: running process id: " << p->id();
234 p->Start();
235 return p->id();
236}
237
Ting Shen92dd7832018-12-21 17:45:07 +0800238std::string StorageTool::Mmc(const std::string& option) {
239 ProcessWithOutput process;
240 process.DisableSandbox();
241 if (!process.Init())
242 return "<process init failed>";
243
244 process.AddArg(kMmc);
245
246 if (option == "extcsd_read") {
247 process.AddArg("extcsd");
248 process.AddArg("read");
249 } else if (option == "extcsd_dump") {
250 process.AddArg("extcsd");
251 process.AddArg("dump");
252 } else {
253 return "<Option not supported>";
254 }
255
Oleh Lamzinfb868682019-09-03 19:23:28 +0200256 const base::FilePath rootdev =
257 GetDevice(base::FilePath(kSource), base::FilePath(kMountFile));
258 process.AddArg(rootdev.value());
259 process.Run();
260 std::string output;
261 process.GetOutput(&output);
262 return output;
263}
264
265std::string StorageTool::Nvme(const std::string& option) {
266 ProcessWithOutput process;
267 // Disabling sandboxing since nvme requires higher privileges.
268 process.DisableSandbox();
269 if (!process.Init())
270 return "<process init failed>";
271
272 process.AddArg(kNvme);
273
274 if (option == "identify_controller") {
275 process.AddArg("id-ctrl");
276 process.AddArg("--vendor-specific");
277 } else {
278 return "<Option not supported>";
279 }
280
281 const base::FilePath rootdev =
282 GetDevice(base::FilePath(kSource), base::FilePath(kMountFile));
Ting Shen92dd7832018-12-21 17:45:07 +0800283 process.AddArg(rootdev.value());
284 process.Run();
285 std::string output;
286 process.GetOutput(&output);
287 return output;
288}
289
Ben Chana0011d82014-05-13 00:19:29 -0700290} // namespace debugd