• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getVolumes() const135 std::vector<std::shared_ptr<VolumeBase>> Disk::getVolumes() const {
136     std::vector<std::shared_ptr<VolumeBase>> vols;
137     for (const auto& vol : mVolumes) {
138         vols.push_back(vol);
139         auto stackedVolumes = vol->getVolumes();
140         vols.insert(vols.end(), stackedVolumes.begin(), stackedVolumes.end());
141     }
142 
143     return vols;
144 }
145 
create()146 status_t Disk::create() {
147     CHECK(!mCreated);
148     mCreated = true;
149 
150     auto listener = VolumeManager::Instance()->getListener();
151     if (listener) listener->onDiskCreated(getId(), mFlags);
152 
153     if (isStub()) {
154         createStubVolume();
155         return OK;
156     }
157     readMetadata();
158     readPartitions();
159     return OK;
160 }
161 
destroy()162 status_t Disk::destroy() {
163     CHECK(mCreated);
164     destroyAllVolumes();
165     mCreated = false;
166 
167     auto listener = VolumeManager::Instance()->getListener();
168     if (listener) listener->onDiskDestroyed(getId());
169 
170     return OK;
171 }
172 
createPublicVolume(dev_t device)173 void Disk::createPublicVolume(dev_t device) {
174     auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
175     if (mJustPartitioned) {
176         LOG(DEBUG) << "Device just partitioned; silently formatting";
177         vol->setSilent(true);
178         vol->create();
179         vol->format("auto");
180         vol->destroy();
181         vol->setSilent(false);
182     }
183 
184     mVolumes.push_back(vol);
185     vol->setDiskId(getId());
186     vol->create();
187 }
188 
createPrivateVolume(dev_t device,const std::string & partGuid)189 void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {
190     std::string normalizedGuid;
191     if (NormalizeHex(partGuid, normalizedGuid)) {
192         LOG(WARNING) << "Invalid GUID " << partGuid;
193         return;
194     }
195 
196     std::string keyRaw;
197     if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {
198         PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;
199         return;
200     }
201 
202     LOG(DEBUG) << "Found key for GUID " << normalizedGuid;
203 
204     auto keyBuffer = KeyBuffer(keyRaw.begin(), keyRaw.end());
205     auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyBuffer));
206     if (mJustPartitioned) {
207         LOG(DEBUG) << "Device just partitioned; silently formatting";
208         vol->setSilent(true);
209         vol->create();
210         vol->format("auto");
211         vol->destroy();
212         vol->setSilent(false);
213     }
214 
215     mVolumes.push_back(vol);
216     vol->setDiskId(getId());
217     vol->setPartGuid(partGuid);
218     vol->create();
219 }
220 
createStubVolume()221 void Disk::createStubVolume() {
222     CHECK(mVolumes.size() == 1);
223     auto listener = VolumeManager::Instance()->getListener();
224     if (listener) listener->onDiskMetadataChanged(getId(), mSize, mLabel, mSysPath);
225     if (listener) listener->onDiskScanned(getId());
226     mVolumes[0]->setDiskId(getId());
227     mVolumes[0]->create();
228 }
229 
destroyAllVolumes()230 void Disk::destroyAllVolumes() {
231     for (const auto& vol : mVolumes) {
232         vol->destroy();
233     }
234     mVolumes.clear();
235 }
236 
readMetadata()237 status_t Disk::readMetadata() {
238     mSize = -1;
239     mLabel.clear();
240 
241     if (GetBlockDevSize(mDevPath, &mSize) != OK) {
242         mSize = -1;
243     }
244 
245     unsigned int majorId = major(mDevice);
246     switch (majorId) {
247         case kMajorBlockLoop: {
248             mLabel = "Virtual";
249             break;
250         }
251         // clang-format off
252         case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC:
253         case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF:
254         case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI:
255         case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
256         case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO:
257         case kMajorBlockScsiP: {
258             // clang-format on
259             std::string path(mSysPath + "/device/vendor");
260             std::string tmp;
261             if (!ReadFileToString(path, &tmp)) {
262                 PLOG(WARNING) << "Failed to read vendor from " << path;
263                 return -errno;
264             }
265             tmp = android::base::Trim(tmp);
266             mLabel = tmp;
267             break;
268         }
269         case kMajorBlockMmc: {
270             std::string path(mSysPath + "/device/manfid");
271             std::string tmp;
272             if (!ReadFileToString(path, &tmp)) {
273                 PLOG(WARNING) << "Failed to read manufacturer from " << path;
274                 return -errno;
275             }
276             tmp = android::base::Trim(tmp);
277             int64_t manfid;
278             if (!android::base::ParseInt(tmp, &manfid)) {
279                 PLOG(WARNING) << "Failed to parse manufacturer " << tmp;
280                 return -EINVAL;
281             }
282             // Our goal here is to give the user a meaningful label, ideally
283             // matching whatever is silk-screened on the card.  To reduce
284             // user confusion, this list doesn't contain white-label manfid.
285             switch (manfid) {
286                 // clang-format off
287                 case 0x000003: mLabel = "SanDisk"; break;
288                 case 0x00001b: mLabel = "Samsung"; break;
289                 case 0x000028: mLabel = "Lexar"; break;
290                 case 0x000074: mLabel = "Transcend"; break;
291                     // clang-format on
292             }
293             break;
294         }
295         default: {
296             if (IsVirtioBlkDevice(majorId)) {
297                 LOG(DEBUG) << "Recognized experimental block major ID " << majorId
298                            << " as virtio-blk (emulator's virtual SD card device)";
299                 mLabel = "Virtual";
300                 break;
301             }
302             if (isNvmeBlkDevice(majorId, mSysPath)) {
303                 std::string path(mSysPath + "/device/model");
304                 std::string tmp;
305                 if (!ReadFileToString(path, &tmp)) {
306                     PLOG(WARNING) << "Failed to read vendor from " << path;
307                     return -errno;
308                 }
309                 mLabel = tmp;
310                 break;
311             }
312             LOG(WARNING) << "Unsupported block major type " << majorId;
313             return -ENOTSUP;
314         }
315     }
316 
317     auto listener = VolumeManager::Instance()->getListener();
318     if (listener) listener->onDiskMetadataChanged(getId(), mSize, mLabel, mSysPath);
319 
320     return OK;
321 }
322 
readPartitions()323 status_t Disk::readPartitions() {
324     int maxMinors = getMaxMinors();
325     if (maxMinors < 0) {
326         return -ENOTSUP;
327     }
328 
329     destroyAllVolumes();
330 
331     // Parse partition table
332 
333     std::vector<std::string> cmd;
334     cmd.push_back(kSgdiskPath);
335     cmd.push_back("--android-dump");
336     cmd.push_back(mDevPath);
337 
338     std::vector<std::string> output;
339     status_t res = ForkExecvp(cmd, &output);
340     if (res != OK) {
341         LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
342 
343         auto listener = VolumeManager::Instance()->getListener();
344         if (listener) listener->onDiskScanned(getId());
345 
346         mJustPartitioned = false;
347         return res;
348     }
349 
350     Table table = Table::kUnknown;
351     bool foundParts = false;
352     for (const auto& line : output) {
353         auto split = android::base::Split(line, kSgdiskToken);
354         auto it = split.begin();
355         if (it == split.end()) continue;
356 
357         if (*it == "DISK") {
358             if (++it == split.end()) continue;
359             if (*it == "mbr") {
360                 table = Table::kMbr;
361             } else if (*it == "gpt") {
362                 table = Table::kGpt;
363             } else {
364                 LOG(WARNING) << "Invalid partition table " << *it;
365                 continue;
366             }
367         } else if (*it == "PART") {
368             foundParts = true;
369 
370             if (++it == split.end()) continue;
371             int i = 0;
372             if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
373                 LOG(WARNING) << "Invalid partition number " << *it;
374                 continue;
375             }
376             dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
377 
378             if (table == Table::kMbr) {
379                 if (++it == split.end()) continue;
380                 int type = 0;
381                 if (!android::base::ParseInt("0x" + *it, &type)) {
382                     LOG(WARNING) << "Invalid partition type " << *it;
383                     continue;
384                 }
385 
386                 switch (type) {
387                     case 0x06:  // FAT16
388                     case 0x07:  // HPFS/NTFS/exFAT
389                     case 0x0b:  // W95 FAT32 (LBA)
390                     case 0x0c:  // W95 FAT32 (LBA)
391                     case 0x0e:  // W95 FAT16 (LBA)
392                         createPublicVolume(partDevice);
393                         break;
394                 }
395             } else if (table == Table::kGpt) {
396                 if (++it == split.end()) continue;
397                 auto typeGuid = *it;
398                 if (++it == split.end()) continue;
399                 auto partGuid = *it;
400 
401                 if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
402                     createPublicVolume(partDevice);
403                 } else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
404                     createPrivateVolume(partDevice, partGuid);
405                 }
406             }
407         }
408     }
409 
410     // Ugly last ditch effort, treat entire disk as partition
411     if (table == Table::kUnknown || !foundParts) {
412         LOG(WARNING) << mId << " has unknown partition table; trying entire device";
413 
414         std::string fsType;
415         std::string unused;
416         if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK) {
417             createPublicVolume(mDevice);
418         } else {
419             LOG(WARNING) << mId << " failed to identify, giving up";
420         }
421     }
422 
423     auto listener = VolumeManager::Instance()->getListener();
424     if (listener) listener->onDiskScanned(getId());
425 
426     mJustPartitioned = false;
427     return OK;
428 }
429 
initializePartition(std::shared_ptr<StubVolume> vol)430 void Disk::initializePartition(std::shared_ptr<StubVolume> vol) {
431     CHECK(isStub());
432     CHECK(mVolumes.empty());
433     mVolumes.push_back(vol);
434 }
435 
unmountAll()436 status_t Disk::unmountAll() {
437     for (const auto& vol : mVolumes) {
438         vol->unmount();
439     }
440     return OK;
441 }
442 
partitionPublic()443 status_t Disk::partitionPublic() {
444     int res;
445 
446     destroyAllVolumes();
447     mJustPartitioned = true;
448 
449     // First nuke any existing partition table
450     std::vector<std::string> cmd;
451     cmd.push_back(kSgdiskPath);
452     cmd.push_back("--zap-all");
453     cmd.push_back(mDevPath);
454 
455     // Zap sometimes returns an error when it actually succeeded, so
456     // just log as warning and keep rolling forward.
457     if ((res = ForkExecvp(cmd)) != 0) {
458         LOG(WARNING) << "Failed to zap; status " << res;
459     }
460 
461     // Now let's build the new MBR table. We heavily rely on sgdisk to
462     // force optimal alignment on the created partitions.
463     cmd.clear();
464     cmd.push_back(kSgdiskPath);
465     cmd.push_back("--new=0:0:-0");
466     cmd.push_back("--typecode=0:0c00");
467     cmd.push_back("--gpttombr=1");
468     cmd.push_back(mDevPath);
469 
470     if ((res = ForkExecvp(cmd)) != 0) {
471         LOG(ERROR) << "Failed to partition; status " << res;
472         return res;
473     }
474 
475     return OK;
476 }
477 
partitionPrivate()478 status_t Disk::partitionPrivate() {
479     return partitionMixed(0);
480 }
481 
partitionMixed(int8_t ratio)482 status_t Disk::partitionMixed(int8_t ratio) {
483     int res;
484 
485     destroyAllVolumes();
486     mJustPartitioned = true;
487 
488     // First nuke any existing partition table
489     std::vector<std::string> cmd;
490     cmd.push_back(kSgdiskPath);
491     cmd.push_back("--zap-all");
492     cmd.push_back(mDevPath);
493 
494     // Zap sometimes returns an error when it actually succeeded, so
495     // just log as warning and keep rolling forward.
496     if ((res = ForkExecvp(cmd)) != 0) {
497         LOG(WARNING) << "Failed to zap; status " << res;
498     }
499 
500     // We've had some success above, so generate both the private partition
501     // GUID and encryption key and persist them.
502     std::string partGuidRaw;
503     if (GenerateRandomUuid(partGuidRaw) != OK) {
504         LOG(ERROR) << "Failed to generate GUID";
505         return -EIO;
506     }
507 
508     KeyBuffer key;
509     if (!generate_volume_key(&key)) {
510         LOG(ERROR) << "Failed to generate key";
511         return -EIO;
512     }
513     std::string keyRaw(key.begin(), key.end());
514 
515     std::string partGuid;
516     StrToHex(partGuidRaw, partGuid);
517 
518     if (!WriteStringToFile(keyRaw, BuildKeyPath(partGuid))) {
519         LOG(ERROR) << "Failed to persist key";
520         return -EIO;
521     } else {
522         LOG(DEBUG) << "Persisted key for GUID " << partGuid;
523     }
524 
525     // Now let's build the new GPT table. We heavily rely on sgdisk to
526     // force optimal alignment on the created partitions.
527     cmd.clear();
528     cmd.push_back(kSgdiskPath);
529 
530     // If requested, create a public partition first. Mixed-mode partitioning
531     // like this is an experimental feature.
532     if (ratio > 0) {
533         if (ratio < 10 || ratio > 90) {
534             LOG(ERROR) << "Mixed partition ratio must be between 10-90%";
535             return -EINVAL;
536         }
537 
538         uint64_t splitMb = ((mSize / 100) * ratio) / 1024 / 1024;
539         cmd.push_back(StringPrintf("--new=0:0:+%" PRId64 "M", splitMb));
540         cmd.push_back(StringPrintf("--typecode=0:%s", kGptBasicData));
541         cmd.push_back("--change-name=0:shared");
542     }
543 
544     // Define a metadata partition which is designed for future use; there
545     // should only be one of these per physical device, even if there are
546     // multiple private volumes.
547     cmd.push_back("--new=0:0:+16M");
548     cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidMeta));
549     cmd.push_back("--change-name=0:android_meta");
550 
551     // Define a single private partition filling the rest of disk.
552     cmd.push_back("--new=0:0:-0");
553     cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidExpand));
554     cmd.push_back(StringPrintf("--partition-guid=0:%s", partGuid.c_str()));
555     cmd.push_back("--change-name=0:android_expand");
556 
557     cmd.push_back(mDevPath);
558 
559     if ((res = ForkExecvp(cmd)) != 0) {
560         LOG(ERROR) << "Failed to partition; status " << res;
561         return res;
562     }
563 
564     return OK;
565 }
566 
getMaxMinors()567 int Disk::getMaxMinors() {
568     // Figure out maximum partition devices supported
569     unsigned int majorId = major(mDevice);
570     switch (majorId) {
571         case kMajorBlockLoop: {
572             std::string tmp;
573             if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) {
574                 LOG(ERROR) << "Failed to read max minors";
575                 return -errno;
576             }
577             return std::stoi(tmp);
578         }
579         // clang-format off
580         case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC:
581         case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF:
582         case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI:
583         case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
584         case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO:
585         case kMajorBlockScsiP: {
586             // clang-format on
587             // Per Documentation/devices.txt this is static
588             return 15;
589         }
590         case kMajorBlockMmc: {
591             // Per Documentation/devices.txt this is dynamic
592             std::string tmp;
593             if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp) &&
594                 !ReadFileToString(kSysfsMmcMaxMinorsDeprecated, &tmp)) {
595                 LOG(ERROR) << "Failed to read max minors";
596                 return -errno;
597             }
598             return std::stoi(tmp);
599         }
600         default: {
601             if (IsVirtioBlkDevice(majorId)) {
602                 // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
603                 // 2^4 - 1 = 15
604                 return 15;
605             }
606             if (isNvmeBlkDevice(majorId, mSysPath)) {
607                 // despite kernel nvme driver supports up to 1M minors,
608                 //     #define NVME_MINORS (1U << MINORBITS)
609                 // sgdisk can not support more than 127 partitions, due to
610                 //     #define MAX_MBR_PARTS 128
611                 return 127;
612             }
613         }
614     }
615 
616     LOG(ERROR) << "Unsupported block major type " << majorId;
617     return -ENOTSUP;
618 }
619 
620 }  // namespace vold
621 }  // namespace android
622