• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/string_utils.h"
26 #include "volume/volume_manager.h"
27 
28 namespace OHOS {
29 namespace StorageDaemon {
30 constexpr int32_t MIN_LINES = 2;
31 constexpr int32_t VOL_LENGTH = 3;
32 constexpr int32_t MAJORID_BLKEXT = 259;
33 constexpr int32_t MAX_PARTITION = 16;
34 constexpr int32_t MAX_INTERVAL_PARTITION = 15;
35 constexpr const char *SGDISK_PATH = "/system/bin/sgdisk";
36 constexpr const char *SGDISK_DUMP_CMD = "--ohos-dump";
37 constexpr const char *SGDISK_ZAP_CMD = "--zap-all";
38 constexpr const char *SGDISK_PART_CMD = "--new=0:0:-0 --typeconde=0:0c00 --gpttombr=1";
39 constexpr const char *BLOCK_PATH = "/dev/block";
40 
41 enum DiskStatus:int {
42     S_INITAL = 0,
43     S_CREATE = 1,
44     S_SCAN = 2,
45     S_DESTROY = 4,
46 };
47 
DiskInfo(std::string & sysPath,std::string & devPath,dev_t device,int flag)48 DiskInfo::DiskInfo(std::string &sysPath, std::string &devPath, dev_t device, int flag)
49 {
50     id_ = StringPrintf("disk-%d-%d", major(device), minor(device));
51     sysPath_ = sysPath;
52     eventPath_ = devPath;
53     devPath_ = StringPrintf("/dev/block/%s", id_.c_str());
54     device_ = device;
55     flags_ = static_cast<unsigned int>(flag);
56     status = S_INITAL;
57 }
58 
GetDevice() const59 dev_t DiskInfo::GetDevice() const
60 {
61     return device_;
62 }
63 
GetId() const64 std::string DiskInfo::GetId() const
65 {
66     return id_;
67 }
68 
GetDevPath() const69 std::string DiskInfo::GetDevPath() const
70 {
71     return devPath_;
72 }
73 
GetDevDSize() const74 uint64_t DiskInfo::GetDevDSize() const
75 {
76     return size_;
77 }
78 
GetSysPath() const79 std::string DiskInfo::GetSysPath() const
80 {
81     return sysPath_;
82 }
83 
GetDevVendor() const84 std::string DiskInfo::GetDevVendor() const
85 {
86     return vendor_;
87 }
88 
GetDevFlag() const89 int DiskInfo::GetDevFlag() const
90 {
91     return flags_;
92 }
93 
~DiskInfo()94 DiskInfo::~DiskInfo()
95 {
96     DestroyDiskNode(devPath_);
97 }
98 
Create()99 int DiskInfo::Create()
100 {
101     int ret;
102 
103     CreateDiskNode(devPath_, device_);
104     status = S_CREATE;
105     ReadMetadata();
106 
107     StorageManagerClient client;
108     ret = client.NotifyDiskCreated(*this);
109     if (ret != E_OK) {
110         LOGE("Notify Disk Created failed");
111         return ret;
112     }
113 
114     ret = ReadPartition();
115     if (ret != E_OK) {
116         LOGE("Create disk failed");
117         return ret;
118     }
119 
120     return E_OK;
121 }
122 
Destroy()123 int DiskInfo::Destroy()
124 {
125     auto volume = VolumeManager::Instance();
126 
127     for (auto volumeId : volumeId_) {
128         auto ret = volume->DestroyVolume(volumeId);
129         if (ret != E_OK) {
130             LOGE("Destroy volume %{public}s failed", volumeId.c_str());
131             return E_ERR;
132         }
133     }
134     status = S_DESTROY;
135     volumeId_.clear();
136     return E_OK;
137 }
138 
ReadMetadata()139 void DiskInfo::ReadMetadata()
140 {
141     size_ = -1;
142     vendor_.clear();
143     if (GetDevSize(devPath_, &size_) != E_OK) {
144         size_ = -1;
145     }
146 
147     unsigned int majorId = major(device_);
148     if (majorId == DISK_MMC_MAJOR) {
149         std::string path(sysPath_ + "/device/manfid");
150         std::string str;
151         if (!ReadFile(path, &str)) {
152             LOGE("open file %{public}s failed", path.c_str());
153             return;
154         }
155         int manfid = std::stoi(str);
156         switch (manfid) {
157             case 0x000003: {
158                 vendor_ = "SanDisk";
159                 break;
160             }
161             case 0x00001b: {
162                 vendor_ = "SamSung";
163                 break;
164             }
165             case 0x000028: {
166                 vendor_ = "Lexar";
167                 break;
168             }
169             case 0x000074: {
170                 vendor_ = "Transcend";
171                 break;
172             }
173             default : {
174                 vendor_ = "Unknown";
175                 LOGI("Unknown vendor information: %{public}d", manfid);
176                 break;
177             }
178         }
179     } else {
180         std::string path(sysPath_ + "/device/vendor");
181         std::string str;
182         if (!ReadFile(path, &str)) {
183             LOGE("open file %{public}s failed", path.c_str());
184             return;
185         }
186         vendor_ = str;
187         LOGI("Read metadata %{public}s", path.c_str());
188     }
189 }
190 
ReadPartition()191 int DiskInfo::ReadPartition()
192 {
193     int maxVolumes = GetMaxVolume(device_);
194     if (maxVolumes < 0) {
195         LOGE("Invaild maxVolumes: %{public}d", maxVolumes);
196         return E_ERR;
197     }
198     int res = Destroy();
199     if (res != E_OK) {
200         LOGE("Destroy failed in ReadPartition");
201     }
202 
203     std::vector<std::string> cmd;
204     std::vector<std::string> output;
205     std::vector<std::string> lines;
206 
207     cmd.push_back(SGDISK_PATH);
208     cmd.push_back(SGDISK_DUMP_CMD);
209     cmd.push_back(devPath_);
210     res = ForkExec(cmd, &output);
211     if (res != E_OK) {
212         LOGE("get %{private}s partition failed", devPath_.c_str());
213         return res;
214     }
215     std::string bufToken = "\n";
216     for (auto &buf : output) {
217         auto split = SplitLine(buf, bufToken);
218         for (auto &tmp : split)
219             lines.push_back(tmp);
220     }
221 
222     if (lines.size() > MIN_LINES) {
223         auto userdataIt = std::find_if(lines.begin(), lines.end(), [](const std::string &str) {
224             return str.find("userdata") != std::string::npos;
225         });
226         if (userdataIt != lines.end()) {
227             std::vector<std::string> hmfsLines;
228             hmfsLines.push_back(lines.front());
229             hmfsLines.push_back(*userdataIt);
230             status = S_SCAN;
231             return ReadDiskLines(hmfsLines, maxVolumes);
232         }
233     }
234     status = S_SCAN;
235     return ReadDiskLines(lines, maxVolumes);
236 }
237 
CreateMBRVolume(int32_t type,dev_t dev)238 bool DiskInfo::CreateMBRVolume(int32_t type, dev_t dev)
239 {
240     // FAT16 || NTFS/EXFAT || W95 FAT32 || W95 FAT32 || W95 FAT16 || EFI FAT32 || EXT 2/3/4
241     if (type == 0x06 || type == 0x07 || type == 0x0b || type == 0x0c || type == 0x0e || type == 0x1b || type == 0x83) {
242         if (CreateVolume(dev) == E_OK) {
243             return true;
244         }
245     }
246     return false;
247 }
248 
CreateUnknownTabVol()249 int32_t DiskInfo::CreateUnknownTabVol()
250 {
251     LOGI("%{public}s has unknown table", id_.c_str());
252     std::string fsType;
253     std::string uuid;
254     std::string label;
255     if (OHOS::StorageDaemon::ReadMetadata(devPath_, fsType, uuid, label) == E_OK) {
256         CreateVolume(device_);
257     } else {
258         LOGE("failed to identify the disk device");
259         return E_NON_EXIST;
260     }
261     return E_OK;
262 }
263 
ReadDiskLines(std::vector<std::string> lines,int32_t maxVols)264 int32_t DiskInfo::ReadDiskLines(std::vector<std::string> lines, int32_t maxVols)
265 {
266     std::string lineToken = " ";
267     bool foundPart = false;
268     Table table = Table::UNKNOWN;
269     for (auto &line : lines) {
270         auto split = SplitLine(line, lineToken);
271         auto it = split.begin();
272         if (it == split.end()) {
273             continue;
274         }
275 
276         if (*it == "DISK") {
277             if (++it == split.end()) {
278                 continue;
279             }
280             if (*it == "mbr") {
281                 table = Table::MBR;
282             } else if (*it == "gpt") {
283                 table = Table::GPT;
284             } else {
285                 LOGI("Unknown partition table %{public}s", (*it).c_str());
286                 continue;
287             }
288         } else if (*it == "PART") {
289             ProcessPartition(it, split.end(), table, maxVols, foundPart);
290         }
291     }
292 
293     if (table == Table::UNKNOWN || !foundPart) {
294         return CreateUnknownTabVol();
295     }
296 
297     return E_OK;
298 }
299 
ProcessPartition(std::vector<std::string>::iterator & it,const std::vector<std::string>::iterator & end,Table table,int32_t maxVols,bool & foundPart)300 void DiskInfo::ProcessPartition(std::vector<std::string>::iterator &it, const std::vector<std::string>::iterator &end,
301                                 Table table, int32_t maxVols, bool &foundPart)
302 {
303     if (++it == end) {
304         return;
305     }
306     int32_t index = std::stoi(*it);
307     unsigned int majorId = major(device_);
308     if ((index > maxVols && majorId == DISK_MMC_MAJOR) || index < 1) {
309         LOGE("Invalid partition %{public}d", index);
310         return;
311     }
312     dev_t partitionDev;
313     if (index > MAX_SCSI_VOLUMES) {
314         int32_t maxMinor = GetMaxMinor(MAJORID_BLKEXT);
315         if (maxMinor == -1 && minor(device_) == 0) {
316             partitionDev = makedev(MAJORID_BLKEXT, minor(device_) + static_cast<uint32_t>(index) - MAX_PARTITION);
317         } else if (maxMinor == -1 && minor(device_) == MAX_PARTITION) {
318             partitionDev = makedev(MAJORID_BLKEXT, static_cast<uint32_t>(index) - MAX_PARTITION);
319         } else {
320             partitionDev = makedev(MAJORID_BLKEXT, maxMinor + static_cast<uint32_t>(index) - MAX_INTERVAL_PARTITION);
321         }
322     } else {
323         partitionDev = makedev(major(device_), minor(device_) + static_cast<uint32_t>(index));
324     }
325 
326     if (table == Table::MBR) {
327         if (++it == end) {
328             return;
329         }
330         int32_t type = std::stoi("0x0" + *it, 0, 16);
331         if (CreateMBRVolume(type, partitionDev)) {
332             foundPart = true;
333         } else {
334             LOGE("Create MBR Volume failed");
335         }
336     } else if (table == Table::GPT) {
337         if (CreateVolume(partitionDev) == E_OK) {
338             foundPart = true;
339         }
340     }
341 }
342 
GetMaxMinor(int32_t major)343 int32_t DiskInfo::GetMaxMinor(int32_t major)
344 {
345     DIR* dir;
346     struct dirent* entry;
347     int maxMinor = -1;
348     if ((dir = opendir(BLOCK_PATH)) == nullptr) {
349         LOGE("fail to open %{public}s", BLOCK_PATH);
350         return E_ERR;
351     }
352     while ((entry = readdir(dir)) != nullptr) {
353         if (entry->d_name[0] == '.' || strncmp(entry->d_name, "vol", VOL_LENGTH) != 0) {
354             continue;
355         }
356         std::string devicePath = std::string(BLOCK_PATH) + "/" + entry->d_name;
357         struct stat statbuf;
358         if (stat(devicePath.c_str(), &statbuf) == 0) {
359             int majorNum = major(statbuf.st_rdev);
360             int minorNum = minor(statbuf.st_rdev);
361 
362             if (majorNum == major) {
363                 maxMinor = minorNum > maxMinor ? minorNum : maxMinor;
364             }
365         }
366     }
367     closedir(dir);
368     return maxMinor;
369 }
370 
CreateVolume(dev_t dev)371 int DiskInfo::CreateVolume(dev_t dev)
372 {
373     auto volume = VolumeManager::Instance();
374 
375     LOGI("disk read volume metadata");
376     std::string volumeId = volume->CreateVolume(GetId(), dev);
377     if (volumeId == "") {
378         LOGE("Create volume failed");
379         return E_ERR;
380     }
381 
382     volumeId_.push_back(volumeId);
383     return E_OK;
384 }
385 
Partition()386 int DiskInfo::Partition()
387 {
388     LOGI("Partitioning the disk.");
389     std::vector<std::string> cmd;
390     int res;
391 
392     res = Destroy();
393     if (res != E_OK) {
394         LOGE("Destroy failed in Partition()");
395     }
396 
397     cmd.push_back(SGDISK_PATH);
398     cmd.push_back(SGDISK_ZAP_CMD);
399     cmd.push_back(devPath_);
400     LOGI("Partition executing command.");
401     res = ForkExec(cmd);
402     if (res != E_OK) {
403         LOGE("sgdisk: zap fail");
404         return res;
405     }
406 
407     cmd.clear();
408     cmd.push_back(SGDISK_PATH);
409     cmd.push_back(SGDISK_PART_CMD);
410     cmd.push_back(devPath_);
411     res = ForkExec(cmd);
412     if (res != E_OK) {
413         LOGE("sgdisk: partition fail");
414         return res;
415     }
416 
417     return E_OK;
418 }
419 } // namespace STORAGE_DAEMON
420 } // namespace OHOS
421