blob: 61bb5a277ac9e0db8696c7737feb1f1b4d7fed2d [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
Alex Vakulenko262be3f2014-07-30 15:25:50 -070020#include "debugd/src/process_with_id.h"
21#include "debugd/src/process_with_output.h"
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070022
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070023namespace debugd {
24
Ben Chanaf125862017-02-08 23:11:18 -080025namespace {
26
27const char kSmartctl[] = "/usr/sbin/smartctl";
28const char kBadblocks[] = "/sbin/badblocks";
Kartik Hegde0227a062018-10-03 16:48:04 -060029const char kMountFile[] = "/proc/1/mounts";
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090030const char kSource[] = "/mnt/stateful_partition";
Ting Shen92dd7832018-12-21 17:45:07 +080031const char kMmc[] = "/usr/bin/mmc";
Ben Chanaf125862017-02-08 23:11:18 -080032
33} // namespace
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070034
Alexis Saverye39d3492017-11-07 18:10:08 -080035const std::string StorageTool::GetPartition(const std::string& dst) {
36 std::string::const_reverse_iterator part = dst.rbegin();
37
38 if (!isdigit(*part))
39 return "";
40 part++;
41
42 while (part < dst.rend() && isdigit(*part))
43 part++;
44
45 return std::string(part.base(), dst.end());
46}
47
48void StorageTool::StripPartition(base::FilePath* dstPath) {
49 std::string dst = dstPath->value();
50 std::string part = StorageTool::GetPartition(dst);
51 if (part.empty())
52 return;
53
54 size_t location = dst.rfind(part);
55 size_t part_size = part.length();
56
57 // For devices matching dm-NN, the digits are not a partition.
58 if (dst.at(location - 1) == '-')
59 return;
60
61 // For devices that end with a digit, the kernel uses a 'p'
62 // as a separator. E.g., mmcblk1p2 and loop0p1, but not loop0.
63 if (dst.at(location - 1) == 'p') {
64 location--;
65 part_size++;
66
67 base::FilePath basename = dstPath->BaseName();
68 std::string bname = basename.value();
69 if (bname.compare(0, 4, "loop") == 0
70 && bname.compare(bname.size() - part.length(),
71 part.length(), part) == 0)
72 return;
73 }
74 dst.erase(location, part_size);
75 *dstPath = base::FilePath(dst);
76}
77
78const base::FilePath StorageTool::GetDevice(const base::FilePath& filesystemIn,
79 const base::FilePath& mountsFile) {
80 base::FilePath device;
81 base::ScopedFILE mountinfo(fopen(mountsFile.value().c_str(), "re"));
82 if (!mountinfo) {
83 PLOG(ERROR) << "Failed to open " << mountsFile.value();
84 return base::FilePath();
85 }
86
87 struct mntent mount_entry;
88 char buffer[PATH_MAX];
89
90 while (getmntent_r(mountinfo.get(), &mount_entry, buffer, sizeof(buffer))) {
91 const std::string mountpoint = mount_entry.mnt_dir;
92 if (mountpoint == filesystemIn.value()) {
93 device = base::FilePath(mount_entry.mnt_fsname);
94 break;
95 }
96 }
97
98 StorageTool::StripPartition(&device);
99 return device;
100}
101
102// This function is called by Smartctl to check for ATA devices.
103// Smartctl is only supported on ATA devices, so this function
104// will return false when other devices are used.
105bool StorageTool::IsSupported(const base::FilePath typeFile,
106 const base::FilePath vendFile,
107 std::string* errorMsg) {
108 base::FilePath r;
109 bool link = base::NormalizeFilePath(typeFile, &r);
110 if (!link) {
111 PLOG(ERROR) << "Failed to read device type link";
112 errorMsg->assign("<Failed to read device type link>");
113 return false;
114 }
115
116 size_t target = r.value().find("target");
117 if (target == -1) {
118 errorMsg->assign("<This feature is not supported>");
119 return false;
120 }
121
122 std::string vend;
123
124 if (!base::ReadFileToString(vendFile, &vend)) {
125 PLOG(ERROR) << "Failed to open " << vendFile.value();
126 errorMsg->assign("<Failed to open vendor file>");
127 return false;
128 }
129
130 if (vend.empty()) {
131 errorMsg->assign("<Failed to find device type>");
132 return false;
133 }
134
135 if (vend.compare(0, 3, "ATA") != 0) {
136 errorMsg->assign("<This feature is not supported>");
137 return false;
138 }
139
140 return true;
141}
142
Eric Carusoc93a15c2017-04-24 16:15:12 -0700143std::string StorageTool::Smartctl(const std::string& option) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800144 const base::FilePath device = GetDevice(base::FilePath(kSource),
145 base::FilePath(kMountFile));
146
147 if (device.empty()) {
148 LOG(ERROR) << "Failed to find device for " << kSource;
149 return "<Failed to find device>";
150 }
151
152 base::FilePath bname = device.BaseName();
153
Ben Chan297c3c22013-07-17 17:34:12 -0700154 std::string path;
155 if (!SandboxedProcess::GetHelperPath("storage", &path))
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700156 return "<path too long>";
157
158 ProcessWithOutput process;
159 // Disabling sandboxing since smartctl requires higher privileges.
160 process.DisableSandbox();
161 if (!process.Init())
162 return "<process init failed>";
163
Alexis Saverye39d3492017-11-07 18:10:08 -0800164 if (bname.value().compare(0, 4, "nvme") == 0) {
165 process.AddArg(kSmartctl);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700166
Alexis Saverye39d3492017-11-07 18:10:08 -0800167 if (option == "attributes")
168 process.AddArg("-A");
169 if (option == "capabilities")
170 process.AddArg("-c");
171 if (option == "error")
172 process.AddStringOption("-l", "error");
173 if (option == "abort_test" || option == "health" ||
174 option == "selftest" || option == "short_test")
175 return "<Option not supported>";
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700176
Alexis Saverye39d3492017-11-07 18:10:08 -0800177 } else {
178 const base::FilePath dir = base::FilePath("/sys/block/" + bname.value()
179 + "/device/");
180 const base::FilePath typeFile = dir.Append("type");
181 const base::FilePath vendFile = dir.Append("vendor");
182 std::string message;
183
184 if (!IsSupported(typeFile, vendFile, &message)) {
185 return message;
186 }
187
188 process.AddArg(kSmartctl);
189
190 if (option == "abort_test")
191 process.AddArg("-X");
192 if (option == "attributes")
193 process.AddArg("-A");
194 if (option == "capabilities")
195 process.AddArg("-c");
196 if (option == "error")
197 process.AddStringOption("-l", "error");
198 if (option == "health")
199 process.AddArg("-H");
200 if (option == "selftest")
201 process.AddStringOption("-l", "selftest");
202 if (option == "short_test")
203 process.AddStringOption("-t", "short");
204 }
205
206 process.AddArg(device.value());
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700207 process.Run();
208 std::string output;
209 process.GetOutput(&output);
210 return output;
211}
212
Eric Caruso0b241882018-04-04 13:43:46 -0700213std::string StorageTool::Start(const base::ScopedFD& outfd) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800214 const base::FilePath device = GetDevice(base::FilePath(kSource),
215 base::FilePath(kMountFile));
216
217 if (device.empty()) {
218 LOG(ERROR) << "Failed to find device for " << kSource;
219 return "<Failed to find device>";
220 }
221
Ben Chan0d840a72018-09-27 00:24:48 -0700222 ProcessWithId* p =
223 CreateProcess(false /* sandboxed */, false /* access_root_mount_ns */);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700224 if (!p)
225 return "";
226
227 p->AddArg(kBadblocks);
228 p->AddArg("-sv");
Alexis Saverye39d3492017-11-07 18:10:08 -0800229 p->AddArg(device.value());
Eric Caruso0b241882018-04-04 13:43:46 -0700230 p->BindFd(outfd.get(), STDOUT_FILENO);
231 p->BindFd(outfd.get(), STDERR_FILENO);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700232 LOG(INFO) << "badblocks: running process id: " << p->id();
233 p->Start();
234 return p->id();
235}
236
Ting Shen92dd7832018-12-21 17:45:07 +0800237std::string StorageTool::Mmc(const std::string& option) {
238 ProcessWithOutput process;
239 process.DisableSandbox();
240 if (!process.Init())
241 return "<process init failed>";
242
243 process.AddArg(kMmc);
244
245 if (option == "extcsd_read") {
246 process.AddArg("extcsd");
247 process.AddArg("read");
248 } else if (option == "extcsd_dump") {
249 process.AddArg("extcsd");
250 process.AddArg("dump");
251 } else {
252 return "<Option not supported>";
253 }
254
255 const base::FilePath rootdev = GetDevice(base::FilePath(kSource),
256 base::FilePath(kMountFile));
257 process.AddArg(rootdev.value());
258 process.Run();
259 std::string output;
260 process.GetOutput(&output);
261 return output;
262}
263
Ben Chana0011d82014-05-13 00:19:29 -0700264} // namespace debugd