起因:客户出现一个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;
...
...
}
}
- 把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;