1 /*
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <dirent.h>
17 #include <sys/stat.h>
18 #include <sys/sysmacros.h>
19
20 #include "ipc/storage_manager_client.h"
21 #include "storage_service_errno.h"
22 #include "storage_service_log.h"
23 #include "utils/disk_utils.h"
24 #include "utils/file_utils.h"
25 #include "utils/storage_radar.h"
26 #include "utils/string_utils.h"
27 #include "volume/volume_manager.h"
28
29 namespace OHOS {
30 namespace StorageDaemon {
31 constexpr int32_t MIN_LINES = 32;
32 constexpr int32_t VOL_LENGTH = 3;
33 constexpr int32_t MAJORID_BLKEXT = 259;
34 constexpr int32_t MAX_PARTITION = 16;
35 constexpr int32_t MAX_INTERVAL_PARTITION = 15;
36 constexpr int32_t PREFIX_LENGTH = 2;
37 constexpr int32_t HEX_SHIFT_BITS = 4;
38 constexpr int32_t HEX_LETTER_OFFSET = 10;
39 constexpr const char *SGDISK_PATH = "/system/bin/sgdisk";
40 constexpr const char *SGDISK_DUMP_CMD = "--ohos-dump";
41 constexpr const char *SGDISK_ZAP_CMD = "--zap-all";
42 constexpr const char *SGDISK_PART_CMD = "--new=0:0:-0 --typeconde=0:0c00 --gpttombr=1";
43 constexpr const char *BLOCK_PATH = "/dev/block";
44
45 enum DiskStatus:int {
46 S_INITAL = 0,
47 S_CREATE = 1,
48 S_SCAN = 2,
49 S_DESTROY = 4,
50 };
51
52 std::map<uint32_t, std::string> vendorMap_ = {
53 {0x000003, "SanDisk"},
54 {0x00001b, "SamSung"},
55 {0x000028, "Lexar"},
56 {0x000074, "Transcend"}
57 };
58
DiskInfo(std::string & sysPath,std::string & devPath,dev_t device,int flag)59 DiskInfo::DiskInfo(std::string &sysPath, std::string &devPath, dev_t device, int flag)
60 {
61 id_ = StringPrintf("disk-%d-%d", major(device), minor(device));
62 sysPath_ = sysPath;
63 eventPath_ = devPath;
64 devPath_ = StringPrintf("/dev/block/%s", id_.c_str());
65 device_ = device;
66 flags_ = static_cast<unsigned int>(flag);
67 status = S_INITAL;
68 isUserdata = false;
69 sgdiskLines_ = std::vector<std::string>();
70 }
71
GetDevice() const72 dev_t DiskInfo::GetDevice() const
73 {
74 return device_;
75 }
76
GetId() const77 std::string DiskInfo::GetId() const
78 {
79 return id_;
80 }
81
GetDevPath() const82 std::string DiskInfo::GetDevPath() const
83 {
84 return devPath_;
85 }
86
GetDevDSize() const87 uint64_t DiskInfo::GetDevDSize() const
88 {
89 return size_;
90 }
91
GetSysPath() const92 std::string DiskInfo::GetSysPath() const
93 {
94 return sysPath_;
95 }
96
GetDevVendor() const97 std::string DiskInfo::GetDevVendor() const
98 {
99 return vendor_;
100 }
101
GetDevFlag() const102 int DiskInfo::GetDevFlag() const
103 {
104 return flags_;
105 }
106
~DiskInfo()107 DiskInfo::~DiskInfo()
108 {
109 DestroyDiskNode(devPath_);
110 }
111
Create()112 int DiskInfo::Create()
113 {
114 int ret;
115
116 CreateDiskNode(devPath_, device_);
117 status = S_CREATE;
118 ReadMetadata();
119
120 StorageManagerClient client;
121 ret = client.NotifyDiskCreated(*this);
122 if (ret != E_OK) {
123 LOGE("Notify Disk Created failed");
124 return ret;
125 }
126
127 ret = ReadPartition();
128 if (ret != E_OK) {
129 LOGE("Create disk failed");
130 return ret;
131 }
132
133 return E_OK;
134 }
135
Destroy()136 int DiskInfo::Destroy()
137 {
138 auto &volume = VolumeManager::Instance();
139
140 for (auto volumeId : volumeId_) {
141 auto ret = volume.DestroyVolume(volumeId);
142 if (ret != E_OK) {
143 LOGE("Destroy volume %{public}s failed", volumeId.c_str());
144 return E_ERR;
145 }
146 }
147 status = S_DESTROY;
148 volumeId_.clear();
149 return E_OK;
150 }
151
ReadMetadata()152 void DiskInfo::ReadMetadata()
153 {
154 size_ = -1;
155 vendor_.clear();
156 if (GetDevSize(devPath_, &size_) != E_OK) {
157 size_ = -1;
158 }
159
160 unsigned int majorId = major(device_);
161 if (majorId == DISK_MMC_MAJOR) {
162 std::string path(sysPath_ + "/device/manfid");
163 std::string str;
164 if (!ReadFile(path, &str)) {
165 LOGE("open file %{public}s failed", path.c_str());
166 return;
167 }
168 LOGI("Raw manfid value from file: %{public}s", str.c_str());
169 uint32_t manfid = 0;
170 bool is_valid = ParseAndValidateManfid(str, manfid);
171 if (is_valid) {
172 auto it = vendorMap_.find(manfid);
173 vendor_ = (it != vendorMap_.end()) ? it->second : "Unknown";
174 } else {
175 LOGI("Invalid manfid: %{public}s", str.c_str());
176 vendor_ = "Invalid";
177 }
178 } else {
179 std::string path(sysPath_ + "/device/vendor");
180 std::string str;
181 if (!ReadFile(path, &str)) {
182 LOGE("open file %{public}s failed", path.c_str());
183 return;
184 }
185 vendor_ = str;
186 LOGI("Read metadata %{public}s", path.c_str());
187 }
188 }
189
ParseAndValidateManfid(const std::string & str,uint32_t & manfid)190 bool DiskInfo::ParseAndValidateManfid(const std::string& str, uint32_t& manfid)
191 {
192 std::string trimmed = str;
193 size_t start = trimmed.find_first_not_of(" \t\n\r");
194 if (start != std::string::npos) {
195 trimmed.erase(0, start);
196 } else {
197 return false;
198 }
199 size_t end = trimmed.find_last_not_of(" \t\n\r");
200 trimmed.erase(std::min(end + 1, trimmed.size()));
201
202 const char* p = trimmed.c_str();
203 if (trimmed.size() > PREFIX_LENGTH && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
204 p += PREFIX_LENGTH;
205 }
206 manfid = 0;
207 while (*p) {
208 char c = *p++;
209 if (c >= '0' && c <= '9') {
210 manfid = (manfid << HEX_SHIFT_BITS) | (c - '0');
211 } else if (c >= 'A' && c <= 'F') {
212 manfid = (manfid << HEX_SHIFT_BITS) | (c - 'A' + HEX_LETTER_OFFSET);
213 } else if (c >= 'a' && c <= 'f') {
214 manfid = (manfid << HEX_SHIFT_BITS) | (c - 'a' + HEX_LETTER_OFFSET);
215 } else {
216 return false;
217 }
218 }
219 return true;
220 }
221
ReadPartition()222 int DiskInfo::ReadPartition()
223 {
224 int maxVolumes = GetMaxVolume(device_);
225 if (maxVolumes < 0) {
226 LOGE("Invaild maxVolumes: %{public}d", maxVolumes);
227 return E_ERR;
228 }
229
230 std::vector<std::string> output;
231 std::vector<std::string> lines;
232 std::vector<std::string> cmd = {SGDISK_PATH, SGDISK_DUMP_CMD, devPath_};
233 int res = ForkExec(cmd, &output);
234 FilterOutput(lines, output);
235 if (res != E_OK || lines.empty()) {
236 int destroyRes = Destroy();
237 sgdiskLines_.clear();
238 LOGE("get partition failed, destroy error is %{public}d", destroyRes);
239 return res;
240 }
241 isUserdata = false;
242 if (lines.size() > MIN_LINES) {
243 auto userdataIt = std::find_if(lines.begin(), lines.end(), [](const std::string &str) {
244 return str.find("userdata") != std::string::npos;
245 });
246 if (userdataIt != lines.end()) {
247 isUserdata = true;
248 std::vector<std::string> hmfsLines;
249 hmfsLines.push_back(lines.front());
250 hmfsLines.push_back(*userdataIt);
251 status = S_SCAN;
252 return ReadDiskLines(hmfsLines, maxVolumes, isUserdata);
253 }
254 }
255 status = S_SCAN;
256 std::sort(lines.begin() + 1, lines.end());
257 if (sgdiskLines_.empty()) {
258 sgdiskLines_ = lines;
259 } else {
260 ProcessPartitionChanges(lines, maxVolumes, isUserdata);
261 return E_OK;
262 }
263 return ReadDiskLines(sgdiskLines_, maxVolumes, isUserdata);
264 }
265
FilterOutput(std::vector<std::string> & lines,std::vector<std::string> & output)266 void DiskInfo::FilterOutput(std::vector<std::string> &lines, std::vector<std::string> &output)
267 {
268 std::string bufToken = "\n";
269 for (auto &buf : output) {
270 auto split = SplitLine(buf, bufToken);
271 lines.insert(lines.end(), split.begin(), split.end());
272 }
273 }
274
ProcessPartitionChanges(const std::vector<std::string> & lines,int maxVolumes,bool isUserdata)275 void DiskInfo::ProcessPartitionChanges(const std::vector<std::string>& lines, int maxVolumes, bool isUserdata)
276 {
277 std::vector<std::string> addedLines;
278 std::vector<std::string> removedLines;
279 std::set_difference(
280 lines.begin(), lines.end(),
281 sgdiskLines_.begin(), sgdiskLines_.end(),
282 std::back_inserter(addedLines)
283 );
284 std::set_difference(
285 sgdiskLines_.begin(), sgdiskLines_.end(),
286 lines.begin(), lines.end(),
287 std::back_inserter(removedLines)
288 );
289
290 if (!addedLines.empty()) {
291 std::vector<std::string> SDLines;
292 SDLines.reserve(addedLines.size() + 1);
293 SDLines.push_back(lines.front());
294 SDLines.insert(SDLines.end(), addedLines.begin(), addedLines.end());
295 sgdiskLines_ = lines;
296 if (ReadDiskLines(SDLines, maxVolumes, isUserdata) != E_OK) {
297 LOGI("Failed to read disk lines ");
298 }
299 }
300 if (!removedLines.empty()) {
301 UmountLines(removedLines, maxVolumes, isUserdata);
302 sgdiskLines_.clear();
303 sgdiskLines_ = lines;
304 }
305 }
306
UmountLines(std::vector<std::string> lines,int32_t maxVols,bool isUserdata)307 void DiskInfo::UmountLines(std::vector<std::string> lines, int32_t maxVols, bool isUserdata)
308 {
309 std::string lineToken = " ";
310 for (auto &line : lines) {
311 auto split = SplitLine(line, lineToken);
312 auto it = split.begin();
313 if (it == split.end()) {
314 continue;
315 }
316
317 if (*it == "PART") {
318 if (++it == split.end()) {
319 continue;
320 }
321 dev_t partitionDev = ProcessPartition(it, maxVols, isUserdata);
322 if (partitionDev == makedev(0, 0)) {
323 continue;
324 }
325 std::string volumeId = StringPrintf("vol-%u-%u", major(partitionDev), minor(partitionDev));
326 auto ret = VolumeManager::Instance().DestroyVolume(volumeId);
327 if (ret != E_OK) {
328 LOGE("Destroy volume %{public}s failed", volumeId.c_str());
329 }
330 }
331 }
332 }
333
CreateMBRVolume(int32_t type,dev_t dev)334 bool DiskInfo::CreateMBRVolume(int32_t type, dev_t dev)
335 {
336 // FAT16 || NTFS/EXFAT || W95 FAT32 || W95 FAT32 || W95 FAT16 || EFI FAT32 || EXT 2/3/4
337 if (type == 0x06 || type == 0x07 || type == 0x0b || type == 0x0c || type == 0x0e || type == 0x1b || type == 0x83) {
338 if (CreateVolume(dev) == E_OK) {
339 return true;
340 }
341 }
342 return false;
343 }
344
CreateUnknownTabVol()345 int32_t DiskInfo::CreateUnknownTabVol()
346 {
347 LOGI("%{public}s has unknown table", id_.c_str());
348 std::string fsType;
349 std::string uuid;
350 std::string label;
351 auto ret = OHOS::StorageDaemon::ReadMetadata(devPath_, fsType, uuid, label);
352 if (ret == E_OK) {
353 CreateVolume(device_);
354 } else {
355 StorageService::StorageRadar::ReportUserManager("DiskInfo::CreateUnknownTabVol::ReadMetadata", 0,
356 ret, "devPath_=" + devPath_);
357 LOGE("failed to identify the disk device");
358 return E_NON_EXIST;
359 }
360 return E_OK;
361 }
362
ReadDiskLines(std::vector<std::string> lines,int32_t maxVols,bool isUserdata)363 int32_t DiskInfo::ReadDiskLines(std::vector<std::string> lines, int32_t maxVols, bool isUserdata)
364 {
365 std::string lineToken = " ";
366 bool foundPart = false;
367 Table table = Table::UNKNOWN;
368 for (auto &line : lines) {
369 auto split = SplitLine(line, lineToken);
370 auto it = split.begin();
371 if (it == split.end()) {
372 continue;
373 }
374
375 if (*it == "DISK") {
376 if (++it == split.end()) {
377 continue;
378 }
379 if (*it == "mbr") {
380 table = Table::MBR;
381 } else if (*it == "gpt") {
382 table = Table::GPT;
383 } else {
384 LOGI("Unknown partition table %{public}s", (*it).c_str());
385 continue;
386 }
387 } else if (*it == "PART") {
388 if (++it == split.end()) {
389 continue;
390 }
391 dev_t partitionDev = ProcessPartition(it, maxVols, isUserdata);
392 if (partitionDev == makedev(0, 0)) {
393 continue;
394 }
395 CreateTableVolume(it, split.end(), table, foundPart, partitionDev);
396 }
397 }
398
399 if (table == Table::UNKNOWN || !foundPart) {
400 return CreateUnknownTabVol();
401 }
402
403 return E_OK;
404 }
405
ProcessPartition(std::vector<std::string>::iterator & it,int32_t maxVols,bool isUserdata)406 dev_t DiskInfo::ProcessPartition(std::vector<std::string>::iterator &it, int32_t maxVols, bool isUserdata)
407 {
408 int32_t index = std::atoi((*it).c_str());
409 unsigned int majorId = major(device_);
410 if ((index > maxVols && majorId == DISK_MMC_MAJOR) || index < 1) {
411 LOGE("Invalid partition %{public}d", index);
412 return makedev(0, 0);
413 }
414 dev_t partitionDev = makedev(0, 0);
415 if (isUserdata) {
416 int32_t maxMinor = GetMaxMinor(MAJORID_BLKEXT);
417 if (maxMinor == -1) {
418 partitionDev = makedev(MAJORID_BLKEXT, static_cast<uint32_t>(index) - MAX_PARTITION);
419 } else {
420 partitionDev = makedev(MAJORID_BLKEXT, static_cast<uint32_t>(maxMinor) +
421 static_cast<uint32_t>(index) - MAX_INTERVAL_PARTITION);
422 }
423 } else {
424 if (index > MAX_SCSI_VOLUMES) {
425 partitionDev = makedev(MAJORID_BLKEXT, static_cast<uint32_t>(index) - MAX_PARTITION);
426 } else {
427 partitionDev = makedev(major(device_), minor(device_) + static_cast<uint32_t>(index));
428 }
429 }
430 return partitionDev;
431 }
432
GetMaxMinor(int32_t major)433 int32_t DiskInfo::GetMaxMinor(int32_t major)
434 {
435 DIR* dir;
436 struct dirent* entry;
437 int32_t maxMinor = -1;
438 if ((dir = opendir(BLOCK_PATH)) == nullptr) {
439 LOGE("fail to open %{public}s", BLOCK_PATH);
440 return E_ERR;
441 }
442 while ((entry = readdir(dir)) != nullptr) {
443 if (entry->d_name[0] == '.' || strncmp(entry->d_name, "vol", VOL_LENGTH) != 0) {
444 continue;
445 }
446 std::string devicePath = std::string(BLOCK_PATH) + "/" + entry->d_name;
447 struct stat statbuf;
448 if (stat(devicePath.c_str(), &statbuf) == 0) {
449 int32_t majorNum = static_cast<int32_t>major(statbuf.st_rdev);
450 int32_t minorNum = static_cast<int32_t>minor(statbuf.st_rdev);
451
452 if (majorNum == major) {
453 maxMinor = minorNum > maxMinor ? minorNum : maxMinor;
454 }
455 }
456 }
457 closedir(dir);
458 return maxMinor;
459 }
460
CreateTableVolume(std::vector<std::string>::iterator & it,const std::vector<std::string>::iterator & end,Table table,bool & foundPart,dev_t partitionDev)461 void DiskInfo::CreateTableVolume(std::vector<std::string>::iterator &it, const std::vector<std::string>::iterator &end,
462 Table table, bool &foundPart, dev_t partitionDev)
463 {
464 if (table == Table::MBR) {
465 if (++it == end) {
466 return;
467 }
468 int32_t type = std::stoi("0x0" + *it, 0, 16);
469 if (CreateMBRVolume(type, partitionDev)) {
470 foundPart = true;
471 } else {
472 LOGE("Create MBR Volume failed");
473 }
474 } else if (table == Table::GPT) {
475 if (CreateVolume(partitionDev) == E_OK) {
476 foundPart = true;
477 }
478 }
479 }
480
CreateVolume(dev_t dev)481 int DiskInfo::CreateVolume(dev_t dev)
482 {
483 auto &volume = VolumeManager::Instance();
484
485 LOGI("disk read volume metadata");
486 std::string volumeId = volume.CreateVolume(GetId(), dev, isUserdata);
487 if (volumeId.empty()) {
488 LOGE("Create volume failed");
489 return E_ERR;
490 }
491
492 volumeId_.push_back(volumeId);
493 return E_OK;
494 }
495
Partition()496 int DiskInfo::Partition()
497 {
498 LOGI("Partitioning the disk.");
499 std::vector<std::string> cmd;
500 int res;
501
502 res = Destroy();
503 if (res != E_OK) {
504 LOGE("Destroy failed in Partition()");
505 }
506
507 cmd.push_back(SGDISK_PATH);
508 cmd.push_back(SGDISK_ZAP_CMD);
509 cmd.push_back(devPath_);
510 LOGI("Partition executing command.");
511 std::vector<std::string> output;
512 res = ForkExec(cmd, &output);
513 if (res != E_OK) {
514 LOGE("sgdisk: zap fail");
515 return res;
516 }
517
518 cmd.clear();
519 output.clear();
520 cmd.push_back(SGDISK_PATH);
521 cmd.push_back(SGDISK_PART_CMD);
522 cmd.push_back(devPath_);
523 res = ForkExec(cmd, &output);
524 if (res != E_OK) {
525 LOGE("sgdisk: partition fail");
526 return res;
527 }
528
529 return E_OK;
530 }
531 } // namespace STORAGE_DAEMON
532 } // namespace OHOS
533