1 /*
2 * Copyright (c) 2023~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 "ipc/cloud_daemon.h"
17
18 #include <exception>
19 #include <stdexcept>
20 #include <thread>
21 #include <malloc.h>
22 #include <sys/stat.h>
23 #include <sys/utsname.h>
24
25 #include "appstate_observer.h"
26 #include "cloud_file_fault_event.h"
27 #include "dfs_error.h"
28 #include "ffrt_inner.h"
29 #include "fuse_manager/fuse_manager.h"
30 #include "iremote_object.h"
31 #include "parameters.h"
32 #include "plugin_loader.h"
33 #include "setting_data_helper.h"
34 #include "system_ability_definition.h"
35 #include "utils_directory.h"
36 #include "utils_log.h"
37 #ifdef HICOLLIE_ENABLE
38 #include "xcollie_helper.h"
39 #endif
40
41 namespace OHOS {
42 namespace FileManagement {
43 namespace CloudFile {
44 using namespace std;
45 using namespace CloudDisk;
46
47 namespace {
48 static const string BETA_VERSION = "beta";
49 static const string CN_REGION = "CN";
50 static const string GLOBAL_REGION = "const.global.region";
51 static const string KEY_VERSION_TYPE = "const.logsystem.versiontype";
52 static const string LOCAL_PATH_DATA_SERVICE_EL2 = "/data/service/el2/";
53 static const string LOCAL_PATH_HMDFS_DENTRY_CACHE = "/hmdfs/cache/account_cache/dentry_cache/";
54 static const string LOCAL_PATH_HMDFS_CACHE_CLOUD = "/hmdfs/cache/account_cache/dentry_cache/cloud";
55 static const int32_t STAT_MODE_DIR = 0771;
56 static const int32_t STAT_MODE_DIR_DENTRY_CACHE = 02771;
57 static const int32_t OID_DFS = 1009;
58 }
59 REGISTER_SYSTEM_ABILITY_BY_ID(CloudDaemon, FILEMANAGEMENT_CLOUD_DAEMON_SERVICE_SA_ID, true);
60
CloudDaemon(int32_t saID,bool runOnCreate)61 CloudDaemon::CloudDaemon(int32_t saID, bool runOnCreate) : SystemAbility(saID, runOnCreate)
62 {
63 accountStatusListener_ = make_shared<AccountStatusListener>();
64 }
65
PublishSA()66 void CloudDaemon::PublishSA()
67 {
68 LOGI("Begin to init");
69 if (!registerToService_) {
70 bool ret = SystemAbility::Publish(this);
71 if (!ret) {
72 throw runtime_error("Failed to publish the daemon");
73 }
74 registerToService_ = true;
75 }
76 LOGI("Init finished successfully");
77 }
78
CheckDeviceInLinux()79 static bool CheckDeviceInLinux()
80 {
81 struct utsname uts;
82 if (uname(&uts) == -1) {
83 LOGE("uname get failed.");
84 return false;
85 }
86 if (strcmp(uts.sysname, "Linux") == 0) {
87 LOGI("uname system is linux.");
88 return true;
89 }
90 return false;
91 }
92
ModSysParam()93 static void ModSysParam()
94 {
95 if (CheckDeviceInLinux()) {
96 const string photos = "persist.kernel.bundle_name.photos";
97 const string clouddrive = "persist.kernel.bundle_name.clouddrive";
98 system::SetParameter(photos, "");
99 system::SetParameter(clouddrive, "");
100 }
101 }
102
ShouldRegisterListener()103 static bool ShouldRegisterListener()
104 {
105 const string versionType = system::GetParameter(KEY_VERSION_TYPE, "");
106 const string region = system::GetParameter(GLOBAL_REGION, "");
107 return versionType == BETA_VERSION && region == CN_REGION;
108 }
109
OnStart()110 void CloudDaemon::OnStart()
111 {
112 LOGI("Begin to start service");
113 if (state_ == ServiceRunningState::STATE_RUNNING) {
114 LOGI("Daemon has already started");
115 return;
116 }
117
118 try {
119 PublishSA();
120 AddSystemAbilityListener(COMMON_EVENT_SERVICE_ID);
121 AddSystemAbilityListener(DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID);
122 mode_t mode = 002;
123 umask(mode);
124 ModSysParam();
125 } catch (const exception &e) {
126 LOGE("%{public}s", e.what());
127 }
128
129 state_ = ServiceRunningState::STATE_RUNNING;
130 /* load cloud file ext plugin */
131 CloudFile::PluginLoader::GetInstance().LoadCloudKitPlugin();
132
133 if (ShouldRegisterListener()) {
134 auto bundleNameList = vector<string>{};
135 std::thread listenThread([bundleNameList] {
136 CloudDisk::AppStateObserverManager::GetInstance().SubscribeAppState(bundleNameList);
137 });
138 listenThread.detach();
139 }
140 LOGI("Start service successfully");
141 }
142
HandleStartMove(int32_t userId)143 void HandleStartMove(int32_t userId)
144 {
145 const string moveFile = "persist.kernel.move.finish";
146 system::SetParameter(moveFile, "false");
147 const std::string filemanagerKey = "persist.kernel.bundle_name.filemanager";
148 string subList[] = {"com.ohos.photos", system::GetParameter(filemanagerKey, "")};
149 string srcBase = "/data/service/el1/public/cloudfile/" + to_string(userId);
150 string dstBase = "/data/service/el2/" + to_string(userId) + "/hmdfs/cloudfile_manager";
151 string removePath = srcBase + "/" + subList[1] + "/backup";
152 bool ret = Storage::DistributedFile::Utils::ForceRemoveDirectoryDeepFirst(removePath);
153 if (!ret) {
154 LOGE("remove failed path: %{public}s", GetAnonyString(removePath).c_str());
155 }
156 const auto copyOptions = filesystem::copy_options::overwrite_existing | filesystem::copy_options::recursive;
157 for (auto sub : subList) {
158 string srcPath = srcBase + '/' + sub;
159 string dstPath = dstBase + '/' + sub;
160 if (access(srcPath.c_str(), F_OK) != 0) {
161 LOGI("srcPath %{public}s not found", GetAnonyString(srcPath).c_str());
162 continue;
163 }
164 LOGI("Begin to move path: %{public}s", GetAnonyString(srcPath).c_str());
165 error_code errCode;
166 filesystem::copy(srcPath, dstPath, copyOptions, errCode);
167 if (errCode.value() != 0) {
168 LOGE("copy failed path: %{public}s, errCode: %{public}d",
169 GetAnonyString(srcPath).c_str(), errCode.value());
170 }
171 LOGI("End move path: %{public}s", GetAnonyString(srcPath).c_str());
172 bool ret = Storage::DistributedFile::Utils::ForceRemoveDirectoryDeepFirst(srcPath);
173 if (!ret) {
174 LOGE("remove failed path: %{public}s", GetAnonyString(srcPath).c_str());
175 }
176 }
177 system::SetParameter(moveFile, "true");
178 }
179
OnStop()180 void CloudDaemon::OnStop()
181 {
182 LOGI("Begin to stop");
183 state_ = ServiceRunningState::STATE_NOT_START;
184 registerToService_ = false;
185 CloudDisk::AppStateObserverManager::GetInstance().UnSubscribeAppState();
186 LOGI("Stop finished successfully");
187 }
188
OnAddSystemAbility(int32_t systemAbilityId,const std::string & deviceId)189 void CloudDaemon::OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
190 {
191 LOGI("OnAddSystemAbility systemAbilityId:%{public}d added!", systemAbilityId);
192 if (systemAbilityId == COMMON_EVENT_SERVICE_ID) {
193 accountStatusListener_->Start();
194 } else if (systemAbilityId == DISTRIBUTED_KV_DATA_SERVICE_ABILITY_ID) {
195 // get setting data when sa load
196 bool ret = SettingDataHelper::GetInstance().InitActiveBundle();
197 LOGI("SERVICE LOAD: Init active bundle, ret: %{public}d", ret);
198 }
199 }
200
ExecuteStartFuse(int32_t userId,int32_t devFd,const std::string & path)201 void CloudDaemon::ExecuteStartFuse(int32_t userId, int32_t devFd, const std::string& path)
202 {
203 ffrt::submit([=]() {
204 int32_t ret = FuseManager::GetInstance().StartFuse(userId, devFd, path);
205 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
206 CloudFile::FaultType::FILE, ret, "start fuse, ret = " + std::to_string(ret)});
207 });
208 }
209
StartFuse(int32_t userId,int32_t devFd,const string & path)210 int32_t CloudDaemon::StartFuse(int32_t userId, int32_t devFd, const string &path)
211 {
212 #ifdef HICOLLIE_ENABLE
213 const int32_t TIMEOUT_S = 2;
214 int32_t xcollieId = XCollieHelper::SetTimer("CloudFileDaemon_StartFuse", TIMEOUT_S, nullptr, nullptr, true);
215 #endif
216 LOGI("Start Fuse");
217 ExecuteStartFuse(userId, devFd, path);
218
219 string dentryPath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CACHE_CLOUD;
220 if (access(dentryPath.c_str(), F_OK) != 0) {
221 string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_DENTRY_CACHE;
222 if (mkdir(cachePath.c_str(), STAT_MODE_DIR) != 0 && errno != EEXIST) {
223 #ifdef HICOLLIE_ENABLE
224 XCollieHelper::CancelTimer(xcollieId);
225 #endif
226 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
227 CloudFile::FaultType::FILE, errno, "create accout_cache path error " + std::to_string(errno)});
228 return E_PATH;
229 }
230 if (chmod(cachePath.c_str(), STAT_MODE_DIR_DENTRY_CACHE) != 0) {
231 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
232 CloudFile::FaultType::FILE, errno, "chmod cachepath error " + std::to_string(errno)});
233 }
234 if (chown(cachePath.c_str(), OID_DFS, OID_DFS) != 0) {
235 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
236 CloudFile::FaultType::FILE, errno, "chown cachepath error " + std::to_string(errno)});
237 }
238 if (mkdir(dentryPath.c_str(), STAT_MODE_DIR) != 0 && errno != EEXIST) {
239 #ifdef HICOLLIE_ENABLE
240 XCollieHelper::CancelTimer(xcollieId);
241 #endif
242 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
243 CloudFile::FaultType::FILE, errno, "create dentrypath " + std::to_string(errno)});
244 return E_PATH;
245 }
246 if (chown(dentryPath.c_str(), OID_DFS, OID_DFS) != 0) {
247 CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{"", CloudFile::FaultOperation::SESSION,
248 CloudFile::FaultType::FILE, errno, "chown cachepath error " + std::to_string(errno)});
249 }
250 }
251 if (path.find("cloud_fuse") != string::npos) {
252 std::thread([userId]() {
253 HandleStartMove(userId);
254 }).detach();
255 }
256 #ifdef HICOLLIE_ENABLE
257 XCollieHelper::CancelTimer(xcollieId);
258 #endif
259 return E_OK;
260 }
261 } // namespace CloudFile
262 } // namespace FileManagement
263 } // namespace OHOS
264