1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Disk.h"
18 #include "FsCrypt.h"
19 #include "PrivateVolume.h"
20 #include "PublicVolume.h"
21 #include "Utils.h"
22 #include "VolumeBase.h"
23 #include "VolumeEncryption.h"
24 #include "VolumeManager.h"
25 
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/parseint.h>
29 #include <android-base/properties.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 #include <fscrypt/fscrypt.h>
33 
34 #include <fcntl.h>
35 #include <inttypes.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/mount.h>
39 #include <sys/stat.h>
40 #include <sys/sysmacros.h>
41 #include <sys/types.h>
42 #include <vector>
43 
44 using android::base::ReadFileToString;
45 using android::base::StringPrintf;
46 using android::base::WriteStringToFile;
47 
48 namespace android {
49 namespace vold {
50 
51 static const char* kSgdiskPath = "/system/bin/sgdisk";
52 static const char* kSgdiskToken = " \t\n";
53 
54 static const char* kSysfsLoopMaxMinors = "/sys/module/loop/parameters/max_part";
55 static const char* kSysfsMmcMaxMinorsDeprecated = "/sys/module/mmcblk/parameters/perdev_minors";
56 static const char* kSysfsMmcMaxMinors = "/sys/module/mmc_block/parameters/perdev_minors";
57 
58 static const unsigned int kMajorBlockLoop = 7;
59 static const unsigned int kMajorBlockScsiA = 8;
60 static const unsigned int kMajorBlockScsiB = 65;
61 static const unsigned int kMajorBlockScsiC = 66;
62 static const unsigned int kMajorBlockScsiD = 67;
63 static const unsigned int kMajorBlockScsiE = 68;
64 static const unsigned int kMajorBlockScsiF = 69;
65 static const unsigned int kMajorBlockScsiG = 70;
66 static const unsigned int kMajorBlockScsiH = 71;
67 static const unsigned int kMajorBlockScsiI = 128;
68 static const unsigned int kMajorBlockScsiJ = 129;
69 static const unsigned int kMajorBlockScsiK = 130;
70 static const unsigned int kMajorBlockScsiL = 131;
71 static const unsigned int kMajorBlockScsiM = 132;
72 static const unsigned int kMajorBlockScsiN = 133;
73 static const unsigned int kMajorBlockScsiO = 134;
74 static const unsigned int kMajorBlockScsiP = 135;
75 static const unsigned int kMajorBlockMmc = 179;
76 static const unsigned int kMajorBlockDynamicMin = 234;
77 static const unsigned int kMajorBlockDynamicMax = 512;
78 
79 static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
80 static const char* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
81 static const char* kGptAndroidExpand = "193D1EA4-B3CA-11E4-B075-10604B889DCF";
82 
83 enum class Table {
84     kUnknown,
85     kMbr,
86     kGpt,
87 };
88 
isNvmeBlkDevice(unsigned int major,const std::string & sysPath)89 static bool isNvmeBlkDevice(unsigned int major, const std::string& sysPath) {
90     return sysPath.find("nvme") != std::string::npos && major >= kMajorBlockDynamicMin &&
91            major <= kMajorBlockDynamicMax;
92 }
93 
Disk(const std::string & eventPath,dev_t device,const std::string & nickname,int flags)94 Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags)
95     : mDevice(device),
96       mSize(-1),
97       mNickname(nickname),
98       mFlags(flags),
99       mCreated(false),
100       mJustPartitioned(false) {
101     mId = StringPrintf("disk:%u,%u", major(device), minor(device));
102     mEventPath = eventPath;
103     mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
104     mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
105     CreateDeviceNode(mDevPath, mDevice);
106 }
107 
~Disk()108 Disk::~Disk() {
109     CHECK(!mCreated);
110     DestroyDeviceNode(mDevPath);
111 }
112 
findVolume(const std::string & id)113 std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
114     for (auto vol : mVolumes) {
115         if (vol->getId() == id) {
116             return vol;
117         }
118         auto stackedVol = vol->findVolume(id);
119         if (stackedVol != nullptr) {
120             return stackedVol;
121         }
122     }
123     return nullptr;
124 }
125 
listVolumes(VolumeBase::Type type,std::list<std::string> & list) const126 void Disk::listVolumes(VolumeBase::Type type, std::list<std::string>& list) const {
127     for (const auto& vol : mVolumes) {
128         if (vol->getType() == type) {
129             list.push_back(vol->getId());
130         }
131         // TODO: consider looking at stacked volumes
132     }
133 }
134 
create()135 status_t Disk::create() {
136     CHECK(!mCreated);
137     mCreated = true;
138 
139     auto listener = VolumeManager::Instance()->getListener();
140     if (listener) listener->onDiskCreated(getId(), mFlags);
141 
142     readMetadata();
143     readPartitions();
144     return OK;
145 }
146 
destroy()147 status_t Disk::destroy() {
148     CHECK(mCreated);
149     destroyAllVolumes();
150     mCreated = false;
151 
152     auto listener = VolumeManager::Instance()->getListener();
153     if (listener) listener->onDiskDestroyed(getId());
154 
155     return OK;
156 }
157 
createPublicVolume(dev_t device)158 void Disk::createPublicVolume(dev_t device) {
159     auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
160     if (mJustPartitioned) {
161         LOG(DEBUG) << "Device just partitioned; silently formatting";
162         vol->setSilent(true);
163         vol->create();
164         vol->format("auto");
165         vol->destroy();
166         vol->setSilent(false);
167     }
168 
169     mVolumes.push_back(vol);
170     vol->setDiskId(getId());
171     vol->create();
172 }
173 
createPrivateVolume(dev_t device,const std::string & partGuid)174 void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {
175     std::string normalizedGuid;
176     if (NormalizeHex(partGuid, normalizedGuid)) {
177         LOG(WARNING) << "Invalid GUID " << partGuid;
178         return;
179     }
180 
181     std::string keyRaw;
182     if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {
183         PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;
184         return;
185     }
186 
187     LOG(DEBUG) << "Found key for GUID " << normalizedGuid;
188 
189     auto keyBuffer = KeyBuffer(keyRaw.begin(), keyRaw.end());
190     auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyBuffer));
191     if (mJustPartitioned) {
192         LOG(DEBUG) << "Device just partitioned; silently formatting";
193         vol->setSilent(true);
194         vol->create();
195         vol->format("auto");
196         vol->destroy();
197         vol->setSilent(false);
198     }
199 
200     mVolumes.push_back(vol);
201     vol->setDiskId(getId());
202     vol->setPartGuid(partGuid);
203     vol->create();
204 }
205 
destroyAllVolumes()206 void Disk::destroyAllVolumes() {
207     for (const auto& vol : mVolumes) {
208         vol->destroy();
209     }
210     mVolumes.clear();
211 }
212 
readMetadata()213 status_t Disk::readMetadata() {
214     mSize = -1;
215     mLabel.clear();
216 
217     if (GetBlockDevSize(mDevPath, &mSize) != OK) {
218         mSize = -1;
219     }
220 
221     unsigned int majorId = major(mDevice);
222     switch (majorId) {
223         case kMajorBlockLoop: {
224             mLabel = "Virtual";
225             break;
226         }
227         // clang-format off
228         case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC:
229         case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF:
230         case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI:
231         case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
232         case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO:
233         case kMajorBlockScsiP: {
234             // clang-format on
235             std::string path(mSysPath + "/device/vendor");
236             std::string tmp;
237             if (!ReadFileToString(path, &tmp)) {
238                 PLOG(WARNING) << "Failed to read vendor from " << path;
239                 return -errno;
240             }
241             tmp = android::base::Trim(tmp);
242             mLabel = tmp;
243             break;
244         }
245         case kMajorBlockMmc: {
246             std::string path(mSysPath + "/device/manfid");
247             std::string tmp;
248             if (!ReadFileToString(path, &tmp)) {
249                 PLOG(WARNING) << "Failed to read manufacturer from " << path;
250                 return -errno;
251             }
252             tmp = android::base::Trim(tmp);
253             int64_t manfid;
254             if (!android::base::ParseInt(tmp, &manfid)) {
255                 PLOG(WARNING) << "Failed to parse manufacturer " << tmp;
256                 return -EINVAL;
257             }
258             // Our goal here is to give the user a meaningful label, ideally
259             // matching whatever is silk-screened on the card.  To reduce
260             // user confusion, this list doesn't contain white-label manfid.
261             switch (manfid) {
262                 // clang-format off
263                 case 0x000003: mLabel = "SanDisk"; break;
264                 case 0x00001b: mLabel = "Samsung"; break;
265                 case 0x000028: mLabel = "Lexar"; break;
266                 case 0x000074: mLabel = "Transcend"; break;
267                     // clang-format on
268             }
269             break;
270         }
271         default: {
272             if (IsVirtioBlkDevice(majorId)) {
273                 LOG(DEBUG) << "Recognized experimental block major ID " << majorId
274                            << " as virtio-blk (emulator's virtual SD card device)";
275                 mLabel = "Virtual";
276                 break;
277             }
278             if (isNvmeBlkDevice(majorId, mSysPath)) {
279                 std::string path(mSysPath + "/device/model");
280                 std::string tmp;
281                 if (!ReadFileToString(path, &tmp)) {
282                     PLOG(WARNING) << "Failed to read vendor from " << path;
283                     return -errno;
284                 }
285                 mLabel = tmp;
286                 break;
287             }
288             LOG(WARNING) << "Unsupported block major type " << majorId;
289             return -ENOTSUP;
290         }
291     }
292 
293     auto listener = VolumeManager::Instance()->getListener();
294     if (listener) listener->onDiskMetadataChanged(getId(), mSize, mLabel, mSysPath);
295 
296     return OK;
297 }
298 
readPartitions()299 status_t Disk::readPartitions() {
300     int maxMinors = getMaxMinors();
301     if (maxMinors < 0) {
302         return -ENOTSUP;
303     }
304 
305     destroyAllVolumes();
306 
307     // Parse partition table
308 
309     std::vector<std::string> cmd;
310     cmd.push_back(kSgdiskPath);
311     cmd.push_back("--android-dump");
312     cmd.push_back(mDevPath);
313 
314     std::vector<std::string> output;
315     status_t res = ForkExecvp(cmd, &output);
316     if (res != OK) {
317         LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
318 
319         auto listener = VolumeManager::Instance()->getListener();
320         if (listener) listener->onDiskScanned(getId());
321 
322         mJustPartitioned = false;
323         return res;
324     }
325 
326     Table table = Table::kUnknown;
327     bool foundParts = false;
328     for (const auto& line : output) {
329         auto split = android::base::Split(line, kSgdiskToken);
330         auto it = split.begin();
331         if (it == split.end()) continue;
332 
333         if (*it == "DISK") {
334             if (++it == split.end()) continue;
335             if (*it == "mbr") {
336                 table = Table::kMbr;
337             } else if (*it == "gpt") {
338                 table = Table::kGpt;
339             } else {
340                 LOG(WARNING) << "Invalid partition table " << *it;
341                 continue;
342             }
343         } else if (*it == "PART") {
344             foundParts = true;
345 
346             if (++it == split.end()) continue;
347             int i = 0;
348             if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
349                 LOG(WARNING) << "Invalid partition number " << *it;
350                 continue;
351             }
352             dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
353 
354             if (table == Table::kMbr) {
355                 if (++it == split.end()) continue;
356                 int type = 0;
357                 if (!android::base::ParseInt("0x" + *it, &type)) {
358                     LOG(WARNING) << "Invalid partition type " << *it;
359                     continue;
360                 }
361 
362                 switch (type) {
363                     case 0x06:  // FAT16
364                     case 0x07:  // HPFS/NTFS/exFAT
365                     case 0x0b:  // W95 FAT32 (LBA)
366                     case 0x0c:  // W95 FAT32 (LBA)
367                     case 0x0e:  // W95 FAT16 (LBA)
368                         createPublicVolume(partDevice);
369                         break;
370                 }
371             } else if (table == Table::kGpt) {
372                 if (++it == split.end()) continue;
373                 auto typeGuid = *it;
374                 if (++it == split.end()) continue;
375                 auto partGuid = *it;
376 
377                 if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
378                     createPublicVolume(partDevice);
379                 } else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
380                     createPrivateVolume(partDevice, partGuid);
381                 }
382             }
383         }
384     }
385 
386     // Ugly last ditch effort, treat entire disk as partition
387     if (table == Table::kUnknown || !foundParts) {
388         LOG(WARNING) << mId << " has unknown partition table; trying entire device";
389 
390         std::string fsType;
391         std::string unused;
392         if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK) {
393             createPublicVolume(mDevice);
394         } else {
395             LOG(WARNING) << mId << " failed to identify, giving up";
396         }
397     }
398 
399     auto listener = VolumeManager::Instance()->getListener();
400     if (listener) listener->onDiskScanned(getId());
401 
402     mJustPartitioned = false;
403     return OK;
404 }
405 
unmountAll()406 status_t Disk::unmountAll() {
407     for (const auto& vol : mVolumes) {
408         vol->unmount();
409     }
410     return OK;
411 }
412 
partitionPublic()413 status_t Disk::partitionPublic() {
414     int res;
415 
416     destroyAllVolumes();
417     mJustPartitioned = true;
418 
419     // First nuke any existing partition table
420     std::vector<std::string> cmd;
421     cmd.push_back(kSgdiskPath);
422     cmd.push_back("--zap-all");
423     cmd.push_back(mDevPath);
424 
425     // Zap sometimes returns an error when it actually succeeded, so
426     // just log as warning and keep rolling forward.
427     if ((res = ForkExecvp(cmd)) != 0) {
428         LOG(WARNING) << "Failed to zap; status " << res;
429     }
430 
431     // Now let's build the new MBR table. We heavily rely on sgdisk to
432     // force optimal alignment on the created partitions.
433     cmd.clear();
434     cmd.push_back(kSgdiskPath);
435     cmd.push_back("--new=0:0:-0");
436     cmd.push_back("--typecode=0:0c00");
437     cmd.push_back("--gpttombr=1");
438     cmd.push_back(mDevPath);
439 
440     if ((res = ForkExecvp(cmd)) != 0) {
441         LOG(ERROR) << "Failed to partition; status " << res;
442         return res;
443     }
444 
445     return OK;
446 }
447 
partitionPrivate()448 status_t Disk::partitionPrivate() {
449     return partitionMixed(0);
450 }
451 
partitionMixed(int8_t ratio)452 status_t Disk::partitionMixed(int8_t ratio) {
453     int res;
454 
455     destroyAllVolumes();
456     mJustPartitioned = true;
457 
458     // First nuke any existing partition table
459     std::vector<std::string> cmd;
460     cmd.push_back(kSgdiskPath);
461     cmd.push_back("--zap-all");
462     cmd.push_back(mDevPath);
463 
464     // Zap sometimes returns an error when it actually succeeded, so
465     // just log as warning and keep rolling forward.
466     if ((res = ForkExecvp(cmd)) != 0) {
467         LOG(WARNING) << "Failed to zap; status " << res;
468     }
469 
470     // We've had some success above, so generate both the private partition
471     // GUID and encryption key and persist them.
472     std::string partGuidRaw;
473     if (GenerateRandomUuid(partGuidRaw) != OK) {
474         LOG(ERROR) << "Failed to generate GUID";
475         return -EIO;
476     }
477 
478     KeyBuffer key;
479     if (!generate_volume_key(&key)) {
480         LOG(ERROR) << "Failed to generate key";
481         return -EIO;
482     }
483     std::string keyRaw(key.begin(), key.end());
484 
485     std::string partGuid;
486     StrToHex(partGuidRaw, partGuid);
487 
488     if (!WriteStringToFile(keyRaw, BuildKeyPath(partGuid))) {
489         LOG(ERROR) << "Failed to persist key";
490         return -EIO;
491     } else {
492         LOG(DEBUG) << "Persisted key for GUID " << partGuid;
493     }
494 
495     // Now let's build the new GPT table. We heavily rely on sgdisk to
496     // force optimal alignment on the created partitions.
497     cmd.clear();
498     cmd.push_back(kSgdiskPath);
499 
500     // If requested, create a public partition first. Mixed-mode partitioning
501     // like this is an experimental feature.
502     if (ratio > 0) {
503         if (ratio < 10 || ratio > 90) {
504             LOG(ERROR) << "Mixed partition ratio must be between 10-90%";
505             return -EINVAL;
506         }
507 
508         uint64_t splitMb = ((mSize / 100) * ratio) / 1024 / 1024;
509         cmd.push_back(StringPrintf("--new=0:0:+%" PRId64 "M", splitMb));
510         cmd.push_back(StringPrintf("--typecode=0:%s", kGptBasicData));
511         cmd.push_back("--change-name=0:shared");
512     }
513 
514     // Define a metadata partition which is designed for future use; there
515     // should only be one of these per physical device, even if there are
516     // multiple private volumes.
517     cmd.push_back("--new=0:0:+16M");
518     cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidMeta));
519     cmd.push_back("--change-name=0:android_meta");
520 
521     // Define a single private partition filling the rest of disk.
522     cmd.push_back("--new=0:0:-0");
523     cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidExpand));
524     cmd.push_back(StringPrintf("--partition-guid=0:%s", partGuid.c_str()));
525     cmd.push_back("--change-name=0:android_expand");
526 
527     cmd.push_back(mDevPath);
528 
529     if ((res = ForkExecvp(cmd)) != 0) {
530         LOG(ERROR) << "Failed to partition; status " << res;
531         return res;
532     }
533 
534     return OK;
535 }
536 
getMaxMinors()537 int Disk::getMaxMinors() {
538     // Figure out maximum partition devices supported
539     unsigned int majorId = major(mDevice);
540     switch (majorId) {
541         case kMajorBlockLoop: {
542             std::string tmp;
543             if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) {
544                 LOG(ERROR) << "Failed to read max minors";
545                 return -errno;
546             }
547             return std::stoi(tmp);
548         }
549         // clang-format off
550         case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC:
551         case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF:
552         case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI:
553         case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
554         case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO:
555         case kMajorBlockScsiP: {
556             // clang-format on
557             // Per Documentation/devices.txt this is static
558             return 15;
559         }
560         case kMajorBlockMmc: {
561             // Per Documentation/devices.txt this is dynamic
562             std::string tmp;
563             if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp) &&
564                 !ReadFileToString(kSysfsMmcMaxMinorsDeprecated, &tmp)) {
565                 LOG(ERROR) << "Failed to read max minors";
566                 return -errno;
567             }
568             return std::stoi(tmp);
569         }
570         default: {
571             if (IsVirtioBlkDevice(majorId)) {
572                 // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
573                 // 2^4 - 1 = 15
574                 return 15;
575             }
576             if (isNvmeBlkDevice(majorId, mSysPath)) {
577                 // despite kernel nvme driver supports up to 1M minors,
578                 //     #define NVME_MINORS (1U << MINORBITS)
579                 // sgdisk can not support more than 127 partitions, due to
580                 //     #define MAX_MBR_PARTS 128
581                 return 127;
582             }
583         }
584     }
585 
586     LOG(ERROR) << "Unsupported block major type " << majorId;
587     return -ENOTSUP;
588 }
589 
590 }  // namespace vold
591 }  // namespace android
592