blob: 7fc6dccba81106aa9fa7e63632d39438b7a03fc0 [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";
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090029const char kMountFile[] = "/proc/mounts";
30const char kSource[] = "/mnt/stateful_partition";
Ben Chanaf125862017-02-08 23:11:18 -080031
32} // namespace
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070033
Alexis Saverye39d3492017-11-07 18:10:08 -080034const std::string StorageTool::GetPartition(const std::string& dst) {
35 std::string::const_reverse_iterator part = dst.rbegin();
36
37 if (!isdigit(*part))
38 return "";
39 part++;
40
41 while (part < dst.rend() && isdigit(*part))
42 part++;
43
44 return std::string(part.base(), dst.end());
45}
46
47void StorageTool::StripPartition(base::FilePath* dstPath) {
48 std::string dst = dstPath->value();
49 std::string part = StorageTool::GetPartition(dst);
50 if (part.empty())
51 return;
52
53 size_t location = dst.rfind(part);
54 size_t part_size = part.length();
55
56 // For devices matching dm-NN, the digits are not a partition.
57 if (dst.at(location - 1) == '-')
58 return;
59
60 // For devices that end with a digit, the kernel uses a 'p'
61 // as a separator. E.g., mmcblk1p2 and loop0p1, but not loop0.
62 if (dst.at(location - 1) == 'p') {
63 location--;
64 part_size++;
65
66 base::FilePath basename = dstPath->BaseName();
67 std::string bname = basename.value();
68 if (bname.compare(0, 4, "loop") == 0
69 && bname.compare(bname.size() - part.length(),
70 part.length(), part) == 0)
71 return;
72 }
73 dst.erase(location, part_size);
74 *dstPath = base::FilePath(dst);
75}
76
77const base::FilePath StorageTool::GetDevice(const base::FilePath& filesystemIn,
78 const base::FilePath& mountsFile) {
79 base::FilePath device;
80 base::ScopedFILE mountinfo(fopen(mountsFile.value().c_str(), "re"));
81 if (!mountinfo) {
82 PLOG(ERROR) << "Failed to open " << mountsFile.value();
83 return base::FilePath();
84 }
85
86 struct mntent mount_entry;
87 char buffer[PATH_MAX];
88
89 while (getmntent_r(mountinfo.get(), &mount_entry, buffer, sizeof(buffer))) {
90 const std::string mountpoint = mount_entry.mnt_dir;
91 if (mountpoint == filesystemIn.value()) {
92 device = base::FilePath(mount_entry.mnt_fsname);
93 break;
94 }
95 }
96
97 StorageTool::StripPartition(&device);
98 return device;
99}
100
101// This function is called by Smartctl to check for ATA devices.
102// Smartctl is only supported on ATA devices, so this function
103// will return false when other devices are used.
104bool StorageTool::IsSupported(const base::FilePath typeFile,
105 const base::FilePath vendFile,
106 std::string* errorMsg) {
107 base::FilePath r;
108 bool link = base::NormalizeFilePath(typeFile, &r);
109 if (!link) {
110 PLOG(ERROR) << "Failed to read device type link";
111 errorMsg->assign("<Failed to read device type link>");
112 return false;
113 }
114
115 size_t target = r.value().find("target");
116 if (target == -1) {
117 errorMsg->assign("<This feature is not supported>");
118 return false;
119 }
120
121 std::string vend;
122
123 if (!base::ReadFileToString(vendFile, &vend)) {
124 PLOG(ERROR) << "Failed to open " << vendFile.value();
125 errorMsg->assign("<Failed to open vendor file>");
126 return false;
127 }
128
129 if (vend.empty()) {
130 errorMsg->assign("<Failed to find device type>");
131 return false;
132 }
133
134 if (vend.compare(0, 3, "ATA") != 0) {
135 errorMsg->assign("<This feature is not supported>");
136 return false;
137 }
138
139 return true;
140}
141
Eric Carusoc93a15c2017-04-24 16:15:12 -0700142std::string StorageTool::Smartctl(const std::string& option) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800143 const base::FilePath device = GetDevice(base::FilePath(kSource),
144 base::FilePath(kMountFile));
145
146 if (device.empty()) {
147 LOG(ERROR) << "Failed to find device for " << kSource;
148 return "<Failed to find device>";
149 }
150
151 base::FilePath bname = device.BaseName();
152
Ben Chan297c3c22013-07-17 17:34:12 -0700153 std::string path;
154 if (!SandboxedProcess::GetHelperPath("storage", &path))
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700155 return "<path too long>";
156
157 ProcessWithOutput process;
158 // Disabling sandboxing since smartctl requires higher privileges.
159 process.DisableSandbox();
160 if (!process.Init())
161 return "<process init failed>";
162
Alexis Saverye39d3492017-11-07 18:10:08 -0800163 if (bname.value().compare(0, 4, "nvme") == 0) {
164 process.AddArg(kSmartctl);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700165
Alexis Saverye39d3492017-11-07 18:10:08 -0800166 if (option == "attributes")
167 process.AddArg("-A");
168 if (option == "capabilities")
169 process.AddArg("-c");
170 if (option == "error")
171 process.AddStringOption("-l", "error");
172 if (option == "abort_test" || option == "health" ||
173 option == "selftest" || option == "short_test")
174 return "<Option not supported>";
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700175
Alexis Saverye39d3492017-11-07 18:10:08 -0800176 } else {
177 const base::FilePath dir = base::FilePath("/sys/block/" + bname.value()
178 + "/device/");
179 const base::FilePath typeFile = dir.Append("type");
180 const base::FilePath vendFile = dir.Append("vendor");
181 std::string message;
182
183 if (!IsSupported(typeFile, vendFile, &message)) {
184 return message;
185 }
186
187 process.AddArg(kSmartctl);
188
189 if (option == "abort_test")
190 process.AddArg("-X");
191 if (option == "attributes")
192 process.AddArg("-A");
193 if (option == "capabilities")
194 process.AddArg("-c");
195 if (option == "error")
196 process.AddStringOption("-l", "error");
197 if (option == "health")
198 process.AddArg("-H");
199 if (option == "selftest")
200 process.AddStringOption("-l", "selftest");
201 if (option == "short_test")
202 process.AddStringOption("-t", "short");
203 }
204
205 process.AddArg(device.value());
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700206 process.Run();
207 std::string output;
208 process.GetOutput(&output);
209 return output;
210}
211
Eric Caruso0b241882018-04-04 13:43:46 -0700212std::string StorageTool::Start(const base::ScopedFD& outfd) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800213 const base::FilePath device = GetDevice(base::FilePath(kSource),
214 base::FilePath(kMountFile));
215
216 if (device.empty()) {
217 LOG(ERROR) << "Failed to find device for " << kSource;
218 return "<Failed to find device>";
219 }
220
Ben Chan0d840a72018-09-27 00:24:48 -0700221 ProcessWithId* p =
222 CreateProcess(false /* sandboxed */, false /* access_root_mount_ns */);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700223 if (!p)
224 return "";
225
226 p->AddArg(kBadblocks);
227 p->AddArg("-sv");
Alexis Saverye39d3492017-11-07 18:10:08 -0800228 p->AddArg(device.value());
Eric Caruso0b241882018-04-04 13:43:46 -0700229 p->BindFd(outfd.get(), STDOUT_FILENO);
230 p->BindFd(outfd.get(), STDERR_FILENO);
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700231 LOG(INFO) << "badblocks: running process id: " << p->id();
232 p->Start();
233 return p->id();
234}
235
Ben Chana0011d82014-05-13 00:19:29 -0700236} // namespace debugd