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