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 "disk/disk_info.h"
17 
18 #include <sys/sysmacros.h>
19 
20 #include "disk/disk_manager.h"
21 #include "ipc/storage_manager_client.h"
22 #include "storage_service_errno.h"
23 #include "storage_service_log.h"
24 #include "utils/disk_utils.h"
25 #include "utils/file_utils.h"
26 #include "utils/string_utils.h"
27 #include "volume/volume_manager.h"
28 
29 namespace OHOS {
30 namespace StorageDaemon {
31 constexpr unsigned int MAJORID_BLKEXT = 259;
32 constexpr unsigned int MAX_PARTITION = 16;
33 const std::string SGDISK_PATH = "/system/bin/sgdisk";
34 const std::string SGDISK_DUMP_CMD = "--ohos-dump";
35 const std::string SGDISK_ZAP_CMD = "--zap-all";
36 const std::string SGDISK_PART_CMD = "--new=0:0:-0 --typeconde=0:0c00 --gpttombr=1";
37 
38 enum class Table {
39     UNKNOWN,
40     MBR,
41     GPT,
42 };
43 
DiskInfo(std::string sysPath,std::string devPath,dev_t device,int flag)44 DiskInfo::DiskInfo(std::string sysPath, std::string devPath, dev_t device, int flag)
45 {
46     id_ = StringPrintf("disk-%d-%d", major(device), minor(device));
47     sysPath_ = sysPath;
48     eventPath_ = devPath;
49     devPath_ = StringPrintf("/dev/block/%s", id_.c_str());
50     device_ = device;
51     flags_ = static_cast<unsigned int>(flag);
52     status = sInital;
53 }
54 
GetDevice() const55 dev_t DiskInfo::GetDevice() const
56 {
57     return device_;
58 }
59 
GetId() const60 std::string DiskInfo::GetId() const
61 {
62     return id_;
63 }
64 
GetDevPath() const65 std::string DiskInfo::GetDevPath() const
66 {
67     return devPath_;
68 }
69 
GetDevDSize() const70 uint64_t DiskInfo::GetDevDSize() const
71 {
72     return size_;
73 }
74 
GetSysPath() const75 std::string DiskInfo::GetSysPath() const
76 {
77     return sysPath_;
78 }
79 
GetDevVendor() const80 std::string DiskInfo::GetDevVendor() const
81 {
82     return vendor_;
83 }
84 
GetDevFlag() const85 int DiskInfo::GetDevFlag() const
86 {
87     return flags_;
88 }
89 
~DiskInfo()90 DiskInfo::~DiskInfo()
91 {
92     DestroyDiskNode(devPath_);
93 }
94 
Create()95 int DiskInfo::Create()
96 {
97     int ret;
98 
99     CreateDiskNode(devPath_, device_);
100     status = sCreate;
101     ReadMetadata();
102 
103     StorageManagerClient client;
104     ret = client.NotifyDiskCreated(*this);
105     if (ret != E_OK) {
106         LOGE("Notify Disk Created failed");
107         return ret;
108     }
109 
110     ret = ReadPartition();
111     if (ret != E_OK) {
112         LOGE("Create disk failed");
113         return ret;
114     }
115 
116     return E_OK;
117 }
118 
Destroy()119 int DiskInfo::Destroy()
120 {
121     auto volume = VolumeManager::Instance();
122 
123     for (auto volumeId : volumeId_) {
124         auto ret = volume->DestroyVolume(volumeId);
125         if (ret != E_OK) {
126             LOGE("Destroy volume %{public}s failed", volumeId.c_str());
127             return E_ERR;
128         }
129     }
130     status = sDestroy;
131     volumeId_.clear();
132     return E_OK;
133 }
134 
ReadMetadata()135 void DiskInfo::ReadMetadata()
136 {
137     size_ = -1;
138     vendor_.clear();
139     if (GetDevSize(devPath_, &size_) != E_OK) {
140         size_ = -1;
141     }
142 
143     unsigned int majorId = major(device_);
144     if (majorId == DISK_MMC_MAJOR) {
145         std::string path(sysPath_ + "/device/manfid");
146         std::string str;
147         if (!ReadFile(path, &str)) {
148             LOGE("open file %{public}s failed", path.c_str());
149             return;
150         }
151         int manfid = std::stoi(str);
152         switch (manfid) {
153             case 0x000003: {
154                 vendor_ = "SanDisk";
155                 break;
156             }
157             case 0x00001b: {
158                 vendor_ = "SamSung";
159                 break;
160             }
161             case 0x000028: {
162                 vendor_ = "Lexar";
163                 break;
164             }
165             case 0x000074: {
166                 vendor_ = "Transcend";
167                 break;
168             }
169             default : {
170                 vendor_ = "Unknown";
171                 LOGI("Unknown vendor information: %{public}d", manfid);
172                 break;
173             }
174         }
175     } else {
176         std::string path(sysPath_ + "/device/vendor");
177         std::string str;
178         if (!ReadFile(path, &str)) {
179             LOGE("open file %{public}s failed", path.c_str());
180             return;
181         }
182         vendor_ = str;
183         LOGI("Read metadata %{public}s", path.c_str());
184     }
185 }
186 
ReadPartition()187 int DiskInfo::ReadPartition()
188 {
189     int maxVolumes = GetMaxVolume(device_);
190     if (maxVolumes < 0) {
191         LOGE("Invaild maxVolumes: %{public}d", maxVolumes);
192         return E_ERR;
193     }
194 
195     std::vector<std::string> cmd;
196     std::vector<std::string> output;
197     std::vector<std::string> lines;
198     int res;
199 
200     cmd.push_back(SGDISK_PATH);
201     cmd.push_back(SGDISK_DUMP_CMD);
202     cmd.push_back(devPath_);
203     res = ForkExec(cmd, &output);
204     if (res != E_OK) {
205         LOGE("get %{private}s partition failed", devPath_.c_str());
206         return res;
207     }
208     std::string bufToken = "\n";
209     for (auto &buf : output) {
210         auto split = SplitLine(buf, bufToken);
211         for (auto &tmp : split)
212             lines.push_back(tmp);
213     }
214 
215     status = sScan;
216     return ReadDiskLines(lines, maxVolumes);
217 }
218 
CreateMBRVolume(int32_t type,dev_t dev)219 bool DiskInfo::CreateMBRVolume(int32_t type, dev_t dev)
220 {
221     // FAT16 || NTFS/EXFAT || W95 FAT32 || W95 FAT32 || W95 FAT16 || EFI FAT32
222     if (type == 0x06 || type == 0x07 || type == 0x0b || type == 0x0c || type == 0x0e || type == 0x1b) {
223         if (CreateVolume(dev) == E_OK) {
224             return true;
225         }
226     }
227     return false;
228 }
229 
CreateUnknownTabVol()230 int32_t DiskInfo::CreateUnknownTabVol()
231 {
232     LOGI("%{public}s has unknown table", id_.c_str());
233     std::string fsType;
234     std::string uuid;
235     std::string label;
236     if (OHOS::StorageDaemon::ReadMetadata(devPath_, fsType, uuid, label) == E_OK) {
237         CreateVolume(device_);
238     } else {
239         LOGE("failed to identify the disk device");
240         return E_NON_EXIST;
241     }
242     return E_OK;
243 }
244 
ReadDiskLines(std::vector<std::string> lines,int32_t maxVols)245 int32_t DiskInfo::ReadDiskLines(std::vector<std::string> lines, int32_t maxVols)
246 {
247     std::string lineToken = " ";
248     bool foundPart = false;
249     Table table = Table::UNKNOWN;
250     for (auto &line : lines) {
251         auto split = SplitLine(line, lineToken);
252         auto it = split.begin();
253         if (it == split.end()) {
254             continue;
255         }
256 
257         if (*it == "DISK") {
258             if (++it == split.end()) {
259                 continue;
260             }
261             if (*it == "mbr") {
262                 table = Table::MBR;
263             } else if (*it == "gpt") {
264                 table = Table::GPT;
265             } else {
266                 LOGI("Unknown partition table %{public}s", (*it).c_str());
267                 continue;
268             }
269         } else if (*it == "PART") {
270             ProcessPartition(it, split.end(), table, maxVols, foundPart);
271         }
272     }
273 
274     if (table == Table::UNKNOWN || !foundPart) {
275         return CreateUnknownTabVol();
276     }
277 
278     return E_OK;
279 }
280 
ProcessPartition(std::vector<std::string>::iterator & it,const std::vector<std::string>::iterator & end,Table table,int32_t maxVols,bool & foundPart)281 void DiskInfo::ProcessPartition(std::vector<std::string>::iterator &it, const std::vector<std::string>::iterator &end,
282                                 Table table, int32_t maxVols, bool &foundPart)
283 {
284     if (++it == end) {
285         return;
286     }
287     int32_t index = std::stoi(*it);
288     unsigned int majorId = major(device_);
289     if ((index > maxVols && majorId == DISK_MMC_MAJOR) || index < 1) {
290         LOGE("Invalid partition %{public}d", index);
291         return;
292     }
293     dev_t partitionDev = (index > MAX_SCSI_VOLUMES) ?
294         makedev(MAJORID_BLKEXT, minor(device_) + static_cast<uint32_t>(index) - MAX_PARTITION) :
295         makedev(major(device_), minor(device_) + static_cast<uint32_t>(index));
296     if (table == Table::MBR) {
297         if (++it == end) {
298             return;
299         }
300         int32_t type = std::stoi("0x0" + *it, 0, 16);
301         foundPart = CreateMBRVolume(type, partitionDev);
302         if (CreateMBRVolume(type, partitionDev)) {
303             foundPart = true;
304         } else {
305             LOGE("Create MBR Volume failed");
306         }
307     } else if (table == Table::GPT) {
308         if (CreateVolume(partitionDev) == E_OK) {
309             foundPart = true;
310         }
311     }
312 }
313 
CreateVolume(dev_t dev)314 int DiskInfo::CreateVolume(dev_t dev)
315 {
316     auto volume = VolumeManager::Instance();
317 
318     LOGI("disk read volume metadata");
319     std::string volumeId = volume->CreateVolume(GetId(), dev);
320     if (volumeId == "") {
321         LOGE("Create volume failed");
322         return E_ERR;
323     }
324 
325     volumeId_.push_back(volumeId);
326     return E_OK;
327 }
328 
Partition()329 int DiskInfo::Partition()
330 {
331     std::vector<std::string> cmd;
332     int res;
333 
334     res = Destroy();
335     if (res != E_OK) {
336         LOGE("Destroy failed in Partition()");
337     }
338 
339     cmd.push_back(SGDISK_PATH);
340     cmd.push_back(SGDISK_ZAP_CMD);
341     cmd.push_back(devPath_);
342     res = ForkExec(cmd);
343     if (res != E_OK) {
344         LOGE("sgdisk: zap fail");
345         return res;
346     }
347 
348     cmd.clear();
349     cmd.push_back(SGDISK_PATH);
350     cmd.push_back(SGDISK_PART_CMD);
351     cmd.push_back(devPath_);
352     res = ForkExec(cmd);
353     if (res != E_OK) {
354         LOGE("sgdisk: partition fail");
355         return res;
356     }
357 
358     return E_OK;
359 }
360 } // namespace STORAGE_DAEMON
361 } // namespace OHOS
362