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 "volume/volume_manager.h"
17
18 #include <fcntl.h>
19 #include <sys/mount.h>
20 #include <sys/sysmacros.h>
21 #include <sys/stat.h>
22 #include <sys/xattr.h>
23 #include <unistd.h>
24
25 #include "ipc/storage_manager_client.h"
26 #include "mtp/mtp_device_monitor.h"
27 #include "storage_service_errno.h"
28 #include "storage_service_log.h"
29 #include "utils/storage_radar.h"
30 #include "utils/string_utils.h"
31 #include "utils/disk_utils.h"
32 #include "utils/file_utils.h"
33 #include "volume/external_volume_info.h"
34
35 const int32_t MTP_QUERY_RESULT_LEN = 10;
36 const std::string MTP_PATH_PREFIX = "/mnt/data/external/mtp";
37 #define STORAGE_MANAGER_IOC_CHK_BUSY _IOR(0xAC, 77, int)
38 using namespace std;
39 using namespace OHOS::StorageService;
40 namespace OHOS {
41 namespace StorageDaemon {
42
Instance()43 VolumeManager &VolumeManager::Instance()
44 {
45 static VolumeManager instance_;
46 return instance_;
47 }
48
GetVolume(const std::string volId)49 std::shared_ptr<VolumeInfo> VolumeManager::GetVolume(const std::string volId)
50 {
51 auto it = volumes_.Find(volId);
52 if (it == volumes_.End()) {
53 return nullptr;
54 }
55 return it->second;
56 }
57
CreateVolume(const std::string diskId,dev_t device,bool isUserdata)58 std::string VolumeManager::CreateVolume(const std::string diskId, dev_t device, bool isUserdata)
59 {
60 std::string volId = StringPrintf("vol-%u-%u", major(device), minor(device));
61
62 LOGI("create volume %{public}s.", volId.c_str());
63
64 std::shared_ptr<VolumeInfo> tmp = GetVolume(volId);
65 if (tmp != nullptr) {
66 LOGE("volume %{public}s exist.", volId.c_str());
67 return "";
68 }
69
70 auto info = std::make_shared<ExternalVolumeInfo>();
71 int32_t ret = info->Create(volId, diskId, device, isUserdata);
72 if (ret) {
73 return "";
74 }
75
76 volumes_.Insert(volId, info);
77
78 StorageManagerClient client;
79 ret = client.NotifyVolumeCreated(info);
80 if (ret != E_OK) {
81 LOGE("Volume Notify Created failed");
82 }
83
84 return volId;
85 }
86
DestroyVolume(const std::string volId)87 int32_t VolumeManager::DestroyVolume(const std::string volId)
88 {
89 LOGI("destroy volume %{public}s.", volId.c_str());
90
91 std::shared_ptr<VolumeInfo> destroyNode = GetVolume(volId);
92
93 if (destroyNode == nullptr) {
94 LOGE("the volume %{public}s does not exist", volId.c_str());
95 return E_NON_EXIST;
96 }
97
98 int32_t ret = destroyNode->Destroy();
99 if (ret) {
100 return ret;
101 }
102 if (IsUsbFuse()) {
103 ret = destroyNode->DestroyUsbFuse();
104 if (ret) {
105 return ret;
106 }
107 }
108
109 volumes_.Erase(volId);
110 destroyNode.reset();
111
112 return E_OK;
113 }
114
Check(const std::string volId)115 int32_t VolumeManager::Check(const std::string volId)
116 {
117 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
118 if (info == nullptr) {
119 LOGE("the volume %{public}s does not exist.", volId.c_str());
120 return E_NON_EXIST;
121 }
122
123 int32_t err = info->Check();
124 if (err != E_OK) {
125 LOGE("the volume %{public}s check failed.", volId.c_str());
126 return err;
127 }
128 return E_OK;
129 }
130
Mount(const std::string volId,uint32_t flags)131 int32_t VolumeManager::Mount(const std::string volId, uint32_t flags)
132 {
133 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
134 if (info == nullptr) {
135 #ifdef SUPPORT_OPEN_SOURCE_MTP_DEVICE
136 return OHOS::StorageDaemon::MtpDeviceMonitor::GetInstance().Mount(volId);
137 #else
138 LOGE("the volume %{public}s does not exist.", volId.c_str());
139 return E_NON_EXIST;
140 #endif
141 }
142 int32_t checkRet = info->DoTryToCheck();
143 if (checkRet == E_VOL_NEED_FIX) {
144 LOGE("external volume maybe damage");
145 StorageManagerClient client;
146 checkRet = client.NotifyVolumeDamaged(info);
147 }
148
149 int32_t err = info->Mount(flags);
150 if (err != E_OK) {
151 LOGE("the volume %{public}s mount failed.", volId.c_str());
152 return err;
153 }
154
155 StorageManagerClient client;
156 err = client.NotifyVolumeMounted(info);
157 if (err) {
158 LOGE("Volume Notify Mount Destroyed failed");
159 }
160 return E_OK;
161 }
162
MountUsbFuse(std::string volumeId,std::string & fsUuid,int & fuseFd)163 int32_t VolumeManager::MountUsbFuse(std::string volumeId, std::string &fsUuid, int &fuseFd)
164 {
165 std::shared_ptr<VolumeInfo> info = GetVolume(volumeId);
166 if (info == nullptr) {
167 LOGE("the volume %{public}s does not exist.", volumeId.c_str());
168 return E_NON_EXIST;
169 }
170 int32_t result = ReadVolumeUuid(volumeId, fsUuid);
171 if (result != E_OK) {
172 return result;
173 }
174 result = CreateMountUsbFusePath(fsUuid);
175 if (result != E_OK) {
176 return result;
177 }
178 fuseFd = open("/dev/fuse", O_RDWR);
179 if (fuseFd < 0) {
180 LOGE("open /dev/fuse fail for file mgr, errno is %{public}d.", errno);
181 return E_OPEN_FUSE;
182 }
183 LOGI("open fuse end.");
184 string opt = StringPrintf("fd=%i,"
185 "rootmode=40000,"
186 "default_permissions,"
187 "allow_other,"
188 "user_id=0,group_id=0,"
189 "context=\"u:object_r:mnt_external_file:s0\","
190 "fscontext=u:object_r:mnt_external_file:s0",
191 fuseFd);
192
193 int ret = StorageDaemon::Mount("/dev/fuse", mountUsbFusePath_.c_str(),
194 "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opt.c_str());
195 if (ret) {
196 LOGE("failed to mount fuse for file mgr, ret is %{public}d, errno is %{public}d.", ret, errno);
197 close(fuseFd);
198 std::string extraData = "dstPath=" + mountUsbFusePath_ + ",kernelCode=" + to_string(errno);
199 return E_MOUNT_FILE_MGR_FUSE;
200 }
201 return E_OK;
202 }
203
ReadVolumeUuid(std::string volumeId,std::string & fsUuid)204 int32_t VolumeManager::ReadVolumeUuid(std::string volumeId, std::string &fsUuid)
205 {
206 std::string devPath = StringPrintf("/dev/block/%s", (volumeId).c_str());
207 int32_t ret = OHOS::StorageDaemon::ReadVolumeUuid(devPath, fsUuid);
208 return ret;
209 }
210
CreateMountUsbFusePath(std::string fsUuid)211 int32_t VolumeManager::CreateMountUsbFusePath(std::string fsUuid)
212 {
213 LOGI("CreateMountUsbFusePath create path");
214 if (fsUuid.find("..") != std::string::npos || fsUuid.find("/") != std::string::npos) {
215 LOGE("Invalid fsUuid: %{public}s, contains path traversal characters or path separators",
216 GetAnonyString(fsUuid).c_str());
217 return E_PARAMS_INVALID;
218 }
219 struct stat statbuf;
220 mountUsbFusePath_ = StringPrintf("/mnt/data/external/%s", fsUuid.c_str());
221 if (!lstat(mountUsbFusePath_.c_str(), &statbuf)) {
222 LOGE("volume mount path %{public}s exists, please remove first", GetAnonyString(mountUsbFusePath_).c_str());
223 remove(mountUsbFusePath_.c_str());
224 return E_SYS_KERNEL_ERR;
225 }
226 if (mkdir(mountUsbFusePath_.c_str(), S_IRWXU | S_IRWXG | S_IXOTH)) {
227 LOGE("the volume %{public}s create path %{public}s failed",
228 GetAnonyString(fsUuid).c_str(), GetAnonyString(mountUsbFusePath_).c_str());
229 return E_MKDIR_MOUNT;
230 }
231 LOGI("CreateMountUsbFusePath create path out");
232 return E_OK;
233 }
234
TryToFix(const std::string volId,uint32_t flags)235 int32_t VolumeManager::TryToFix(const std::string volId, uint32_t flags)
236 {
237 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
238 if (info == nullptr) {
239 LOGE("the volume %{public}s does not exist.", volId.c_str());
240 return E_NON_EXIST;
241 }
242 int32_t errFix = info->DoTryToFix();
243 LOGE("The volume result: %{public}d.", errFix);
244 if (errFix != E_OK) {
245 LOGE("the volume %{public}s fix failed, ret: %{public}d.", volId.c_str(), errFix);
246 }
247
248 int32_t currentState = info->GetState();
249 if (currentState == MOUNTED) {
250 LOGE("the volume has mounted.id: %{public}s", volId.c_str());
251 return errFix;
252 }
253
254 int32_t errMount = info->UMount();
255 if (errMount != E_OK) {
256 LOGE("the volume %{public}s UMount failed, ret:%{public}d.", volId.c_str(), errMount);
257 }
258
259 errMount = info->Check();
260 if (errMount != E_OK) {
261 LOGE("the volume %{public}s check failed.", volId.c_str());
262 return errMount;
263 }
264
265 errMount = info->Mount(flags);
266 if (errMount != E_OK) {
267 LOGE("the volume %{public}s mount failed.", volId.c_str());
268 return errMount;
269 }
270
271 StorageManagerClient client;
272 errMount = client.NotifyVolumeMounted(info);
273 if (errMount) {
274 LOGE("Volume Notify Mount Destroyed failed");
275 }
276 return E_OK;
277 }
278
UMount(const std::string volId)279 int32_t VolumeManager::UMount(const std::string volId)
280 {
281 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
282 if (info == nullptr) {
283 #ifdef SUPPORT_OPEN_SOURCE_MTP_DEVICE
284 return OHOS::StorageDaemon::MtpDeviceMonitor::GetInstance().Umount(volId);
285 #else
286 LOGE("the volume %{public}s does not exist.", volId.c_str());
287 return E_NON_EXIST;
288 #endif
289 }
290
291 int32_t err = info->UMount();
292 if (err != E_OK) {
293 LOGE("the volume %{public}s mount failed.", volId.c_str());
294 return err;
295 }
296 return E_OK;
297 }
298
Format(const std::string volId,const std::string fsType)299 int32_t VolumeManager::Format(const std::string volId, const std::string fsType)
300 {
301 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
302 if (info == nullptr) {
303 LOGE("the volume %{public}s does not exist.", volId.c_str());
304 return E_NON_EXIST;
305 }
306
307 int32_t err = info->Format(fsType);
308 if (err != E_OK) {
309 LOGE("the volume %{public}s format failed.", volId.c_str());
310 StorageRadar::ReportVolumeOperation("VolumeInfo::Format", err);
311 return err;
312 }
313
314 return E_OK;
315 }
316
SetVolumeDescription(const std::string volId,const std::string description)317 int32_t VolumeManager::SetVolumeDescription(const std::string volId, const std::string description)
318 {
319 std::shared_ptr<VolumeInfo> info = GetVolume(volId);
320 if (info == nullptr) {
321 LOGE("the volume %{public}s does not exist.", volId.c_str());
322 return E_NON_EXIST;
323 }
324
325 int32_t err = info->SetVolumeDescription(description);
326 if (err != E_OK) {
327 LOGE("the volume %{public}s setVolumeDescription failed.", volId.c_str());
328 StorageRadar::ReportVolumeOperation("VolumeInfo::SetVolumeDescription", err);
329 return err;
330 }
331
332 return E_OK;
333 }
334
QueryUsbIsInUse(const std::string & diskPath,bool & isInUse)335 int32_t VolumeManager::QueryUsbIsInUse(const std::string &diskPath, bool &isInUse)
336 {
337 isInUse = true;
338 if (diskPath.empty()) {
339 LOGE("diskPath is null");
340 return E_PARAMS_NULLPTR_ERR;
341 }
342 char realPath[PATH_MAX] = { 0 };
343 if (realpath(diskPath.c_str(), realPath) == nullptr) {
344 LOGE("get realpath failed for diskPath =%{public}s.", diskPath.c_str());
345 return E_PARAMS_INVALID;
346 }
347 if (strncmp(realPath, diskPath.c_str(), diskPath.size()) != 0) {
348 LOGE("diskPath is not equal to realPath, realPath.size = %{public}zu, diskPath.size() = %{public}zu",
349 string_view(realPath).size(), diskPath.size());
350 return E_PARAMS_INVALID;
351 }
352 if (IsMtpDeviceInUse(diskPath)) {
353 isInUse = true;
354 return E_OK;
355 }
356 int fd = open(realPath, O_RDONLY);
357 if (fd < 0) {
358 LOGE("open file fail realPath %{public}s, errno %{public}d", realPath, errno);
359 return E_OPEN_FAILED;
360 }
361 int inUse = -1;
362 if (ioctl(fd, STORAGE_MANAGER_IOC_CHK_BUSY, &inUse) < 0) {
363 LOGE("ioctl check in use failed errno %{public}d", errno);
364 close(fd);
365 return E_IOCTL_FAILED;
366 }
367
368 if (inUse) {
369 LOGI("usb inuse number is %{public}d", inUse);
370 close(fd);
371 isInUse = true;
372 return E_OK;
373 }
374 LOGI("usb not inUse");
375 isInUse = false;
376 close(fd);
377 return E_OK;
378 }
379
IsMtpDeviceInUse(const std::string & diskPath)380 bool VolumeManager::IsMtpDeviceInUse(const std::string &diskPath)
381 {
382 if (diskPath.rfind(MTP_PATH_PREFIX, 0) != 0) {
383 return false;
384 }
385
386 std::string key = "user.queryMtpIsInUse";
387 char value[MTP_QUERY_RESULT_LEN] = { 0 };
388 int32_t len = getxattr(diskPath.c_str(), key.c_str(), value, MTP_QUERY_RESULT_LEN);
389 if (len < 0) {
390 LOGE("Failed to getxattr for diskPath = %{public}s", diskPath.c_str());
391 return false;
392 }
393
394 if ("true" == std::string(value)) {
395 LOGI("MTP device is in use for diskPath = %{public}s", diskPath.c_str());
396 return true;
397 }
398 return false;
399 }
400 } // StorageDaemon
401 } // OHOS
402