blob: c47adeedd6251de6b27ac804cb3ce50a6dda1ff0 [file] [log] [blame]
Tom Marshallaecfe0e2019-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 "PublicVolume.h"
20#include <volume_manager/ResponseCode.h>
21#include <volume_manager/VolumeManager.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>
Tom Marshallaecfe0e2019-01-04 14:37:31 -080029
30#include <sgdisk.h>
31
32#include <fcntl.h>
33#include <inttypes.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <sys/mount.h>
37#include <sys/stat.h>
38#include <sys/sysmacros.h>
39#include <sys/types.h>
40#include <vector>
41
42using android::base::ReadFileToString;
43using android::base::WriteStringToFile;
44using android::base::StringPrintf;
45
46namespace android {
47namespace volmgr {
48
49static const char* kSysfsLoopMaxMinors = "/sys/module/loop/parameters/max_part";
50static const char* kSysfsMmcMaxMinorsDeprecated = "/sys/module/mmcblk/parameters/perdev_minors";
51static const char* kSysfsMmcMaxMinors = "/sys/module/mmc_block/parameters/perdev_minors";
52
53static const unsigned int kMajorBlockLoop = 7;
54static const unsigned int kMajorBlockScsiA = 8;
55static const unsigned int kMajorBlockScsiB = 65;
56static const unsigned int kMajorBlockScsiC = 66;
57static const unsigned int kMajorBlockScsiD = 67;
58static const unsigned int kMajorBlockScsiE = 68;
59static const unsigned int kMajorBlockScsiF = 69;
60static const unsigned int kMajorBlockScsiG = 70;
61static const unsigned int kMajorBlockScsiH = 71;
62static const unsigned int kMajorBlockScsiI = 128;
63static const unsigned int kMajorBlockScsiJ = 129;
64static const unsigned int kMajorBlockScsiK = 130;
65static const unsigned int kMajorBlockScsiL = 131;
66static const unsigned int kMajorBlockScsiM = 132;
67static const unsigned int kMajorBlockScsiN = 133;
68static const unsigned int kMajorBlockScsiO = 134;
69static const unsigned int kMajorBlockScsiP = 135;
70static const unsigned int kMajorBlockMmc = 179;
71static const unsigned int kMajorBlockExperimentalMin = 240;
72static const unsigned int kMajorBlockExperimentalMax = 254;
Yumi Yukimura39247482024-08-10 03:43:17 +080073static const unsigned int kMajorBlockCdrom = 11;
Tom Marshallaecfe0e2019-01-04 14:37:31 -080074
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 }
Yumi Yukimura39247482024-08-10 03:43:17 +0800209 case kMajorBlockCdrom:
210 LOG(DEBUG) << "Found a CDROM: " << mSysPath;
211 FALLTHROUGH_INTENDED;
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800212 case kMajorBlockScsiA:
213 case kMajorBlockScsiB:
214 case kMajorBlockScsiC:
215 case kMajorBlockScsiD:
216 case kMajorBlockScsiE:
217 case kMajorBlockScsiF:
218 case kMajorBlockScsiG:
219 case kMajorBlockScsiH:
220 case kMajorBlockScsiI:
221 case kMajorBlockScsiJ:
222 case kMajorBlockScsiK:
223 case kMajorBlockScsiL:
224 case kMajorBlockScsiM:
225 case kMajorBlockScsiN:
226 case kMajorBlockScsiO:
227 case kMajorBlockScsiP: {
228 std::string path(mSysPath + "/device/vendor");
229 std::string tmp;
230 if (!ReadFileToString(path, &tmp)) {
231 PLOG(WARNING) << "Failed to read vendor from " << path;
232 return -errno;
233 }
234 mLabel = tmp;
235 break;
236 }
237 case kMajorBlockMmc: {
238 std::string path(mSysPath + "/device/manfid");
239 std::string tmp;
240 if (!ReadFileToString(path, &tmp)) {
241 PLOG(WARNING) << "Failed to read manufacturer from " << path;
242 return -errno;
243 }
244 uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
245 // Our goal here is to give the user a meaningful label, ideally
246 // matching whatever is silk-screened on the card. To reduce
247 // user confusion, this list doesn't contain white-label manfid.
248 switch (manfid) {
249 case 0x000003:
250 mLabel = "SanDisk";
251 break;
252 case 0x00001b:
253 mLabel = "Samsung";
254 break;
255 case 0x000028:
256 mLabel = "Lexar";
257 break;
258 case 0x000074:
259 mLabel = "Transcend";
260 break;
261 }
262 break;
263 }
264 default: {
265 if (isVirtioBlkDevice(majorId)) {
266 LOG(DEBUG) << "Recognized experimental block major ID " << majorId
267 << " as virtio-blk (emulator's virtual SD card device)";
268 mLabel = "Virtual";
269 break;
270 }
271 LOG(WARNING) << "Unsupported block major type " << majorId;
272 return -ENOTSUP;
273 }
274 }
275
276 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSizeChanged,
277 StringPrintf("%" PRIu64, mSize));
278 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
279 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
280 return OK;
281}
282
283status_t Disk::readPartitions() {
Yumi Yukimura39247482024-08-10 03:43:17 +0800284 sgdisk_partition_table ptbl;
285 std::vector<sgdisk_partition> partitions;
286 int res;
287 Table table = Table::kUnknown;
288 bool foundParts = false;
289
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800290 int8_t maxMinors = getMaxMinors();
291 if (maxMinors < 0) {
292 return -ENOTSUP;
293 }
294
295 if (mSkipChange) {
296 mSkipChange = false;
297 LOG(INFO) << "Skip first change";
298 return OK;
299 }
300
301 destroyAllVolumes();
302
Yumi Yukimura39247482024-08-10 03:43:17 +0800303 if (!maxMinors) {
304 std::string cdFsType, cdUnused;
305 if (ReadMetadataUntrusted(mDevPath, cdFsType, cdUnused, cdUnused) == OK) {
306 if (cdFsType == "iso9660" || cdFsType == "udf") {
307 LOG(INFO) << "Detect " << cdFsType;
308 goto treat_disk_as_partition;
309 }
310 }
311 }
312
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800313 // Parse partition table
Yumi Yukimura39247482024-08-10 03:43:17 +0800314 res = sgdisk_read(mDevPath.c_str(), ptbl, partitions);
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800315 if (res != 0) {
316 LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
Yumi Yukimura39247482024-08-10 03:43:17 +0800317
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800318 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
319 return res;
320 }
321
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800322 switch (ptbl.type) {
323 case MBR:
324 table = Table::kMbr;
325 break;
326 case GPT:
327 table = Table::kGpt;
328 break;
329 default:
330 table = Table::kUnknown;
331 }
332
333 foundParts = partitions.size() > 0;
334 for (const auto& part : partitions) {
335 if (part.num <= 0 || part.num > maxMinors) {
336 LOG(WARNING) << mId << " is ignoring partition " << part.num
337 << " beyond max supported devices";
338 continue;
339 }
340 dev_t partDevice = makedev(major(mDevice), minor(mDevice) + part.num);
341 if (table == Table::kMbr) {
342 switch (strtol(part.type.c_str(), nullptr, 16)) {
343 case 0x06: // FAT16
344 case 0x07: // NTFS/exFAT
345 case 0x0b: // W95 FAT32 (LBA)
346 case 0x0c: // W95 FAT32 (LBA)
347 case 0x0e: // W95 FAT16 (LBA)
348 case 0x83: // Linux EXT4/F2FS/...
349 createPublicVolume(partDevice);
350 break;
351 }
352 } else if (table == Table::kGpt) {
353 if (!strcasecmp(part.guid.c_str(), kGptBasicData) ||
354 !strcasecmp(part.guid.c_str(), kGptLinuxFilesystem)) {
355 createPublicVolume(partDevice);
356 }
357 }
358 }
359
Yumi Yukimura39247482024-08-10 03:43:17 +0800360treat_disk_as_partition:
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800361 // Ugly last ditch effort, treat entire disk as partition
362 if (table == Table::kUnknown || !foundParts) {
363 LOG(WARNING) << mId << " has unknown partition table; trying entire device";
364
365 std::string fsType;
366 std::string unused;
367 if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
368 createPublicVolume(mDevice);
369 } else {
370 LOG(WARNING) << mId << " failed to identify, giving up";
371 }
372 }
373
374 VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
375 return OK;
376}
377
378status_t Disk::unmountAll() {
379 for (const auto& vol : mVolumes) {
380 vol->unmount();
381 }
382 return OK;
383}
384
385int Disk::getMaxMinors() {
386 // Figure out maximum partition devices supported
387 unsigned int majorId = major(mDevice);
388 switch (majorId) {
389 case kMajorBlockLoop: {
390 std::string tmp;
391 if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) {
392 LOG(ERROR) << "Failed to read max minors";
393 return -errno;
394 }
395 return atoi(tmp.c_str());
396 }
397 case kMajorBlockScsiA:
398 case kMajorBlockScsiB:
399 case kMajorBlockScsiC:
400 case kMajorBlockScsiD:
401 case kMajorBlockScsiE:
402 case kMajorBlockScsiF:
403 case kMajorBlockScsiG:
404 case kMajorBlockScsiH:
405 case kMajorBlockScsiI:
406 case kMajorBlockScsiJ:
407 case kMajorBlockScsiK:
408 case kMajorBlockScsiL:
409 case kMajorBlockScsiM:
410 case kMajorBlockScsiN:
411 case kMajorBlockScsiO:
412 case kMajorBlockScsiP: {
413 // Per Documentation/devices.txt this is static
414 return 15;
415 }
Yumi Yukimura39247482024-08-10 03:43:17 +0800416 case kMajorBlockCdrom: {
417 return 0;
418 }
Tom Marshallaecfe0e2019-01-04 14:37:31 -0800419 case kMajorBlockMmc: {
420 // Per Documentation/devices.txt this is dynamic
421 std::string tmp;
422 if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp) &&
423 !ReadFileToString(kSysfsMmcMaxMinorsDeprecated, &tmp)) {
424 LOG(ERROR) << "Failed to read max minors";
425 return -errno;
426 }
427 return atoi(tmp.c_str());
428 }
429 default: {
430 if (isVirtioBlkDevice(majorId)) {
431 // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
432 // 2^4 - 1 = 15
433 return 15;
434 }
435 }
436 }
437
438 LOG(ERROR) << "Unsupported block major type " << majorId;
439 return -ENOTSUP;
440}
441
442} // namespace volmgr
443} // namespace android