1/*
2 * Copyright (c) 2023 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 "iremote_object.h"
26#include "system_ability_definition.h"
27#include "parameters.h"
28#include "plugin_loader.h"
29#include "dfs_error.h"
30#include "fuse_manager/fuse_manager.h"
31#include "utils_directory.h"
32#include "utils_log.h"
33
34namespace OHOS {
35namespace FileManagement {
36namespace CloudFile {
37using namespace std;
38using namespace CloudDisk;
39
40namespace {
41    static const string LOCAL_PATH_DATA_SERVICE_EL2 = "/data/service/el2/";
42    static const string LOCAL_PATH_HMDFS_DENTRY_CACHE = "/hmdfs/cache/account_cache/dentry_cache/";
43    static const string LOCAL_PATH_HMDFS_CACHE_CLOUD = "/hmdfs/cache/account_cache/dentry_cache/cloud";
44    static const int32_t STAT_MODE_DIR = 0771;
45    static const int32_t STAT_MODE_DIR_DENTRY_CACHE = 02771;
46    static const int32_t OID_DFS = 1009;
47}
48REGISTER_SYSTEM_ABILITY_BY_ID(CloudDaemon, FILEMANAGEMENT_CLOUD_DAEMON_SERVICE_SA_ID, true);
49
50CloudDaemon::CloudDaemon(int32_t saID, bool runOnCreate) : SystemAbility(saID, runOnCreate)
51{
52    accountStatusListener_ = make_shared<AccountStatusListener>();
53}
54
55void CloudDaemon::PublishSA()
56{
57    LOGI("Begin to init");
58    if (!registerToService_) {
59        bool ret = SystemAbility::Publish(this);
60        if (!ret) {
61            throw runtime_error("Failed to publish the daemon");
62        }
63        registerToService_ = true;
64    }
65    LOGI("Init finished successfully");
66}
67
68static bool CheckDeviceInLinux()
69{
70    struct utsname uts;
71    if (uname(&uts) == -1) {
72        LOGE("uname get failed.");
73        return false;
74    }
75    if (strcmp(uts.sysname, "Linux") == 0) {
76        LOGI("uname system is linux.");
77        return true;
78    }
79    return false;
80}
81
82static void ModSysParam()
83{
84    if (CheckDeviceInLinux()) {
85        const string photos = "persist.kernel.bundle_name.photos";
86        const string clouddrive = "persist.kernel.bundle_name.clouddrive";
87        system::SetParameter(photos, "");
88        system::SetParameter(clouddrive, "");
89    }
90}
91
92void CloudDaemon::OnStart()
93{
94    LOGI("Begin to start service");
95    if (state_ == ServiceRunningState::STATE_RUNNING) {
96        LOGI("Daemon has already started");
97        return;
98    }
99
100    try {
101        PublishSA();
102        AddSystemAbilityListener(COMMON_EVENT_SERVICE_ID);
103        mode_t mode = 002;
104        umask(mode);
105        ModSysParam();
106    } catch (const exception &e) {
107        LOGE("%{public}s", e.what());
108    }
109
110    state_ = ServiceRunningState::STATE_RUNNING;
111    /* load cloud file ext plugin */
112    CloudFile::PluginLoader::GetInstance().LoadCloudKitPlugin();
113    LOGI("Start service successfully");
114}
115
116void HandleStartMove(int32_t userId)
117{
118    const std::string filemanagerKey = "persist.kernel.bundle_name.filemanager";
119    string subList[] = {"com.ohos.photos", system::GetParameter(filemanagerKey, "")};
120    string srcBase = "/data/service/el1/public/cloudfile/" + to_string(userId);
121    string dstBase = "/data/service/el2/" + to_string(userId) + "/hmdfs/cloudfile_manager";
122    const auto copyOptions = filesystem::copy_options::overwrite_existing | filesystem::copy_options::recursive;
123    for (auto sub : subList) {
124        string srcPath = srcBase + '/' + sub;
125        string dstPath = dstBase + '/' + sub;
126        if (access(srcPath.c_str(), F_OK) != 0) {
127            LOGI("srcPath %{public}s not found", GetAnonyString(srcPath).c_str());
128            continue;
129        }
130        LOGI("Begin to move path: %{public}s", GetAnonyString(srcPath).c_str());
131        error_code errCode;
132        filesystem::copy(srcPath, dstPath, copyOptions, errCode);
133        if (errCode.value() != 0) {
134            LOGE("copy failed path: %{public}s, errCode: %{public}d",
135                GetAnonyString(srcPath).c_str(), errCode.value());
136        }
137        LOGI("End move path: %{public}s", GetAnonyString(srcPath).c_str());
138        bool ret = Storage::DistributedFile::Utils::ForceRemoveDirectoryDeepFirst(srcPath);
139        if (!ret) {
140            LOGE("remove failed path: %{public}s", GetAnonyString(srcPath).c_str());
141        }
142    }
143    const string moveFile = "persist.kernel.move.finish";
144    system::SetParameter(moveFile, "true");
145}
146
147void CloudDaemon::OnStop()
148{
149    LOGI("Begin to stop");
150    state_ = ServiceRunningState::STATE_NOT_START;
151    registerToService_ = false;
152    LOGI("Stop finished successfully");
153}
154
155void CloudDaemon::OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
156{
157    LOGI("OnAddSystemAbility systemAbilityId:%{public}d added!", systemAbilityId);
158    accountStatusListener_->Start();
159}
160
161int32_t CloudDaemon::StartFuse(int32_t userId, int32_t devFd, const string &path)
162{
163    LOGI("Start Fuse");
164    std::thread([=]() {
165        int32_t ret = FuseManager::GetInstance().StartFuse(userId, devFd, path);
166        LOGI("start fuse result %d", ret);
167        }).detach();
168
169    string dentryPath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CACHE_CLOUD;
170    if (access(dentryPath.c_str(), F_OK) != 0) {
171        string cachePath = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_DENTRY_CACHE;
172        if (mkdir(cachePath.c_str(), STAT_MODE_DIR) != 0 && errno != EEXIST) {
173            LOGE("create accout_cache path error %{public}d", errno);
174            return E_PATH;
175        }
176        if (chmod(cachePath.c_str(), STAT_MODE_DIR_DENTRY_CACHE) != 0) {
177            LOGE("chmod cachepath error %{public}d", errno);
178        }
179        if (chown(cachePath.c_str(), OID_DFS, OID_DFS) != 0) {
180            LOGE("chown cachepath error %{public}d", errno);
181        }
182        if (mkdir(dentryPath.c_str(), STAT_MODE_DIR) != 0 && errno != EEXIST) {
183            LOGE("create dentrypath %{public}d", errno);
184            return E_PATH;
185        }
186        if (chown(dentryPath.c_str(), OID_DFS, OID_DFS) != 0) {
187            LOGE("chown cachepath error %{public}d", errno);
188        }
189    }
190    if (path.find("cloud_fuse") != string::npos) {
191        std::thread([userId]() {
192            HandleStartMove(userId);
193        }).detach();
194    }
195    return E_OK;
196}
197} // namespace CloudFile
198} // namespace FileManagement
199} // namespace OHOS
200