• 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 "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