转自 http://blog.csdn.net/kc58236582/article/details/50577333——看看这两篇文章,也许能解决U盘不能在es 文件浏览器看见的问题
之前分析过vold插入sd卡的流程,本以为otg插入U盘与sd卡走的流程一样。想不到还是有差别的,下面我们来分析下:
一、kAdoptable flag
先来看otg插入卡,后handleBlockEvent的流程
- void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
- std::lock_guard<std::mutex> lock(mLock);
- std::string eventPath(evt->findParam("DEVPATH"));
- std::string devType(evt->findParam("DEVTYPE"));
- if (devType != "disk") return;
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- dev_t device = makedev(major, minor);
- switch (evt->getAction()) {
- case NetlinkEvent::Action::kAdd: {
- for (auto source : mDiskSources) {
- if (source->matches(eventPath)) {
- // For now, assume that MMC devices are SD, and that
- // everything else is USB
- int flags = source->getFlags();
- if (flags & android::vold::Disk::Flags::kAdoptable) {//这边的log是自己调试的时候加的
- PLOG(ERROR) << "add disk kAdoptable";
- }
- if (major == kMajorBlockMmc) {
- flags |= android::vold::Disk::Flags::kSd;//加入sd的flag
- } else {
- LOG(DEBUG) << "usb disk add.";
- flags |= android::vold::Disk::Flags::kUsb;//加入usb的flag
- }
- auto disk = new android::vold::Disk(eventPath, device,
- source->getNickname(), flags);
- disk->create();
- mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
- break;
- }
- }
- break;
- }
- ......
- }
上面的函数我们自己加了log,当走sd卡的时候从底层传上来的flag中就有kAdoptable,而如果是usb的就没有kAdoptable这个flag。
这点差别也会导致上层MountService处理的差别。
二、MountFlags MOUNT_FLAG_VISIBLE
- private void onVolumeCreatedLocked(VolumeInfo vol) {
- if (vol.type == VolumeInfo.TYPE_EMULATED) {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
- if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
- && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
- Slog.v(TAG, "Found primary storage at " + vol);
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
- Slog.v(TAG, "Found primary storage at " + vol);
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- }
- } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {//sd卡和otg都是Public
- Slog.d(TAG, "kangchen VolumeInfo.TYPE_PUBLIC");
- // TODO: only look at first public partition
- if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
- && vol.disk.isDefaultPrimary()) {
- Slog.d(TAG, "kangchen UUID_PRIMARY_PHYSICAL");
- Slog.v(TAG, "Found primary storage at " + vol);
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- }
- // Adoptable public disks are visible to apps, since they meet
- // public API requirement of being in a stable location.
- if (vol.disk.isAdoptable()) {//如果有kAdoptable这个flag
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- }
- vol.mountUserId = UserHandle.USER_OWNER;
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
- } else {
- Slog.d(TAG, "Skipping automatic mounting of " + vol);
- }
- }
从上面处理volume创建来看,如果Disk有kAdoptable这个flag,那么往vold传的mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE
三、vold otg的挂载流程
下面我们再来看vold中PublicVolume的挂载:
- status_t PublicVolume::doMount() {
- // TODO: expand to support mounting other filesystems
- readMetadata();
- if (mFsType != "vfat") {
- LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
- return -EIO;
- }
- if (vfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
- // Use UUID as stable name, if available
- std::string stableName = getId();
- if (!mFsUuid.empty()) {
- stableName = mFsUuid;
- }
- mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
- mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
- mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
- mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
- setInternalPath(mRawPath);
- if (getMountFlags() & MountFlags::kVisible) {
- setPath(StringPrintf("/storage/%s", stableName.c_str()));
- } else {
- setPath(mRawPath);
- }
- //创建挂载目录,以及各个fuse目录
- if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT) ||
- fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
- fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
- fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
- PLOG(ERROR) << getId() << " failed to create mount points";
- return -errno;
- }
- if (vfat::Mount(mDevPath, mRawPath, false, false, false,
- AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
- PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
- return -EIO;
- }
- if (getMountFlags() & MountFlags::kPrimary) {
- initAsecStage();
- }
- if (!(getMountFlags() & MountFlags::kVisible)) {//如果MountService没有传kVisible这个flag下来,那么就不进行fuse操作了。
- // Not visible to apps, so no need to spin up FUSE
- return OK;
- }
- dev_t before = GetDevice(mFuseWrite);
- if (!(mFusePid = fork())) {
- if (getMountFlags() & MountFlags::kPrimary) {
- if (execl(kFusePath, kFusePath,
- "-u", "1023", // AID_MEDIA_RW
- "-g", "1023", // AID_MEDIA_RW
- "-U", std::to_string(getMountUserId()).c_str(),
- "-w",
- mRawPath.c_str(),
- stableName.c_str(),
- NULL)) {
- PLOG(ERROR) << "Failed to exec";
- }
- } else {
- if (execl(kFusePath, kFusePath,
- "-u", "1023", // AID_MEDIA_RW
- "-g", "1023", // AID_MEDIA_RW
- "-U", std::to_string(getMountUserId()).c_str(),
- mRawPath.c_str(),
- stableName.c_str(),
- NULL)) {
- PLOG(ERROR) << "Failed to exec";
- }
- }
- LOG(ERROR) << "FUSE exiting";
- _exit(1);
- }
- if (mFusePid == -1) {
- PLOG(ERROR) << getId() << " failed to fork";
- return -errno;
- }
- while (before == GetDevice(mFuseWrite)) {
- LOG(VERBOSE) << "Waiting for FUSE to spin up...";
- usleep(50000); // 50ms
- }
- return OK;
- }
而我们看PublicVolume的挂载流程,当MountService传的MountFlag没有kVisible这个flag的话,就直接挂载完就结束了,不会再走fuse流程了。
所以在storage下面是看不到otg的U盘路径的,这里说错了,因为前面fuse的路径是创建了,但是没有fuse上,所以storage下的这个路径是无效的。
所以上面的代码需要修改下:
- status_t PublicVolume::doMount() {
- // TODO: expand to support mounting other filesystems
- readMetadata();
- if (mFsType != "vfat") {
- LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
- return -EIO;
- }
- if (vfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
- // Use UUID as stable name, if available
- std::string stableName = getId();
- if (!mFsUuid.empty()) {
- stableName = mFsUuid;
- }
- mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
- mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
- mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
- mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
- setInternalPath(mRawPath);
- if (getMountFlags() & MountFlags::kVisible) {
- setPath(StringPrintf("/storage/%s", stableName.c_str()));
- } else {
- setPath(mRawPath);
- }
- if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
- PLOG(ERROR) << getId() << " failed to create mount points";
- return -errno;
- }
- if (vfat::Mount(mDevPath, mRawPath, false, false, false,
- AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
- PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
- return -EIO;
- }
- if (getMountFlags() & MountFlags::kPrimary) {
- initAsecStage();
- }
- if (!(getMountFlags() & MountFlags::kVisible)) {
- // Not visible to apps, so no need to spin up FUSE
- return OK;
- }
- if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
- fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
- fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
- PLOG(ERROR) << getId() << " failed to create fuse points";
- return -errno;
- }
- //执行fuse操作
- .......
- return OK;
- }
上面如果是otg的话,创建的fuse路径也就没有什么用,所以我们把mount的路径放前面,fuse的路径放后面,这样挂载otg后也就不会有fuse这个没有用的路径生成了。
所以这样的话otg挂载的volume,拿到路径的话,也是没有权限的。因为挂载的路径是mnt/media下面的需要的权限需要:
- <permission name="android.permission.WRITE_MEDIA_STORAGE" >
- <group gid="media_rw" />
- <group gid="sdcard_rw" />
- </permission>
android6.0 把sd卡可以设置成内部存储。
一、fatab文件匹配
我们先来看下,vold的main函数:
main函数在创建了VolumeManager后,调用了如下函数:
- if (process_config(vm)) {
- PLOG(ERROR) << "Error reading configuration... continuing anyways";
- }
我们再来看看这个函数:
- static int process_config(VolumeManager *vm) {
- std::string path(android::vold::DefaultFstabPath());
- fstab = fs_mgr_read_fstab(path.c_str());//获取fstab文件
- if (!fstab) {
- PLOG(ERROR) << "Failed to open default fstab " << path;
- return -1;
- }
- /* Loop through entries looking for ones that vold manages */
- bool has_adoptable = false;
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {//fastab文件满足vold的项
- if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
- LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
- continue;
- }
- std::string sysPattern(fstab->recs[i].blk_device);
- std::string nickname(fstab->recs[i].label);
- int flags = 0;
- if (fs_mgr_is_encryptable(&fstab->recs[i])) {
- flags |= android::vold::Disk::Flags::kAdoptable;
- has_adoptable = true;
- }
- if (fs_mgr_is_noemulatedsd(&fstab->recs[i])
- || property_get_bool("vold.debug.default_primary", false)) {
- flags |= android::vold::Disk::Flags::kDefaultPrimary;
- }
- vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
- new VolumeManager::DiskSource(sysPattern, nickname, flags)));
- }
- }
- property_set("vold.has_adoptable", has_adoptable ? "1" : "0");
- return 0;
- }
这个函数先获取fstab文件,分析fstab文件,创建了DiskSource对象。然后调用了VolumeManager的addDiskSource函数:
- void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
- mDiskSources.push_back(diskSource);
- }
下面我们来看下fstab文件,下面文件有机箱是voldmanaged的,就是上面函数满足的条件
- /dev/block/platform/comip-mmc.1/by-name/system /system ext4 ro,barrier=1 wait
- /dev/block/platform/comip-mmc.1/by-name/cache /cache ext4 noatime,nosuid,nodev,barrier=1,data=ordered wait,check
- /dev/block/platform/comip-mmc.1/by-name/userdata /data ext4 noatime,nosuid,nodev,barrier=1,data=ordered,noauto_da_alloc resize,wait,check,encryptable=footer
- #/dev/block/platform/comip-mmc.1/by-name/amt /amt ext4 rw wait
- /devices/platform/comip-mmc.0/mmc_host/mmc1/* auto vfat defaults voldmanaged=sdcard1:auto,encryptable=false
- /devices/a0400000.usb_hcd/usb1/* auto vfat defaults voldmanaged=usbotg:auto,noemulatedsd
- /dev/block/mmcblk1p1 /sdcard vfat defaults recoveryonly
- /dev/block/platform/comip-mmc.1/by-name/kernel /kernel emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/ramdisk /boot emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/ramdisk_recovery /recovery emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/ramdisk_amt1 /ramdisk_amt1 emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/ramdisk_amt3 /ramdisk_amt3 emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/kernel_recovery /kernel_recovery emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/logo /logo emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/misc /misc emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/fota /fota emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/modemarm /modemarm emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/modemdsp /modemdsp emmc defaults defaults
- /dev/block/mmcblk0boot0 /uboot emmc defaults defaults
- /dev/block/platform/comip-mmc.1/by-name/lcboot /lcboot emmc defaults defaults
- /dev/block/zram0 none swap defaults zramsize=268435456
二、检测到设备
然后就是有检测到设备,到下面函数:
- void NetlinkHandler::onEvent(NetlinkEvent *evt) {
- VolumeManager *vm = VolumeManager::Instance();
- const char *subsys = evt->getSubsystem();
- if (!subsys) {
- SLOGW("No subsystem found in netlink event");
- return;
- }
- if (!strcmp(subsys, "block")) {
- vm->handleBlockEvent(evt);
- }
- }
调用了VolumeManager的handleBlockEvent函数:
- void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
- std::lock_guard<std::mutex> lock(mLock);
- if (mDebug) {
- LOG(VERBOSE) << "----------------";
- LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();
- evt->dump();
- }
- std::string eventPath(evt->findParam("DEVPATH"));
- std::string devType(evt->findParam("DEVTYPE"));
- if (devType != "disk") return;
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- dev_t device = makedev(major, minor);//创建设备
- switch (evt->getAction()) {
- case NetlinkEvent::Action::kAdd: {
- for (auto source : mDiskSources) {
- if (source->matches(eventPath)) {//之前的mDiskSources就是用来匹配的
- // For now, assume that MMC devices are SD, and that
- // everything else is USB
- int flags = source->getFlags();//从source获取flags
- if (major == kMajorBlockMmc) {
- flags |= android::vold::Disk::Flags::kSd;
- } else {
- flags |= android::vold::Disk::Flags::kUsb;
- }
- auto disk = new android::vold::Disk(eventPath, device,
- source->getNickname(), flags);//新建Disk
- disk->create();
- mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
- break;
- }
- }
- break;
- }
有设备是主要是Aciton:kAdd,这个函数先创建了设备,根据major和minor。
从mDiskSources看是否有满足的DiskSource,然后获取其flags,然后新建Disk,调用create函数。
三、Disk创建
- Disk::Disk(const std::string& eventPath, dev_t device,
- const std::string& nickname, int flags) :
- mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(
- false), mJustPartitioned(false) {
- mId = StringPrintf("disk:%u,%u", major(device), minor(device));
- mEventPath = eventPath;
- mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
- mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());//设备地址
- CreateDeviceNode(mDevPath, mDevice);
- }
我们再来看看CreateDeviceNode函数:
- status_t CreateDeviceNode(const std::string& path, dev_t dev) {
- const char* cpath = path.c_str();
- status_t res = 0;
- char* secontext = nullptr;
- if (sehandle) {
- if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
- setfscreatecon(secontext);
- }
- }
- mode_t mode = 0660 | S_IFBLK;
- if (mknod(cpath, mode, dev) < 0) {
- if (errno != EEXIST) {
- PLOG(ERROR) << "Failed to create device node for " << major(dev)
- << ":" << minor(dev) << " at " << path;
- res = -errno;
- }
- }
- if (secontext) {
- setfscreatecon(nullptr);
- freecon(secontext);
- }
- return res;
- }
这个函数里面,其中最重要的就是mknod,把device的设备放在了dev,这个设备目录。等于创建了设备目录,等于我们就有了dev/block/vold/disk:179,128这个目录。
我们再来看,create函数
- status_t Disk::create() {
- CHECK(!mCreated);
- mCreated = true;
- notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
- readMetadata();
- readPartitions();
- return OK;
- }
readMetadata主要就是获取信息,然后和MountService通信,和MountService通信我们这里不说了,之前的博客都分析过很多。
- status_t Disk::readMetadata() {
- mSize = -1;
- mLabel.clear();
- int fd = open(mDevPath.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- if (ioctl(fd, BLKGETSIZE64, &mSize)) {
- mSize = -1;
- }
- close(fd);
- }
- switch (major(mDevice)) {
- case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
- case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
- case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
- case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO: case kMajorBlockScsiP: {
- std::string path(mSysPath + "/device/vendor");
- std::string tmp;
- if (!ReadFileToString(path, &tmp)) {
- PLOG(WARNING) << "Failed to read vendor from " << path;
- return -errno;
- }
- mLabel = tmp;
- break;
- }
- case kMajorBlockMmc: {
- std::string path(mSysPath + "/device/manfid");
- std::string tmp;
- if (!ReadFileToString(path, &tmp)) {
- PLOG(WARNING) << "Failed to read manufacturer from " << path;
- return -errno;
- }
- uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
- // Our goal here is to give the user a meaningful label, ideally
- // matching whatever is silk-screened on the card. To reduce
- // user confusion, this list doesn't contain white-label manfid.
- switch (manfid) {
- case 0x000003: mLabel = "SanDisk"; break;
- case 0x00001b: mLabel = "Samsung"; break;
- case 0x000028: mLabel = "Lexar"; break;
- case 0x000074: mLabel = "Transcend"; break;
- }
- break;
- }
- default: {
- LOG(WARNING) << "Unsupported block major type" << major(mDevice);
- return -ENOTSUP;
- }
- }
- notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));
- notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
- notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
- return OK;
- }
我们主要看readPartitions这个函数:
- status_t Disk::readPartitions() {
- int8_t maxMinors = getMaxMinors();
- if (maxMinors < 0) {
- return -ENOTSUP;
- }
- destroyAllVolumes();
- // Parse partition table
- std::vector<std::string> cmd;
- cmd.push_back(kSgdiskPath);//"/system/bin/sgdisk";
- cmd.push_back("--android-dump");
- cmd.push_back(mDevPath);
- std::vector<std::string> output;
- status_t res = ForkExecvp(cmd, output);
- if (res != OK) {
- LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
- notifyEvent(ResponseCode::DiskScanned);
- mJustPartitioned = false;
- return res;
- }
- Table table = Table::kUnknown;
- bool foundParts = false;
- for (auto line : output) {
- char* cline = (char*) line.c_str();
- char* token = strtok(cline, kSgdiskToken);
- if (token == nullptr) continue;
- if (!strcmp(token, "DISK")) {
- const char* type = strtok(nullptr, kSgdiskToken);
- if (!strcmp(type, "mbr")) {//mbr
- table = Table::kMbr;
- } else if (!strcmp(type, "gpt")) {
- table = Table::kGpt;//gpt
- }
- } else if (!strcmp(token, "PART")) {
- foundParts = true;
- int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
- if (i <= 0 || i > maxMinors) {
- LOG(WARNING) << mId << " is ignoring partition " << i
- << " beyond max supported devices";
- continue;
- }
- dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);//和之前Disk一样根据disk的device的major和minor创建volume的device
- if (table == Table::kMbr) {
- const char* type = strtok(nullptr, kSgdiskToken);
- switch (strtol(type, nullptr, 16)) {
- case 0x06: // FAT16
- case 0x0b: // W95 FAT32 (LBA)
- case 0x0c: // W95 FAT32 (LBA)
- case 0x0e: // W95 FAT16 (LBA)
- createPublicVolume(partDevice);
- break;
- }
- } else if (table == Table::kGpt) {//下面的就是gpt
- const char* typeGuid = strtok(nullptr, kSgdiskToken);
- const char* partGuid = strtok(nullptr, kSgdiskToken);
- if (!strcasecmp(typeGuid, kGptBasicData)) {
- createPublicVolume(partDevice);
- } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {//expand就是privatevolume
- createPrivateVolume(partDevice, partGuid);
- }
- }
- }
- }
- // Ugly last ditch effort, treat entire disk as partition
- if (table == Table::kUnknown || !foundParts) {
- LOG(WARNING) << mId << " has unknown partition table; trying entire device";
- std::string fsType;
- std::string unused;
- if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
- createPublicVolume(mDevice);
- } else {
- LOG(WARNING) << mId << " failed to identify, giving up";
- }
- }
- notifyEvent(ResponseCode::DiskScanned);
- mJustPartitioned = false;
- return OK;
- }
上面函数,就是根据sgdisk这个工具,输入命令,去读取输出,相应创建privatevolume还是PublicVolume
下面我们结合sgdisk的命令输出来看:
- DISK gpt D25EA824-98CC-4390-899F-40F8B7609491
- PART 1 193D1EA4-B3CA-11E4-B075-10604B889DCF 7F3EE593-E357-196F-7707-FA295A508E64 android_expand
这里是内部存储sgdisk的输出。这里disk是gpt。
四、内部存储创建
我们再来看看createPrivatevolume,
- void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {
- std::string normalizedGuid;
- if (NormalizeHex(partGuid, normalizedGuid)) {
- LOG(WARNING) << "Invalid GUID " << partGuid;
- return;
- }
- std::string keyRaw;
- if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {
- PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;
- return;
- }
- LOG(DEBUG) << "Found key for GUID " << normalizedGuid;
- auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));
- if (mJustPartitioned) {
- LOG(DEBUG) << "Device just partitioned; silently formatting";
- vol->setSilent(true);
- vol->create();
- vol->format("auto");
- vol->destroy();
- vol->setSilent(false);
- }
- mVolumes.push_back(vol);
- vol->setDiskId(getId());
- vol->setPartGuid(partGuid);
- vol->create();
- }
再来看Privatevolume的构造函数
- PrivateVolume::PrivateVolume(dev_t device, const std::string& keyRaw) :
- VolumeBase(Type::kPrivate), mRawDevice(device), mKeyRaw(keyRaw) {
- setId(StringPrintf("private:%u,%u", major(device), minor(device)));
- mRawDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
- }
doFormat函数就是将sd卡格式化,比如格式化成ext4的。
- status_t PrivateVolume::doFormat(const std::string& fsType) {
- std::string resolvedFsType = fsType;
- if (fsType == "auto") {
- // For now, assume that all MMC devices are flash-based SD cards, and
- // give everyone else ext4 because sysfs rotational isn't reliable.
- if ((major(mRawDevice) == kMajorBlockMmc) && f2fs::IsSupported()) {
- resolvedFsType = "f2fs";
- } else {
- resolvedFsType = "ext4";
- }
- LOG(DEBUG) << "Resolved auto to " << resolvedFsType;
- }
- if (resolvedFsType == "ext4") {
- // TODO: change reported mountpoint once we have better selinux support
- if (ext4::Format(mDmDevPath, 0, "/data")) {
- PLOG(ERROR) << getId() << " failed to format";
- return -EIO;
- }
- } else if (resolvedFsType == "f2fs") {
- if (f2fs::Format(mDmDevPath)) {
- PLOG(ERROR) << getId() << " failed to format";
- return -EIO;
- }
- } else {
- LOG(ERROR) << getId() << " unsupported filesystem " << fsType;
- return -EINVAL;
- }
- return OK;
- }
doCreate函数,就会调用CreateDeviceNode,来创建dev设备,就是将device绑在dev目录下。后面一些函数没有深入研究
- status_t PrivateVolume::doCreate() {
- if (CreateDeviceNode(mRawDevPath, mRawDevice)) {
- return -EIO;
- }
- // Recover from stale vold by tearing down any old mappings
- cryptfs_revert_ext_volume(getId().c_str());
- // TODO: figure out better SELinux labels for private volumes
- unsigned char* key = (unsigned char*) mKeyRaw.data();
- char crypto_blkdev[MAXPATHLEN];
- int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(),
- key, mKeyRaw.size(), crypto_blkdev);
- mDmDevPath = crypto_blkdev;
- if (res != 0) {
- PLOG(ERROR) << getId() << " failed to setup cryptfs";
- return -EIO;
- }
- return OK;
- }
之后就是mount了,内部存储是将dev文件mount到mnt/expand下。
- status_t PrivateVolume::doMount() {
- if (readMetadata()) {
- LOG(ERROR) << getId() << " failed to read metadata";
- return -EIO;
- }
- mPath = StringPrintf("/mnt/expand/%s", mFsUuid.c_str());
- setPath(mPath);
- if (PrepareDir(mPath, 0700, AID_ROOT, AID_ROOT)) {
- PLOG(ERROR) << getId() << " failed to create mount point " << mPath;
- return -EIO;
- }
- if (mFsType == "ext4") {
- int res = ext4::Check(mDmDevPath, mPath);
- if (res == 0 || res == 1) {
- LOG(DEBUG) << getId() << " passed filesystem check";
- } else {
- PLOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
- if (ext4::Mount(mDmDevPath, mPath, false, false, true)) {
- PLOG(ERROR) << getId() << " failed to mount";
- return -EIO;
- }
- }
五、创建外部存储
外部存储sgdisk的输出命令如下:
- 05-30 20:23:45.358 175 177 V vold : DISK mbr
- 05-30 20:23:45.359 175 177 V vold : PART 1 c
我们再来看Disk的readPartitions函数的其中一段
- Table table = Table::kUnknown;
- bool foundParts = false;
- for (auto line : output) {
- char* cline = (char*) line.c_str();
- char* token = strtok(cline, kSgdiskToken);
- if (token == nullptr) continue;
- if (!strcmp(token, "DISK")) {
- const char* type = strtok(nullptr, kSgdiskToken);
- if (!strcmp(type, "mbr")) {//mbr
- table = Table::kMbr;
- } else if (!strcmp(type, "gpt")) {
- table = Table::kGpt;
- }
- } else if (!strcmp(token, "PART")) {
- foundParts = true;
- int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
- if (i <= 0 || i > maxMinors) {
- LOG(WARNING) << mId << " is ignoring partition " << i
- << " beyond max supported devices";
- continue;
- }
- dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
- if (table == Table::kMbr) {
- const char* type = strtok(nullptr, kSgdiskToken);
- switch (strtol(type, nullptr, 16)) {
- case 0x06: // FAT16
- case 0x0b: // W95 FAT32 (LBA)
- case 0x0c: // W95 FAT32 (LBA)
- case 0x0e: // W95 FAT16 (LBA)
- createPublicVolume(partDevice);// 是mbr的就直接调用createPublicVolume函数
- break;
- }
- } else if (table == Table::kGpt) {
- const char* typeGuid = strtok(nullptr, kSgdiskToken);
- const char* partGuid = strtok(nullptr, kSgdiskToken);
- if (!strcasecmp(typeGuid, kGptBasicData)) {
- createPublicVolume(partDevice);
- } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {
- createPrivateVolume(partDevice, partGuid);
- }
- }
- }
- }
然后我们看createPublicVolume函数:
- void Disk::createPublicVolume(dev_t device) {
- auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
- if (mJustPartitioned) {
- LOG(DEBUG) << "Device just partitioned; silently formatting";
- vol->setSilent(true);
- vol->create();
- vol->format("auto");
- vol->destroy();
- vol->setSilent(false);
- }
- mVolumes.push_back(vol);
- vol->setDiskId(getId());
- vol->create();
- }
构造函数
- PublicVolume::PublicVolume(dev_t device) :
- VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
- setId(StringPrintf("public:%u,%u", major(device), minor(device)));
- mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
- }
创建设备
- status_t PublicVolume::doCreate() {
- return CreateDeviceNode(mDevPath, mDevice);
- }
- status_t PublicVolume::doFormat(const std::string& fsType) {
- if (fsType == "vfat" || fsType == "auto") {
- if (WipeBlockDevice(mDevPath) != OK) {
- LOG(WARNING) << getId() << " failed to wipe";
- }
- if (vfat::Format(mDevPath, 0)) {
- LOG(ERROR) << getId() << " failed to format";
- return -errno;
- }
- } else {
- LOG(ERROR) << "Unsupported filesystem " << fsType;
- return -EINVAL;
- }
- return OK;
- }
我们再来看下mount的第一步流程:
- status_t PublicVolume::doMount() {
- // TODO: expand to support mounting other filesystems
- readMetadata();
- if (mFsType != "vfat") {
- LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
- return -EIO;
- }
- if (vfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
doMount函数中显示调用了readMetadata函数:
- status_t PublicVolume::readMetadata() {
- status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
- notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
- notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
- notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
- return res;
- }
我们可以知道通过ReadMetadataUntrusted函数来获取mFsType, mFsUuid, mFsLabel3个值。
我们再来看下ReadMetadataUntrusted函数:
- status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
- std::string& fsUuid, std::string& fsLabel) {
- return readMetadata(path, fsType, fsUuid, fsLabel, true);
- }
readMetadata函数,我们通过blkid进程来获取这些值。
- static status_t readMetadata(const std::string& path, std::string& fsType,
- std::string& fsUuid, std::string& fsLabel, bool untrusted) {
- fsType.clear();
- fsUuid.clear();
- fsLabel.clear();
- std::vector<std::string> cmd;
- cmd.push_back(kBlkidPath);//"/system/bin/blkid"
- cmd.push_back("-c");
- cmd.push_back("/dev/null");
- cmd.push_back("-s");
- cmd.push_back("TYPE");
- cmd.push_back("-s");
- cmd.push_back("UUID");
- cmd.push_back("-s");
- cmd.push_back("LABEL");
- cmd.push_back(path);
- std::vector<std::string> output;
- status_t res = ForkExecvp(cmd, output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
- if (res != OK) {
- LOG(WARNING) << "blkid failed to identify " << path;
- return res;
- }
- char value[128];
- for (auto line : output) {
- // Extract values from blkid output, if defined
- const char* cline = line.c_str();
- char* start = strstr(cline, "TYPE=");
- if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
- fsType = value;
- }
- start = strstr(cline, "UUID=");
- if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
- fsUuid = value;
- }
- start = strstr(cline, "LABEL=");
- if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
- fsLabel = value;
- }
- }
- return OK;
- }
在这里我们再看下ForkExecvp函数:
- status_t ForkExecvp(const std::vector<std::string>& args,
- std::vector<std::string>& output, security_context_t context) {
- std::string cmd;
- for (size_t i = 0; i < args.size(); i++) {
- cmd += args[i] + " ";
- if (i == 0) {
- LOG(VERBOSE) << args[i];//输入的命令打印
- } else {
- LOG(VERBOSE) << " " << args[i];
- }
- }
- output.clear();
- if (setexeccon(context)) {
- LOG(ERROR) << "Failed to setexeccon";
- abort();
- }
- FILE* fp = popen(cmd.c_str(), "r");//通过popen函数
- if (setexeccon(nullptr)) {
- LOG(ERROR) << "Failed to setexeccon";
- abort();
- }
- if (!fp) {
- PLOG(ERROR) << "Failed to popen " << cmd;
- return -errno;
- }
- char line[1024];
- while (fgets(line, sizeof(line), fp) != nullptr) {//获取输出
- LOG(VERBOSE) << line;
- output.push_back(std::string(line));
- }
- if (pclose(fp) != 0) {
- PLOG(ERROR) << "Failed to pclose " << cmd;
- return -errno;
- }
- return OK;
- }
上面通过popen来执行blkid进程获取信息,popen函数先fork,然后调用exec执行cmd,并且返回一个标准I/O的文件指针。
当type是r,文件指针连接到cmd的标准输出。如果是w,文件指针连接到cmd的标准输入。
最后pclose是关闭标准I/O流。
六、如何决定外部存储还是内部存储
流程我们看完了,但是disk在调用readPartitions函数时,根据sgdisk工具的输出,来决定是创建外部存储还是内部存储。但是这个sgdisk的输出又是谁来决定的,我们可以看下下面两个函数:
第一个就是格式化为内部存储:
- status_t Disk::partitionPrivate() {
- return partitionMixed(0);
- }
partitionMixed函数同样调用了sgdisk工具
- status_t Disk::partitionMixed(int8_t ratio) {
- int res;
- destroyAllVolumes();
- mJustPartitioned = true;
- // First nuke any existing partition table
- std::vector<std::string> cmd;
- cmd.push_back(kSgdiskPath);
- cmd.push_back("--zap-all");
- cmd.push_back(mDevPath);
- // Zap sometimes returns an error when it actually succeeded, so
- // just log as warning and keep rolling forward.
- if ((res = ForkExecvp(cmd)) != 0) {
- LOG(WARNING) << "Failed to zap; status " << res;
- }
- // We've had some success above, so generate both the private partition
- // GUID and encryption key and persist them.
- std::string partGuidRaw;
- std::string keyRaw;
- if (ReadRandomBytes(16, partGuidRaw) || ReadRandomBytes(16, keyRaw)) {
- LOG(ERROR) << "Failed to generate GUID or key";
- return -EIO;
- }
- std::string partGuid;
- StrToHex(partGuidRaw, partGuid);
- if (!WriteStringToFile(keyRaw, BuildKeyPath(partGuid))) {
- LOG(ERROR) << "Failed to persist key";
- return -EIO;
- } else {
- LOG(DEBUG) << "Persisted key for GUID " << partGuid;
- }
- // Now let's build the new GPT table. We heavily rely on sgdisk to
- // force optimal alignment on the created partitions.
- cmd.clear();
- cmd.push_back(kSgdiskPath);
- // If requested, create a public partition first. Mixed-mode partitioning
- // like this is an experimental feature.
- if (ratio > 0) {
- if (ratio < 10 || ratio > 90) {
- LOG(ERROR) << "Mixed partition ratio must be between 10-90%";
- return -EINVAL;
- }
- uint64_t splitMb = ((mSize / 100) * ratio) / 1024 / 1024;
- cmd.push_back(StringPrintf("--new=0:0:+%" PRId64 "M", splitMb));
- cmd.push_back(StringPrintf("--typecode=0:%s", kGptBasicData));
- cmd.push_back("--change-name=0:shared");
- }
- // Define a metadata partition which is designed for future use; there
- // should only be one of these per physical device, even if there are
- // multiple private volumes.
- /*cmd.push_back("--new=0:0:+16M");// 这段代码被我注释了,因为在我们的手机上有错,不知为何
- cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidMeta));
- cmd.push_back("--change-name=0:android_meta");*/
- // Define a single private partition filling the rest of disk.
- cmd.push_back("--new=0:0:-0");
- cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidExpand));
- cmd.push_back(StringPrintf("--partition-guid=0:%s", partGuid.c_str()));
- cmd.push_back("--change-name=0:android_expand");// 这里就是对应我们的PART1 的expand
- cmd.push_back(mDevPath);
- if ((res = ForkExecvp(cmd)) != 0) {
- LOG(ERROR) << "Failed to partition; status " << res;
- return res;
- }
- return OK;
- }
原来的意思是先创建一个16M的part1为android_meta, 剩余的存储创建了part2,为android_expand。但是这样后面,在创建part2的时候createPrivatevolume,最后在Privatevolume的docreate函数出错了,没有找到设备。所以将part1,这段代码注释后是可以的。
第二个函数partitionPublic函数如下,变成外部存储。类似上面
- status_t Disk::partitionPublic() {
- int res;
- // TODO: improve this code
- destroyAllVolumes();
- mJustPartitioned = true;
- // First nuke any existing partition table
- std::vector<std::string> cmd;
- cmd.push_back(kSgdiskPath);
- cmd.push_back("--zap-all");
- cmd.push_back(mDevPath);
- // Zap sometimes returns an error when it actually succeeded, so
- // just log as warning and keep rolling forward.
- if ((res = ForkExecvp(cmd)) != 0) {
- LOG(WARNING) << "Failed to zap; status " << res;
- }
- struct disk_info dinfo;
- memset(&dinfo, 0, sizeof(dinfo));
- if (!(dinfo.part_lst = (struct part_info *) malloc(
- MAX_NUM_PARTS * sizeof(struct part_info)))) {
- return -1;
- }
- memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
- dinfo.device = strdup(mDevPath.c_str());
- dinfo.scheme = PART_SCHEME_MBR;
- dinfo.sect_size = 512;
- dinfo.skip_lba = 2048;
- dinfo.num_lba = 0;
- dinfo.num_parts = 1;
- struct part_info *pinfo = &dinfo.part_lst[0];
- pinfo->name = strdup("android_sdcard");
- pinfo->flags |= PART_ACTIVE_FLAG;
- pinfo->type = PC_PART_TYPE_FAT32;
- pinfo->len_kb = -1;
- int rc = apply_disk_config(&dinfo, 0);
- if (rc) {
- LOG(ERROR) << "Failed to apply disk configuration: " << rc;
- goto out;
- }
- out:
- free(pinfo->name);
- free(dinfo.device);
- free(dinfo.part_lst);
- return rc;
- }
而在调用这两个函数之后,应该是将某些数据烧录到了sd卡中。
因为下次开机,vold在Disk的readPartitions函数根据sgdisk工具输出,就自动识别出是publicvolume还是Privatevolume