记一次U盘挂载无法出现uuid

起因:客户出现一个u盘挂载后不能正确显示名字label。

现象

U盘插入后,目录挂载到/mnt/media_rw/public:8,0

流程

插上U盘看打印信息

  • blkid 信息
/dev/block/zram0: UUID="1499340a-1634-496b-9ddb-38a8a6535458" TYPE="swap" 
/dev/block/mmcblk0p13: LABEL="/" UUID="d3e293f6-a2d6-49d1-9a6b-b9d39184a993" TYPE="ext4" 
/dev/block/mmcblk0p14: UUID="6f7f0cca-8d3b-4139-afeb-91ff2bb7b60e" TYPE="ext4" 
/dev/block/mmcblk0p15: LABEL="vendor" UUID="bcfdbf10-7971-4308-941a-d9674961f0cd" TYPE="ext4" 
/dev/block/mmcblk0p22: LABEL="ta" UUID="77ee8b5c-cbf1-4e67-859b-1bbff2017aa8" TYPE="ext4" 
/dev/block/mmcblk0p24: UUID="19e65932-22a2-4fc2-93de-15e9a76f6547" TYPE="ext4" 
/dev/block/sda1: UUID="64B4E1BAB4E18EBC" TYPE="ntfs" 

可以看到我们新加U盘的UUID和TYPE都是正常的

  • mount信息
/dev/block/vold/public:8,0 on /mnt/media_rw/public:8,0 type iso9660 (ro,dirsync,nosuid,nodev,noexec,relatime,utf8,uid=1023,gid=1023)
/mnt/media_rw/public:8,0 on /mnt/runtime/default/public:8,0 type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=1015,mask=6)
/mnt/media_rw/public:8,0 on /storage/public:8,0 type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=1015,mask=6)
/mnt/media_rw/public:8,0 on /mnt/runtime/read/public:8,0 type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,mask=23)
/mnt/media_rw/public:8,0 on /mnt/runtime/write/public:8,0 type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,mask=7)
  • dmesg信息
[ 7589.270312] usb 2-1: New USB device found, idVendor=0781, idProduct=55a3
[ 7589.270329] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 7589.270340] usb 2-1: Product: Ultra Luxe
[ 7589.270351] usb 2-1: Manufacturer: SanDisk
[ 7589.270363] usb 2-1: SerialNumber: 01010687dee090f0d29a7ab260abf035b88a5808637ee88e1c80bcb00545f452a214000000000000000000003e1f827e009b5400a3558107b7a74c9a
[ 7589.273065] usb-storage 2-1:1.0: USB Mass Storage device detected
[ 7589.277763] scsi host0: usb-storage 2-1:1.0
[ 7590.286713] scsi 0:0:0:0: Direct-Access     SanDisk  Ultra Luxe       1.00 PQ: 0 ANSI: 6
[ 7590.290039] sd 0:0:0:0: Attached scsi generic sg0 type 0
[ 7590.290603] sd 0:0:0:0: [sda] 60088320 512-byte logical blocks: (30.8 GB/28.7 GiB)
[ 7590.291664] sd 0:0:0:0: [sda] Write Protect is off
[ 7590.291684] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00
[ 7590.292238] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[ 7590.314122] sda: sda1
[ 7590.320675] sd 0:0:0:0: [sda] Attached SCSI removable disk
[ 7590.455314] ISO 9660 Extensions: RRIP_1991A
[ 7590.475735] sdcardfs version 2.0
[ 7590.475742] sdcardfs: dev_name -> /mnt/media_rw/public:8,0
[ 7590.475744] sdcardfs: options -> fsuid=1023,fsgid=1023,mask=6,userid=0,gid=1015
[ 7590.475748] sdcardfs: mnt -> ecaddc10
[ 7590.475796] sdcardfs: mounted on top of /mnt/media_rw/public:8,0 type iso9660
[ 7590.476645] Remount options were mask=23,gid=9997 for vfsmnt e9e9ec10.
[ 7590.476653] sdcardfs : options - debug:1
[ 7590.476656] sdcardfs : options - gid:9997
[ 7590.476658] sdcardfs : options - mask:23
[ 7590.477475] Remount options were mask=7,gid=9997 for vfsmnt c79cf210.
[ 7590.477483] sdcardfs : options - debug:1
[ 7590.477486] sdcardfs : options - gid:9997
[ 7590.477488] sdcardfs : options - mask:7

具体流程 是先由vold 的节点/dev/block/vold/public:8,0 通过sdcardfs执行mount 到 /mnt/media_rw/public:8,0

使用blkid 查看 /dev/block/vold/public:8,0 块设备的信息

console:/system/bin # blkid /dev/block/vold/public:8,0                     
/dev/block/vold/public:8,0: LABEL="PVE" TYPE="iso9660"

这里出来的怎么和上面blkid的不一致呢? 所以可能是vold出现创建设备出现问题

  • 问题结果
    我们查看U盘产生的设备节点
console:/dev/block # ls -l sd*                                                 
brw------- 1 root root 8,   0 2020-08-20 14:12 sda
brw------- 1 root root 8,   1 2020-08-20 14:12 sda1

sda是U盘的磁盘节点
sda1是u盘的分区节点
分别使用blkid看设备节点信息

console:/dev/block # blkid sda                                                 
sda: LABEL="PVE" TYPE="iso9660" 
console:/dev/block # blkid sda1                                                
sda1: UUID="64B4E1BAB4E18EBC" TYPE="ntfs" 

所以可以得到vold 只创建了磁盘节点的public节点,而不是像正常U盘那样创建的是分区节点的public节点
(我不知道这样的表诉是否正确......)

  • vold 流程 android/system/vlod
    1.在插入U盘时,usb驱动会通过uevent事件NetlinkEvent通知 volumeManager
//system/vlod/VolumeManager.cpp
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")?evt->findParam("DEVPATH"):"");
    std::string devType(evt->findParam("DEVTYPE")?evt->findParam("DEVTYPE"):"");

    if (devType != "disk") return;

    int major = std::stoi(evt->findParam("MAJOR"));
    int minor = std::stoi(evt->findParam("MINOR"));
    dev_t device = makedev(major, minor);  //即 /dev/block/sda设备
    switch (evt->getAction()) {
    case NetlinkEvent::Action::kAdd: {
        for (const auto& source : mDiskSources) {
            if (source->matches(eventPath)) {
                // For now, assume that MMC and virtio-blk (the latter is
                // emulator-specific; see Disk.cpp for details) devices are SD,
                // and that everything else is USB
                int flags = source->getFlags();
                if (major == kMajorBlockMmc
                    || (android::vold::IsRunningInEmulator()
                    && major >= (int) kMajorBlockExperimentalMin
                    && major <= (int) kMajorBlockExperimentalMax)) {
                    flags |= android::vold::Disk::Flags::kSd;
                } else {
                    flags |= android::vold::Disk::Flags::kUsb;
                }
                  //创建磁盘节点
                auto disk = new android::vold::Disk(eventPath, device, 
                        source->getNickname(), flags);
                handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));
                break;
            }
        }
        break;
      ...
      ...
}
}
  1. 把device当作disk设备处理

创建 /dev/block/vold/disk:8,0

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);
}
status_t Disk::create() {
    CHECK(!mCreated);
    mCreated = true;

    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskCreated(getId(), mFlags);

    bool read = true;
    
    if (read) {
        readMetadata();  
        readPartitions(); //读分区信息
    }
    return OK;
}

使用命令读分区信息sgdisk --android-dump /dev/block/vold/disk:8,0

status_t Disk::readPartitions() {
    int maxMinors = getMaxMinors();
    if (maxMinors < 0) {
        return -ENOTSUP;
    }

    destroyAllVolumes();

    // Parse partition table

    std::vector<std::string> cmd; //使用命令读取分区信息 
    cmd.push_back(kSgdiskPath);
    cmd.push_back("--android-dump");
    cmd.push_back(mDevPath);

    std::vector<std::string> output;
    status_t res = ForkExecvp(cmd, output);

    if (res != OK) {
        std::string fsType;
        std::string unused;
        if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) != OK) {
            LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
            auto listener = VolumeManager::Instance()->getListener();
            if (listener) listener->onDiskScanned(getId());

            mJustPartitioned = false;
            return res;
        }
        LOG(WARNING) << "sgdisk failed to scan " << mDevPath << ", ReadMetadataUntrusted OK";
    }

    std::string fsType;
    std::string unused;
    if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK && fsType.length() > 0 && unused.length() > 0) //使用blkid读取信息,如果有type uuid 就创建 /dev/block/vold/public:8,0
    {
        LOG(WARNING) << mId << " fsType :: " << fsType << " unused :: " << unused;
        createPublicVolume(mDevice);
    }
    else
    { //否者就读取分区信息

    Table table = Table::kUnknown;
    bool foundParts = false;
    for (const auto& line : output) {
        auto split = android::base::Split(line, kSgdiskToken);
        auto it = split.begin();
        if (it == split.end()) continue;

        if (*it == "DISK") {
            ......
        } else if (*it == "PART") {
            ......
            dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i); // 次设备+1 即代表/dev/block/sda*

            if (table == Table::kMbr) {
                if (++it == split.end()) continue;
                int type = 0;
                if (!android::base::ParseInt("0x" + *it, &type)) {
                    LOG(WARNING) << "Invalid partition type " << *it;
                    continue;
                }

                switch (type) {
                    case 0x06:  // FAT16
                    case 0x07:  // HPFS/NTFS/exFAT
                    case 0x0b:  // W95 FAT32 (LBA)
                    case 0x0c:  // W95 FAT32 (LBA)
                    case 0x0e:  // W95 FAT16 (LBA)
                        createPublicVolume(partDevice); //创建vold的分区节点
                        break;
                    default:
                        createPublicVolume(partDevice);
                        break;
                }
      ......
            } 
   ......
    return OK;
}

static status_t readMetadata(const std::string& path, std::string* fsType,
        std::string* fsUuid, std::string* fsLabel, bool untrusted) {
    ...

    std::vector<std::string> cmd;
    cmd.push_back(kBlkidPath);
    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;
    }
...
}

总结

由上面就可以知道 VolumeManager 首先会传入/dev/block/sda 设备节点当作 disk设备的参数传入。
在disk 中每个磁盘节点都会生成 /dev/block/vold/disk8,0 的节点 ;如果该disk的vold磁盘节点由数据则不会继续读取分区节点。
也不会生成其他分区的public节点。
解决方法 将两者顺序调反,在sgdisk读取分区后,先创建分区vold的节点

问题

  • makedev的工作原理?
  • vold如何通知文件系统来mount节点 ?
  • kernel usb 驱动和VolumeManager如何工作?

本文有许多地方是自己的猜测,不对的地方请指出。

note

主设备号代表类型
major(device) 查看主设备号

static const unsigned int kMajorBlockLoop = 7;
static const unsigned int kMajorBlockScsiA = 8;
static const unsigned int kMajorBlockScsiB = 65;
static const unsigned int kMajorBlockScsiC = 66;
static const unsigned int kMajorBlockScsiD = 67;
static const unsigned int kMajorBlockScsiE = 68;
static const unsigned int kMajorBlockScsiF = 69;
static const unsigned int kMajorBlockScsiG = 70;
static const unsigned int kMajorBlockScsiH = 71;
static const unsigned int kMajorBlockScsiI = 128;
static const unsigned int kMajorBlockScsiJ = 129;
static const unsigned int kMajorBlockScsiK = 130;
static const unsigned int kMajorBlockScsiL = 131;
static const unsigned int kMajorBlockScsiM = 132;
static const unsigned int kMajorBlockScsiN = 133;
static const unsigned int kMajorBlockScsiO = 134;
static const unsigned int kMajorBlockScsiP = 135;
static const unsigned int kMajorBlockMmc = 179;
static const unsigned int kMajorBlockExperimentalMin = 240;
static const unsigned int kMajorBlockExperimentalMax = 254;

参考

__Shadow:Vold工作流程分析学习

原文地址:https://www.cnblogs.com/rootshaw/p/13534194.html