1 /*
2 * Copyright (c) 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 "copy/remote_file_copy_manager.h"
17
18 #include <mutex>
19 #include <sys/stat.h>
20
21 #include "copy/file_size_utils.h"
22 #include "dfs_error.h"
23 #include "datashare_helper.h"
24 #include "ipc_skeleton.h"
25 #include "sandbox_helper.h"
26 #include "utils_log.h"
27
28 #undef LOG_DOMAIN
29 #undef LOG_TAG
30 #define LOG_DOMAIN 0xD004315
31 #define LOG_TAG "distributedfile_daemon"
32
33 namespace OHOS {
34 namespace Storage {
35 namespace DistributedFile {
36 using namespace AppFileService;
37 using namespace FileManagement;
38 static const std::string MEDIA_AUTHORITY = "media";
39 static const std::string FILE_MANAGER_AUTHORITY = "docs";
40 static const std::string FILE_SCHEMA = "file://";
41 static const std::string FILE_SEPARATOR = "/";
42 std::shared_ptr<RemoteFileCopyManager> RemoteFileCopyManager::instance_ = nullptr;
43
GetBundleName(const std::string & uri)44 static std::string GetBundleName(const std::string &uri)
45 {
46 auto pos = uri.find(FILE_SCHEMA);
47 if (pos == std::string::npos) {
48 return "";
49 }
50 auto tmpUri = uri.substr(pos + FILE_SCHEMA.size());
51 if (tmpUri.empty()) {
52 return "";
53 }
54 auto bundleNamePos = tmpUri.find(FILE_SEPARATOR);
55 if (bundleNamePos == std::string::npos) {
56 return "";
57 }
58 return tmpUri.substr(0, bundleNamePos);
59 }
60
ChangeOwnerRecursive(const std::string & path,uid_t uid,gid_t gid)61 static int32_t ChangeOwnerRecursive(const std::string &path, uid_t uid, gid_t gid)
62 {
63 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), &closedir);
64 if (dir == nullptr) {
65 LOGE("Directory is null");
66 return EINVAL;
67 }
68
69 struct dirent *entry = nullptr;
70 while ((entry = readdir(dir.get())) != nullptr) {
71 if (entry->d_type == DT_DIR) {
72 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
73 continue;
74 }
75 std::string subPath = path + "/" + entry->d_name;
76 if (chown(subPath.c_str(), uid, gid) == -1) {
77 LOGE("Change owner recursive failed");
78 return EIO;
79 }
80 return ChangeOwnerRecursive(subPath, uid, gid);
81 } else {
82 std::string filePath = path + "/" + entry->d_name;
83 if (chown(filePath.c_str(), uid, gid) == -1) {
84 LOGE("Change owner recursive failed");
85 return EIO;
86 }
87 }
88 }
89 return E_OK;
90 }
91
IsMediaUri(const std::string & uriPath)92 bool RemoteFileCopyManager::IsMediaUri(const std::string &uriPath)
93 {
94 Uri uri(uriPath);
95 std::string bundleName = uri.GetAuthority();
96 return bundleName == MEDIA_AUTHORITY;
97 }
98
IsFile(const std::string & path)99 bool RemoteFileCopyManager::IsFile(const std::string &path)
100 {
101 struct stat buf {};
102 int ret = stat(path.c_str(), &buf);
103 if (ret == -1) {
104 LOGE("stat failed, errno is %{public}d, ", errno);
105 return false;
106 }
107 return (buf.st_mode & S_IFMT) == S_IFREG;
108 }
109
GetFileName(const std::string & path)110 static std::string GetFileName(const std::string &path)
111 {
112 auto pos = path.rfind("/");
113 if (pos == std::string::npos) {
114 LOGE("invalid path");
115 return "";
116 }
117 return path.substr(pos + 1);
118 }
119
GetInstance()120 std::shared_ptr<RemoteFileCopyManager> RemoteFileCopyManager::GetInstance()
121 {
122 static std::once_flag once;
123 std::call_once(once, []() {
124 RemoteFileCopyManager::instance_ = std::make_shared<RemoteFileCopyManager>();
125 });
126 return instance_;
127 }
128
AddFileInfos(std::shared_ptr<FileInfos> infos)129 void RemoteFileCopyManager::AddFileInfos(std::shared_ptr<FileInfos> infos)
130 {
131 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
132 FileInfosVec_.push_back(infos);
133 }
134
CreateFileInfos(const std::string & srcUri,const std::string & destUri,std::shared_ptr<FileInfos> & infos,const int32_t userId,const std::string & copyPath)135 int32_t RemoteFileCopyManager::CreateFileInfos(const std::string &srcUri,
136 const std::string &destUri, std::shared_ptr<FileInfos> &infos, const int32_t userId, const std::string ©Path)
137 {
138 infos->srcUri = srcUri;
139 infos->destUri = destUri;
140 std::string srcPhysicalPath;
141 if (SandboxHelper::GetPhysicalPath(srcUri, std::to_string(userId), srcPhysicalPath)) {
142 LOGE("Get src path failed, invalid uri");
143 return EINVAL;
144 }
145 std::string dstPhysicalPath;
146 if (SandboxHelper::GetPhysicalPath(destUri, std::to_string(userId), dstPhysicalPath)) {
147 LOGE("Get dst path failed, invalid uri");
148 return EINVAL;
149 }
150 Uri uri(destUri);
151 auto authority = uri.GetAuthority();
152 if (authority != FILE_MANAGER_AUTHORITY && authority != MEDIA_AUTHORITY) {
153 std::string bundleName = GetBundleName(destUri);
154 std::string fileName = GetFileName(srcPhysicalPath);
155 // copy to tmp path
156 dstPhysicalPath = "/data/service/el2/" + std::to_string(userId) + "/hmdfs/account/data/" + bundleName +
157 "/" + copyPath + "/" + fileName;
158 }
159 infos->srcPath = srcPhysicalPath;
160 infos->destPath = dstPhysicalPath;
161 infos->srcUriIsFile = IsMediaUri(infos->srcUri) || IsFile(infos->srcPath);
162 infos->callingUid = IPCSkeleton::GetCallingUid();
163 AddFileInfos(infos);
164 return E_OK;
165 }
166
RemoveFileInfos(std::shared_ptr<FileInfos> infos)167 void RemoteFileCopyManager::RemoveFileInfos(std::shared_ptr<FileInfos> infos)
168 {
169 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
170 for (auto it = FileInfosVec_.begin(); it != FileInfosVec_.end();) {
171 if ((*it)->srcUri == infos->srcUri && (*it)->destUri == infos->destUri) {
172 it = FileInfosVec_.erase(it);
173 } else {
174 ++it;
175 }
176 }
177 }
178
RemoteCancel(const std::string & srcUri,const std::string & destUri)179 int32_t RemoteFileCopyManager::RemoteCancel(const std::string &srcUri, const std::string &destUri)
180 {
181 LOGI("RemoteCancel");
182 std::lock_guard<std::mutex> lock(FileInfosVecMutex_);
183 int32_t ret = 0;
184 if (!FileSizeUtils::IsFilePathValid(FileSizeUtils::GetRealUri(srcUri)) ||
185 !FileSizeUtils::IsFilePathValid(FileSizeUtils::GetRealUri(destUri))) {
186 LOGE("path is forbidden");
187 return EINVAL;
188 }
189 for (auto item = FileInfosVec_.begin(); item != FileInfosVec_.end();) {
190 if ((*item)->srcUri != srcUri || (*item)->destUri != destUri) {
191 ++item;
192 continue;
193 }
194 auto callingUid = IPCSkeleton::GetCallingUid();
195 if (callingUid != (*item)->callingUid) {
196 LOGE("RemoteCancel failed, calling uid=%{public}d has no permission to cancel copy for uid=%{public}d.",
197 callingUid, (*item)->callingUid);
198 return EPERM;
199 }
200 LOGI("RemoteCancel success");
201 (*item)->needCancel.store(true);
202 item = FileInfosVec_.erase(item);
203 return ret;
204 }
205 return E_OK;
206 }
207
RemoteCopy(const std::string & srcUri,const std::string & destUri,const sptr<IFileTransListener> & listener,const int32_t userId,const std::string & copyPath)208 int32_t RemoteFileCopyManager::RemoteCopy(const std::string &srcUri, const std::string &destUri,
209 const sptr<IFileTransListener> &listener, const int32_t userId, const std::string ©Path)
210 {
211 LOGI("RemoteCopy start");
212 if (srcUri.empty() || destUri.empty()) {
213 return EINVAL;
214 }
215 if (!FileSizeUtils::IsFilePathValid(FileSizeUtils::GetRealUri(srcUri)) ||
216 !FileSizeUtils::IsFilePathValid(FileSizeUtils::GetRealUri(destUri))) {
217 LOGE("path is forbidden");
218 return EINVAL;
219 }
220 auto infos = std::make_shared<FileInfos>();
221 auto ret = CreateFileInfos(srcUri, destUri, infos, userId, copyPath);
222 if (ret != E_OK) {
223 LOGE("CreateFileInfos failed,ret= %{public}d", ret);
224 return ret;
225 }
226 std::function<void(uint64_t processSize, uint64_t totalSize)> processCallback =
227 [&listener](uint64_t processSize, uint64_t totalSize) -> void {
228 if (processSize != totalSize) {
229 listener->OnFileReceive(totalSize, processSize);
230 }
231 };
232 infos->localListener = FileCopyLocalListener::GetLocalListener(infos->srcPath,
233 infos->srcUriIsFile, processCallback);
234 auto result = FileCopyManager::GetInstance()->ExecLocal(infos);
235 if (ChangeOwnerRecursive(infos->destPath, infos->callingUid, infos->callingUid) != 0) {
236 LOGE("ChangeOwnerRecursive failed, calling uid= %{public}d", infos->callingUid);
237 }
238 RemoveFileInfos(infos);
239 infos->localListener->StopListener();
240
241 if (result != E_OK) {
242 return result;
243 }
244 result = infos->localListener->GetResult();
245 if (result != E_OK) {
246 listener->OnFailed("", result);
247 return result;
248 }
249 listener->OnFinished("");
250 LOGI("RemoteCopy end");
251 return E_OK;
252 }
253 } // namespace DistributedFile
254 } // namespace Storage
255 } // namespace OHOS
256