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