blob: 96d8d7853f82bd13cd3c3b7f37347338b6fa5743 [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";
Alexis Saverye39d3492017-11-07 18:10:08 -080029const base::FilePath::CharType kMountFile[] =
30 FILE_PATH_LITERAL("/proc/mounts");
31const base::FilePath::CharType kSource[] =
32 FILE_PATH_LITERAL("/mnt/stateful_partition");
Ben Chanaf125862017-02-08 23:11:18 -080033
34} // namespace
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -070035
Alexis Saverye39d3492017-11-07 18:10:08 -080036const std::string StorageTool::GetPartition(const std::string& dst) {
37 std::string::const_reverse_iterator part = dst.rbegin();
38
39 if (!isdigit(*part))
40 return "";
41 part++;
42
43 while (part < dst.rend() && isdigit(*part))
44 part++;
45
46 return std::string(part.base(), dst.end());
47}
48
49void StorageTool::StripPartition(base::FilePath* dstPath) {
50 std::string dst = dstPath->value();
51 std::string part = StorageTool::GetPartition(dst);
52 if (part.empty())
53 return;
54
55 size_t location = dst.rfind(part);
56 size_t part_size = part.length();
57
58 // For devices matching dm-NN, the digits are not a partition.
59 if (dst.at(location - 1) == '-')
60 return;
61
62 // For devices that end with a digit, the kernel uses a 'p'
63 // as a separator. E.g., mmcblk1p2 and loop0p1, but not loop0.
64 if (dst.at(location - 1) == 'p') {
65 location--;
66 part_size++;
67
68 base::FilePath basename = dstPath->BaseName();
69 std::string bname = basename.value();
70 if (bname.compare(0, 4, "loop") == 0
71 && bname.compare(bname.size() - part.length(),
72 part.length(), part) == 0)
73 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) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800145 const base::FilePath device = GetDevice(base::FilePath(kSource),
146 base::FilePath(kMountFile));
147
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;
156 if (!SandboxedProcess::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");
174 if (option == "abort_test" || option == "health" ||
175 option == "selftest" || option == "short_test")
176 return "<Option not supported>";
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700177
Alexis Saverye39d3492017-11-07 18:10:08 -0800178 } else {
179 const base::FilePath dir = base::FilePath("/sys/block/" + bname.value()
180 + "/device/");
181 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) {
Alexis Saverye39d3492017-11-07 18:10:08 -0800215 const base::FilePath device = GetDevice(base::FilePath(kSource),
216 base::FilePath(kMountFile));
217
218 if (device.empty()) {
219 LOG(ERROR) << "Failed to find device for " << kSource;
220 return "<Failed to find device>";
221 }
222
Gediminas Ramanauskas2f0b8852013-03-14 13:52:32 -0700223 ProcessWithId* p = CreateProcess(false);
224 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
Ben Chana0011d82014-05-13 00:19:29 -0700237} // namespace debugd