• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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