blob: 3cac02d34e24bb3e24c49117f47def155eecddcb [file] [log] [blame]
Tom Marshallbead2612019-01-04 14:37:31 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Copyright (C) 2019 The LineageOS Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "Disk.h"
19#include <volume_manager/VolumeManager.h>
20#include "PublicVolume.h"
21#include "ResponseCode.h"
22#include "Utils.h"
23#include "VolumeBase.h"
24
25#include <android-base/file.h>
26#include <android-base/logging.h>
27#include <android-base/properties.h>
28#include <android-base/stringprintf.h>
29#include <diskconfig/diskconfig.h>
30
31#include <sgdisk.h>
32
33#include <fcntl.h>
34#include <inttypes.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <sys/mount.h>
38#include <sys/stat.h>
39#include <sys/sysmacros.h>
40#include <sys/types.h>
41#include <vector>
42
43using android::base::ReadFileToString;
44using android::base::WriteStringToFile;
45using android::base::StringPrintf;
46
47namespace android {
48namespace volmgr {
49
50static const char* kSysfsLoopMaxMinors = "/sys/module/loop/parameters/max_part";
51static const char* kSysfsMmcMaxMinorsDeprecated = "/sys/module/mmcblk/parameters/perdev_minors";
52static const char* kSysfsMmcMaxMinors = "/sys/module/mmc_block/parameters/perdev_minors";
53
54static const unsigned int kMajorBlockLoop = 7;
55static const unsigned int kMajorBlockScsiA = 8;
56static const unsigned int kMajorBlockScsiB = 65;
57static const unsigned int kMajorBlockScsiC = 66;
58static const unsigned int kMajorBlockScsiD = 67;
59static const unsigned int kMajorBlockScsiE = 68;
60static const unsigned int kMajorBlockScsiF = 69;
61static const unsigned int kMajorBlockScsiG = 70;
62static const unsigned int kMajorBlockScsiH = 71;
63static const unsigned int kMajorBlockScsiI = 128;
64static const unsigned int kMajorBlockScsiJ = 129;
65static const unsigned int kMajorBlockScsiK = 130;
66static const unsigned int kMajorBlockScsiL = 131;
67static const unsigned int kMajorBlockScsiM = 132;
68static const unsigned int kMajorBlockScsiN = 133;
69static const unsigned int kMajorBlockScsiO = 134;
70static const unsigned int kMajorBlockScsiP = 135;
71static const unsigned int kMajorBlockMmc = 179;
72static const unsigned int kMajorBlockExperimentalMin = 240;
73static const unsigned int kMajorBlockExperimentalMax = 254;
74
75static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
76static const char* kGptLinuxFilesystem = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
77
78enum class Table {
79 kUnknown,
80 kMbr,
81 kGpt,
82};
83
84static bool isVirtioBlkDevice(unsigned int major) {
85 /*
86 * The new emulator's "ranchu" virtual board no longer includes a goldfish
87 * MMC-based SD card device; instead, it emulates SD cards with virtio-blk,
88 * which has been supported by upstream kernel and QEMU for quite a while.
89 * Unfortunately, the virtio-blk block device driver does not use a fixed
90 * major number, but relies on the kernel to assign one from a specific
91 * range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE"
92 * per Documentation/devices.txt. This is true even for the latest Linux
93 * kernel (4.4; see init() in drivers/block/virtio_blk.c).
94 *
95 * This makes it difficult for vold to detect a virtio-blk based SD card.
96 * The current solution checks two conditions (both must be met):
97 *
98 * a) If the running environment is the emulator;
99 * b) If the major number is an experimental block device major number (for
100 * x86/x86_64 3.10 ranchu kernels, virtio-blk always gets major number
101 * 253, but it is safer to match the range than just one value).
102 *
103 * Other conditions could be used, too, e.g. the hardware name should be
104 * "ranchu", the device's sysfs path should end with "/block/vd[d-z]", etc.
105 * But just having a) and b) is enough for now.
106 */
107 return IsRunningInEmulator() && major >= kMajorBlockExperimentalMin &&
108 major <= kMajorBlockExperimentalMax;
109}
110
111Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags)
112 : mDevice(device),
113 mSize(-1),
114 mNickname(nickname),
115 mFlags(flags),
116 mCreated(false),
117 mSkipChange(false) {
118 mId = StringPrintf("disk:%u_%u", major(device), minor(device));
119 mEventPath = eventPath;
120 mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
121 mDevPath = StringPrintf("/dev/block/volmgr/%s", mId.c_str());
122 CreateDeviceNode(mDevPath, mDevice);
123}
124
125Disk::~Disk() {
126 CHECK(!mCreated);
127 DestroyDeviceNode(mDevPath);
128}
129
130void Disk::getVolumeInfo(std::vector<VolumeInfo>& info) {
131 for (auto vol : mVolumes) {
132 info.push_back(VolumeInfo(vol.get()));
133 }
134}
135
136std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
137 for (auto vol : mVolumes) {
138 if (vol->getId() == id) {
139 return vol;
140 }
141 }
142 return nullptr;
143}
144
145void Disk::listVolumes(VolumeBase::Type type, std::list<std::string>& list) {
146 for (const auto& vol : mVolumes) {
147 if (vol->getType() == type) {
148 list.push_back(vol->getId());
149 }
150 // TODO: consider looking at stacked volumes
151 }
152}
153
154status_t Disk::create() {
155 CHECK(!mCreated);
156 mCreated = true;
157 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
158 readMetadata();
159 readPartitions();
160 return OK;
161}
162
163status_t Disk::destroy() {
164 CHECK(mCreated);
165 destroyAllVolumes();
166 mCreated = false;
167 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskDestroyed);
168 return OK;
169}
170
171void Disk::createPublicVolume(dev_t device, const std::string& fstype /* = "" */,
172 const std::string& mntopts /* = "" */) {
173 auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device, mNickname, fstype, mntopts));
174
175 mVolumes.push_back(vol);
176 vol->setDiskId(getId());
177 vol->create();
178}
179
180void Disk::destroyAllVolumes() {
181 for (const auto& vol : mVolumes) {
182 vol->destroy();
183 }
184 mVolumes.clear();
185}
186
187status_t Disk::readMetadata() {
188 if (mSkipChange) {
189 return OK;
190 }
191
192 mSize = -1;
193 mLabel.clear();
194
195 int fd = open(mDevPath.c_str(), O_RDONLY | O_CLOEXEC);
196 if (fd != -1) {
197 if (ioctl(fd, BLKGETSIZE64, &mSize)) {
198 mSize = -1;
199 }
200 close(fd);
201 }
202
203 unsigned int majorId = major(mDevice);
204 switch (majorId) {
205 case kMajorBlockLoop: {
206 mLabel = "Virtual";
207 break;
208 }
209 case kMajorBlockScsiA:
210 case kMajorBlockScsiB:
211 case kMajorBlockScsiC:
212 case kMajorBlockScsiD:
213 case kMajorBlockScsiE:
214 case kMajorBlockScsiF:
215 case kMajorBlockScsiG:
216 case kMajorBlockScsiH:
217 case kMajorBlockScsiI:
218 case kMajorBlockScsiJ:
219 case kMajorBlockScsiK:
220 case kMajorBlockScsiL:
221 case kMajorBlockScsiM:
222 case kMajorBlockScsiN:
223 case kMajorBlockScsiO:
224 case kMajorBlockScsiP: {
225 std::string path(mSysPath + "/device/vendor");
226 std::string tmp;
227 if (!ReadFileToString(path, &tmp)) {
228 PLOG(WARNING) << "Failed to read vendor from " << path;
229 return -errno;
230 }
231 mLabel = tmp;
232 break;
233 }
234 case kMajorBlockMmc: {
235 std::string path(mSysPath + "/device/manfid");
236 std::string tmp;
237 if (!ReadFileToString(path, &tmp)) {
238 PLOG(WARNING) << "Failed to read manufacturer from " << path;
239 return -errno;
240 }
241 uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
242 // Our goal here is to give the user a meaningful label, ideally
243 // matching whatever is silk-screened on the card. To reduce
244 // user confusion, this list doesn't contain white-label manfid.
245 switch (manfid) {
246 case 0x000003:
247 mLabel = "SanDisk";
248 break;
249 case 0x00001b:
250 mLabel = "Samsung";
251 break;
252 case 0x000028:
253 mLabel = "Lexar";
254 break;
255 case 0x000074:
256 mLabel = "Transcend";
257 break;
258 }
259 break;
260 }
261 default: {
262 if (isVirtioBlkDevice(majorId)) {
263 LOG(DEBUG) << "Recognized experimental block major ID " << majorId
264 << " as virtio-blk (emulator's virtual SD card device)";
265 mLabel = "Virtual";
266 break;
267 }
268 LOG(WARNING) << "Unsupported block major type " << majorId;
269 return -ENOTSUP;
270 }
271 }
272
273 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSizeChanged,
274 StringPrintf("%" PRIu64, mSize));
275 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
276 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
277 return OK;
278}
279
280status_t Disk::readPartitions() {
281 int8_t maxMinors = getMaxMinors();
282 if (maxMinors < 0) {
283 return -ENOTSUP;
284 }
285
286 if (mSkipChange) {
287 mSkipChange = false;
288 LOG(INFO) << "Skip first change";
289 return OK;
290 }
291
292 destroyAllVolumes();
293
294 // Parse partition table
295 sgdisk_partition_table ptbl;
296 std::vector<sgdisk_partition> partitions;
297 int res = sgdisk_read(mDevPath.c_str(), ptbl, partitions);
298 if (res != 0) {
299 LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
300 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
301 return res;
302 }
303
304 Table table = Table::kUnknown;
305 bool foundParts = false;
306
307 switch (ptbl.type) {
308 case MBR:
309 table = Table::kMbr;
310 break;
311 case GPT:
312 table = Table::kGpt;
313 break;
314 default:
315 table = Table::kUnknown;
316 }
317
318 foundParts = partitions.size() > 0;
319 for (const auto& part : partitions) {
320 if (part.num <= 0 || part.num > maxMinors) {
321 LOG(WARNING) << mId << " is ignoring partition " << part.num
322 << " beyond max supported devices";
323 continue;
324 }
325 dev_t partDevice = makedev(major(mDevice), minor(mDevice) + part.num);
326 if (table == Table::kMbr) {
327 switch (strtol(part.type.c_str(), nullptr, 16)) {
328 case 0x06: // FAT16
329 case 0x07: // NTFS/exFAT
330 case 0x0b: // W95 FAT32 (LBA)
331 case 0x0c: // W95 FAT32 (LBA)
332 case 0x0e: // W95 FAT16 (LBA)
333 case 0x83: // Linux EXT4/F2FS/...
334 createPublicVolume(partDevice);
335 break;
336 }
337 } else if (table == Table::kGpt) {
338 if (!strcasecmp(part.guid.c_str(), kGptBasicData) ||
339 !strcasecmp(part.guid.c_str(), kGptLinuxFilesystem)) {
340 createPublicVolume(partDevice);
341 }
342 }
343 }
344
345 // Ugly last ditch effort, treat entire disk as partition
346 if (table == Table::kUnknown || !foundParts) {
347 LOG(WARNING) << mId << " has unknown partition table; trying entire device";
348
349 std::string fsType;
350 std::string unused;
351 if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
352 createPublicVolume(mDevice);
353 } else {
354 LOG(WARNING) << mId << " failed to identify, giving up";
355 }
356 }
357
358 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
359 return OK;
360}
361
362status_t Disk::unmountAll() {
363 for (const auto& vol : mVolumes) {
364 vol->unmount();
365 }
366 return OK;
367}
368
369int Disk::getMaxMinors() {
370 // Figure out maximum partition devices supported
371 unsigned int majorId = major(mDevice);
372 switch (majorId) {
373 case kMajorBlockLoop: {
374 std::string tmp;
375 if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) {
376 LOG(ERROR) << "Failed to read max minors";
377 return -errno;
378 }
379 return atoi(tmp.c_str());
380 }
381 case kMajorBlockScsiA:
382 case kMajorBlockScsiB:
383 case kMajorBlockScsiC:
384 case kMajorBlockScsiD:
385 case kMajorBlockScsiE:
386 case kMajorBlockScsiF:
387 case kMajorBlockScsiG:
388 case kMajorBlockScsiH:
389 case kMajorBlockScsiI:
390 case kMajorBlockScsiJ:
391 case kMajorBlockScsiK:
392 case kMajorBlockScsiL:
393 case kMajorBlockScsiM:
394 case kMajorBlockScsiN:
395 case kMajorBlockScsiO:
396 case kMajorBlockScsiP: {
397 // Per Documentation/devices.txt this is static
398 return 15;
399 }
400 case kMajorBlockMmc: {
401 // Per Documentation/devices.txt this is dynamic
402 std::string tmp;
403 if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp) &&
404 !ReadFileToString(kSysfsMmcMaxMinorsDeprecated, &tmp)) {
405 LOG(ERROR) << "Failed to read max minors";
406 return -errno;
407 }
408 return atoi(tmp.c_str());
409 }
410 default: {
411 if (isVirtioBlkDevice(majorId)) {
412 // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
413 // 2^4 - 1 = 15
414 return 15;
415 }
416 }
417 }
418
419 LOG(ERROR) << "Unsupported block major type " << majorId;
420 return -ENOTSUP;
421}
422
423} // namespace volmgr
424} // namespace android