目录
- 概述
- 源码解析
- 1. DmTarget模块
- 2. DeviceMapper模块
- 2.1 DeviceMapper构造函数-打开device-mapper设备
- 2.2 CreateDevice-创建dm设备
- 2.3 GenerateUuid-随机生成uuid数
- 2.4 InitIo-初始化dm_ioctl数据
- 2.5 LoadTableAndActivate-传table数据下去
- 2.6 GetDeviceUniquePath-获取dm设备的uuid,看dm设备是否创建成功了
- 2.7 GetDmDevicePathByName-获取dm设备,看dm设备是否创建成功
- 2.8 GetAvailableDevices-DM_LIST_DEVICES获取已经创建的设备
- 2.9 DeleteDevice-删除设备
- 3. DmTable模块
- 问题
- 补充
- 参考
概述
android/system/core/fs_mgr/libdm和dm设备的创建
源码解析
1. DmTarget模块
1.1 DmTargetLinear构造函数
DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
uint64_t physical_sector)
// 开始,长度,/dev/block/mmcblk,物理扇区起始数
: DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
1.2 DmTarget构造函数
DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
1.3 Serialize
std::string DmTarget::Serialize() const {
// Create a string containing a dm_target_spec, parameter data, and an
// explicit null terminator.
std::string data(sizeof(dm_target_spec), '\0');
// dm_target_spec + /dev/block/mmcblk0p05 1234
data += GetParameterString();
data.push_back('\0');
// The kernel expects each target to be 8-byte aligned.
size_t padding = DM_ALIGN(data.size()) - data.size();
for (size_t i = 0; i < padding; i++) {
data.push_back('\0');
}
// Finally fill in the dm_target_spec.
struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
spec->sector_start = start();
spec->length = size();
snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
spec->next = (uint32_t)data.size();
return data;
}
struct dm_target_spec {
__u64 sector_start;
__u64 length;
__s32 status;
__u32 next;
char target_type[DM_MAX_TYPE_NAME];
};
1.4 GetParameterString-/dev/block/mmcblk0p05-1234
std::string DmTargetLinear::GetParameterString() const {
return block_device_ + " " + std::to_string(physical_sector_);
}
2. DeviceMapper模块
2.1 DeviceMapper构造函数-打开device-mapper设备
DeviceMapper::DeviceMapper() : fd_(-1) {
// 打开/dev/device-mapper设备
fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd_ < 0) {
PLOG(ERROR) << "Failed to open device-mapper";
}
}
DeviceMapper& DeviceMapper::Instance() {
static DeviceMapper instance;
return instance;
}
2.2 CreateDevice-创建dm设备
// system_a,table,path是要传回去的,/sys/devices/virtual/block/dm-0/dm可以看到
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
const std::chrono::milliseconds& timeout_ms) {
// 1. 生成uuid
std::string uuid = GenerateUuid();
// 2. 通过ioctl函数创建DM_DEV_CREATE设备
if (!CreateDevice(name, uuid)) {
return false;
}
// We use the unique path for testing whether the device is ready. After
// that, it's safe to use the dm-N path which is compatible with callers
// that expect it to be formatted as such.
std::string unique_path;
// 1. 加载dm table
// 2. 获取设备文件/dev/block/mapper/by-uuid/uuid
// 3. 获取设备文件/dev/block/dm-0
if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
!GetDmDevicePathByName(name, path)) {
DeleteDevice(name);
return false;
}
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
}
if (IsRecovery()) {
bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
if (non_ab_device && sdk && sdk <= 29) {
LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
unique_path = *path;
}
}
if (!WaitForFile(unique_path, timeout_ms)) {
LOG(ERROR) << "Failed waiting for device path: " << unique_path;
DeleteDevice(name);
return false;
}
return true;
}
bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
if (name.empty()) {
LOG(ERROR) << "Unnamed device mapper device creation is not supported";
return false;
}
if (name.size() >= DM_NAME_LEN) {
LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
return false;
}
struct dm_ioctl io;
InitIo(&io, name);
if (!uuid.empty()) {
snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
}
// 创建dm设备
if (ioctl(fd_, DM_DEV_CREATE, &io)) {
PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
return false;
}
// Check to make sure the newly created device doesn't already have targets
// added or opened by someone
CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
// Creates a new device mapper device with the name passed in
return true;
}
2.3 GenerateUuid-随机生成uuid数
static std::string GenerateUuid() {
uuid_t uuid_bytes;
// 通过/dev/urandom生成随机数
uuid_generate(uuid_bytes);
char uuid_chars[37] = {};
// 将二进制格式转成36-byte字符串的类型
uuid_unparse_lower(uuid_bytes, uuid_chars);
return std::string{uuid_chars};
}
2.4 InitIo-初始化dm_ioctl数据
void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
memset(io, 0, sizeof(*io));
io->version[0] = DM_VERSION0;
io->version[1] = DM_VERSION1;
io->version[2] = DM_VERSION2;
io->data_size = sizeof(*io);
io->data_start = 0;
if (!name.empty()) {
snprintf(io->name, sizeof(io->name), "%s", name.c_str());
}
}
2.5 LoadTableAndActivate-传table数据下去
// system_a table
bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
// 建成serialize
ioctl_buffer += table.Serialize();
struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
InitIo(io, name);
io->data_size = ioctl_buffer.size();
io->data_start = sizeof(struct dm_ioctl);
io->target_count = static_cast<uint32_t>(table.num_targets());
if (table.readonly()) {
io->flags |= DM_READONLY_FLAG;
}// 设成DM_TABLE_LOAD
if (ioctl(fd_, DM_TABLE_LOAD, io)) {
PLOG(ERROR) << "DM_TABLE_LOAD failed";
return false;
}
InitIo(io, name);
// 设成DM_DEV_SUSPEND
if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
return false;
}
return true;
}
2.6 GetDeviceUniquePath-获取dm设备的uuid,看dm设备是否创建成功了
bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
struct dm_ioctl io;
InitIo(&io, name);
if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
PLOG(ERROR) << "Failed to get device path: " << name;
return false;
}
if (io.uuid[0] == '\0') {
LOG(ERROR) << "Device does not have a unique path: " << name;
return false;
}
*path = "/dev/block/mapper/by-uuid/"s + io.uuid;
return true;
}
2.7 GetDmDevicePathByName-获取dm设备,看dm设备是否创建成功
bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
struct dm_ioctl io;
InitIo(&io, name);
if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
return false;
}
uint32_t dev_num = minor(io.dev);
*path = "/dev/block/dm-" + std::to_string(dev_num);
return true;
}
2.8 GetAvailableDevices-DM_LIST_DEVICES获取已经创建的设备
bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
devices->clear();
// calculate the space needed to read a maximum of 256 targets, each with
// name with maximum length of 16 bytes
// 分配buffer
uint32_t payload_size = sizeof(struct dm_name_list);
// 128-bytes for the name
payload_size += DM_NAME_LEN;
// dm wants every device spec to be aligned at 8-byte boundary
payload_size = DM_ALIGN(payload_size);
payload_size *= kMaxPossibleDmDevices;
uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
// 分配buffer
auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
if (buffer == nullptr) {
LOG(ERROR) << "failed to allocate memory";
return false;
}
// Sets appropriate data size and data_start to make sure we tell kernel
// about the total size of the buffer we are passing and where to start
// writing the list of targets.
struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
InitIo(io);
io->data_size = data_size;
io->data_start = sizeof(*io);
// 获取dm设备信息
if (ioctl(fd_, DM_LIST_DEVICES, io)) {
PLOG(ERROR) << "DM_LIST_DEVICES failed";
return false;
}
// If the provided buffer wasn't enough to list all devices any data
// beyond sizeof(*io) must not be read.
if (io->flags & DM_BUFFER_FULL_FLAG) {
LOG(INFO) << data_size << " is not enough memory to list all dm devices";
return false;
}
// if there are no devices created yet, return success with empty vector
if (io->data_size == sizeof(*io)) {
return true;
}
// Parse each device and add a new DmBlockDevice to the vector
// created from the kernel data.
uint32_t next = sizeof(*io);
data_size = io->data_size - next;
struct dm_name_list* dm_dev =
reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
while (next && data_size) {
// dm设备信息
devices->emplace_back((dm_dev));
if (dm_dev->next == 0) {
break;
}
next += dm_dev->next;
data_size -= dm_dev->next;
dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
}
return true;
}
2.9 DeleteDevice-删除设备
bool DeviceMapper::DeleteDevice(const std::string& name,
const std::chrono::milliseconds& timeout_ms) {
std::string unique_path;
if (!GetDeviceUniquePath(name, &unique_path)) {
LOG(ERROR) << "Failed to get unique path for device " << name;
}
struct dm_ioctl io;
InitIo(&io, name);
// 删除设备
if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
return false;
}
// Check to make sure appropriate uevent is generated so ueventd will
// do the right thing and remove the corresponding device node and symlinks.
CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
<< "Didn't generate uevent for [" << name << "] removal";
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
}
if (unique_path.empty()) {
return false;
}
if (!WaitForFileDeleted(unique_path, timeout_ms)) {
LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
return false;
}
return true;
}
bool DeviceMapper::DeleteDevice(const std::string& name) {
return DeleteDevice(name, 0ms);
}
3. DmTable模块
3.1 Serialize
std::string DmTable::Serialize() const {
if (!valid()) {
return "";
}
std::string table;
for (const auto& target : targets_) {
table += target->Serialize();
}
return table;
}