1 /*
2  * Copyright (c) 2024 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 #include "file_operations_cloud.h"
16 
17 #include <cerrno>
18 #include <sstream>
19 #include <sys/types.h>
20 #include <sys/xattr.h>
21 #include <functional>
22 
23 #include "account_status.h"
24 #include "cloud_disk_inode.h"
25 #include "cloud_file_fault_event.h"
26 #include "cloud_file_kit.h"
27 #include "cloud_file_utils.h"
28 #include "clouddisk_rdb_transaction.h"
29 #include "clouddisk_rdb_utils.h"
30 #include "clouddisk_notify.h"
31 #include "database_manager.h"
32 #include "directory_ex.h"
33 #include "ffrt_inner.h"
34 #include "parameter.h"
35 #include "parameters.h"
36 #include "file_operations_helper.h"
37 #include "hitrace_meter.h"
38 #include "securec.h"
39 #include "utils_log.h"
40 
41 namespace OHOS {
42 namespace FileManagement {
43 namespace CloudDisk {
44 using namespace std;
45 using namespace CloudFile;
46 enum XATTR_CODE {
47     ERROR_CODE = -1,
48     HMDFS_PERMISSION,
49     CLOUD_LOCATION,
50     CLOUD_RECYCLE,
51     IS_FAVORITE
52 };
53 namespace {
54     static const uint32_t STAT_NLINK_REG = 1;
55     static const uint32_t STAT_NLINK_DIR = 2;
56     static const uint32_t CLOUD_FILE_LAYER = 2;
57     static const uint32_t USER_LOCAL_ID_OFFSET = 100;
58     static const uint32_t STAT_MODE_REG = 0660;
59     static const uint32_t STAT_MODE_DIR = 0771;
60     static const uint32_t MILLISECOND_TO_SECONDS_TIMES = 1000;
61     static const uint32_t RECYCLE_LOCAL_ID = 4;
62     static const string FILE_LOCAL = "1";
63     static const string ROOT_CLOUD_ID = "rootId";
64     static const string RECYCLE_NAME = ".trash";
65     static const uint64_t UNKNOWN_INODE_ID = 0;
66     static const std::string FILEMANAGER_KEY = "persist.kernel.bundle_name.filemanager";
67     static const unsigned int MAX_READ_SIZE = 4 * 1024 * 1024;
68 }
69 
InitInodeAttr(struct CloudDiskFuseData *data, fuse_ino_t parent, struct CloudDiskInode *childInode, const MetaBase &metaBase, const int64_t &inodeId)70 static void InitInodeAttr(struct CloudDiskFuseData *data, fuse_ino_t parent,
71     struct CloudDiskInode *childInode, const MetaBase &metaBase, const int64_t &inodeId)
72 {
73     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
74     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
75         static_cast<int64_t>(parent));
76     childInode->stat = parentInode->stat;
77     childInode->stat.st_ino = static_cast<uint64_t>(inodeId);
78     childInode->stat.st_mtime = metaBase.mtime / MILLISECOND_TO_SECONDS_TIMES;
79     childInode->stat.st_atime = metaBase.atime / MILLISECOND_TO_SECONDS_TIMES;
80 
81     childInode->bundleName = parentInode->bundleName;
82     childInode->fileName = metaBase.name;
83     childInode->layer = FileOperationsHelper::GetNextLayer(parentInode, parent);
84     childInode->parent = parent;
85     childInode->cloudId = metaBase.cloudId;
86     childInode->ops = make_shared<FileOperationsCloud>();
87 
88     if (S_ISDIR(metaBase.mode)) {
89         childInode->stat.st_mode = S_IFDIR | STAT_MODE_DIR;
90         childInode->stat.st_nlink = STAT_NLINK_DIR;
91     } else {
92         childInode->stat.st_mode = S_IFREG | STAT_MODE_REG;
93         childInode->stat.st_nlink = STAT_NLINK_REG;
94         childInode->stat.st_size = metaBase.size;
95     }
96 }
97 
InitFileAttr(struct CloudDiskFuseData *data, struct fuse_file_info *fi)98 static shared_ptr<CloudDiskFile> InitFileAttr(struct CloudDiskFuseData *data, struct fuse_file_info *fi)
99 {
100     std::unique_lock<std::shared_mutex> wLock(data->fileLock, std::defer_lock);
101     shared_ptr<CloudDiskFile> filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
102     if (filePtr == nullptr) {
103         filePtr = make_shared<CloudDiskFile>();
104         wLock.lock();
105         data->fileCache[fi->fh] = filePtr;
106         wLock.unlock();
107     }
108     filePtr->refCount++;
109     return filePtr;
110 }
111 
InitLocalIdCache(struct CloudDiskFuseData *data, const std::string &key, const int64_t val)112 static void InitLocalIdCache(struct CloudDiskFuseData *data, const std::string &key, const int64_t val)
113 {
114     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
115     std::unique_lock<std::shared_mutex> wLock(data->localIdLock, std::defer_lock);
116     int64_t localId = FileOperationsHelper::FindLocalId(data, key);
117     if (localId == -1) {
118         wLock.lock();
119         data->localIdCache[key] = val;
120         wLock.unlock();
121     }
122 }
123 
LookUpRecycleBin(struct CloudDiskFuseData *data, fuse_ino_t parent, shared_ptr<CloudDiskInode> parentInode, struct fuse_entry_param *e)124 static void LookUpRecycleBin(struct CloudDiskFuseData *data, fuse_ino_t parent,
125     shared_ptr<CloudDiskInode> parentInode, struct fuse_entry_param *e)
126 {
127     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
128     std::unique_lock<std::shared_mutex> cacheWLock(data->cacheLock, std::defer_lock);
129     auto child = FileOperationsHelper::FindCloudDiskInode(data, RECYCLE_LOCAL_ID);
130     if (child == nullptr) {
131         child = make_shared<CloudDiskInode>();
132         child->stat = parentInode->stat;
133         child->stat.st_ino = RECYCLE_LOCAL_ID;
134         child->bundleName = parentInode->bundleName;
135         child->fileName = RECYCLE_NAME;
136         child->layer = FileOperationsHelper::GetNextLayer(parentInode, parent);
137         child->parent = parent;
138         child->cloudId = RECYCLE_CLOUD_ID;
139         child->ops = make_shared<FileOperationsCloud>();
140         child->stat.st_mode = S_IFDIR | STAT_MODE_DIR;
141         child->stat.st_nlink = STAT_NLINK_DIR;
142         cacheWLock.lock();
143         data->inodeCache[RECYCLE_LOCAL_ID] = child;
144         cacheWLock.unlock();
145     }
146     e->ino = static_cast<fuse_ino_t>(RECYCLE_LOCAL_ID);
147     FileOperationsHelper::GetInodeAttr(child, &e->attr);
148 }
149 
UpdateChildCache(struct CloudDiskFuseData *data, int64_t localId, shared_ptr<CloudDiskInode> child)150 static shared_ptr<CloudDiskInode> UpdateChildCache(struct CloudDiskFuseData *data, int64_t localId,
151     shared_ptr<CloudDiskInode> child)
152 {
153     std::unique_lock<std::shared_mutex> cacheWLock(data->cacheLock, std::defer_lock);
154     std::unique_lock<std::shared_mutex> localIdWLock(data->localIdLock, std::defer_lock);
155     if (child == nullptr) {
156         child = make_shared<CloudDiskInode>();
157         cacheWLock.lock();
158         data->inodeCache[localId] = child;
159         cacheWLock.unlock();
160     } else {
161         auto old_key = std::to_string(child->parent) + child->fileName;
162         localIdWLock.lock();
163         data->localIdCache.erase(old_key);
164         localIdWLock.unlock();
165     }
166     return child;
167 }
168 
LookupRecycledFile(struct CloudDiskFuseData *data, const char *name, const std::string bundleName, struct fuse_entry_param *e)169 static int32_t LookupRecycledFile(struct CloudDiskFuseData *data, const char *name,
170     const std::string bundleName, struct fuse_entry_param *e)
171 {
172     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
173     MetaBase metaBase(name);
174     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId, bundleName,
175         RECYCLE_CLOUD_ID);
176     int ret = metaFile->DoLookup(metaBase);
177     if (ret != 0) {
178         LOGE("file %{public}s not found in recyclebin", GetAnonyString(name).c_str());
179         return EINVAL;
180     }
181     int64_t inodeId = static_cast<int64_t>(CloudFileUtils::DentryHash(metaBase.cloudId));
182     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, inodeId);
183     if (inoPtr == nullptr) {
184         string nameStr = name;
185         size_t lastSlash = nameStr.find_last_of("_");
186         metaBase.name = nameStr.substr(0, lastSlash);
187         inoPtr = UpdateChildCache(data, inodeId, inoPtr);
188         inoPtr->refCount++;
189         InitInodeAttr(data, RECYCLE_LOCAL_ID, inoPtr.get(), metaBase, inodeId);
190         inoPtr->parent = UNKNOWN_INODE_ID;
191     }
192     e->ino = static_cast<fuse_ino_t>(inodeId);
193     FileOperationsHelper::GetInodeAttr(inoPtr, &e->attr);
194     return 0;
195 }
196 
DoCloudLookup(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse_entry_param *e)197 static int32_t DoCloudLookup(fuse_req_t req, fuse_ino_t parent, const char *name,
198                              struct fuse_entry_param *e)
199 {
200     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
201     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
202     if (parent == FUSE_ROOT_ID) {
203         LOGE("cloud file operations should not get a fuse root inode");
204         return EINVAL;
205     }
206 
207     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
208         static_cast<int64_t>(parent));
209     if (parentInode == nullptr) {
210         LOGE("fail to find parent inode");
211         return EINVAL;
212     }
213     if (name == RECYCLE_NAME) {
214         LookUpRecycleBin(data, parent, parentInode, e);
215         return 0;
216     } else if (parent == RECYCLE_LOCAL_ID) {
217         int32_t ret = LookupRecycledFile(data, name, parentInode->bundleName, e);
218         if (ret != 0) {
219             LOGE("fail to lookup recycledfile");
220             return ret;
221         }
222         return 0;
223     }
224     MetaBase metaBase(name);
225     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId, parentInode->bundleName,
226         parentInode->cloudId);
227     int32_t ret = metaFile->DoLookup(metaBase);
228     if (ret != 0) {
229         LOGE("lookup dentry failed, name:%{public}s, ret = %{public}d", GetAnonyString(name).c_str(), ret);
230         return ENOENT;
231     }
232     string key = std::to_string(parent) + name;
233     int64_t inodeId = static_cast<int64_t>(CloudFileUtils::DentryHash(metaBase.cloudId));
234     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, inodeId);
235     // if inoPtr is nullptr, UpdateChildCache will create it
236     auto child = UpdateChildCache(data, inodeId, inoPtr);
237     child->refCount++;
238     InitInodeAttr(data, parent, child.get(), metaBase, inodeId);
239     InitLocalIdCache(data, key, inodeId);
240     e->ino = static_cast<fuse_ino_t>(inodeId);
241     FileOperationsHelper::GetInodeAttr(child, &e->attr);
242     return 0;
243 }
244 
Lookup(fuse_req_t req, fuse_ino_t parent, const char *name)245 void FileOperationsCloud::Lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
246 {
247     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
248     struct fuse_entry_param e;
249     e.attr_timeout = 1.0;
250     e.entry_timeout = 1.0;
251     int32_t err = DoCloudLookup(req, parent, name, &e);
252     if (err) {
253         fuse_reply_err(req, err);
254     } else {
255         fuse_reply_entry(req, &e);
256     }
257 }
258 
Access(fuse_req_t req, fuse_ino_t ino, int mask)259 void FileOperationsCloud::Access(fuse_req_t req, fuse_ino_t ino, int mask)
260 {
261     LOGI("Access operation is not supported!");
262     fuse_reply_err(req, ENOSYS);
263 }
264 
GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)265 void FileOperationsCloud::GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
266 {
267     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
268     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
269     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
270     if (inoPtr == nullptr) {
271         LOGE("inode not found");
272         fuse_reply_err(req, EINVAL);
273         return;
274     }
275     fuse_reply_attr(req, &inoPtr->stat, 0);
276 }
277 
HandleCloudError(fuse_req_t req, CloudError error)278 static bool HandleCloudError(fuse_req_t req, CloudError error)
279 {
280     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
281     if (error == CloudError::CK_NO_ERROR) {
282         return false;
283     }
284     if (error == CloudError::CK_NETWORK_ERROR) {
285         LOGE("network error");
286         fuse_reply_err(req, ENOTCONN);
287     } else if (error == CloudError::CK_SERVER_ERROR) {
288         LOGE("server error");
289         fuse_reply_err(req, EIO);
290     } else if (error == CloudError::CK_LOCAL_ERROR) {
291         LOGE("local error");
292         fuse_reply_err(req, EINVAL);
293     } else {
294         LOGE("Unknow error");
295         fuse_reply_err(req, EIO);
296     }
297     return true;
298 }
299 
GetDatabase(int32_t userId, const string &bundleName)300 static shared_ptr<CloudDatabase> GetDatabase(int32_t userId, const string &bundleName)
301 {
302     auto instance = CloudFile::CloudFileKit::GetInstance();
303     if (instance == nullptr) {
304         LOGE("get cloud file helper instance failed");
305         return nullptr;
306     }
307 
308     if (AccountStatus::IsNeedCleanCache()) {
309         auto ret = instance->CleanCloudUserInfo(userId);
310         if (ret != 0) {
311             return nullptr;
312         }
313         LOGI("execute clean cloud user info success");
314     }
315 
316     auto database = instance->GetCloudDatabase(userId, bundleName);
317     if (database == nullptr) {
318         LOGE("get cloud file kit database fail");
319         return nullptr;
320     }
321     return database;
322 }
323 
CloudOpen(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr, struct fuse_file_info *fi, string path)324 static void CloudOpen(fuse_req_t req,
325     shared_ptr<CloudDiskInode> inoPtr, struct fuse_file_info *fi, string path)
326 {
327     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
328     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
329     auto filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
330     if (filePtr == nullptr) {
331         filePtr = InitFileAttr(data, fi);
332     }
333     auto database = GetDatabase(data->userId, inoPtr->bundleName);
334     if (!database) {
335         fuse_reply_err(req, EPERM);
336         LOGE("database is null");
337         return;
338     }
339 
340     if (filePtr->readSession) {
341         filePtr->type = CLOUD_DISK_FILE_TYPE_CLOUD;
342         fuse_reply_open(req, fi);
343         return;
344     }
345 
346     string cloudId = inoPtr->cloudId;
347     LOGD("cloudId: %s", cloudId.c_str());
348     filePtr->readSession = database->NewAssetReadSession("file", cloudId, "content", path);
349     if (filePtr->readSession) {
350         auto error = filePtr->readSession->InitSession();
351         if (!HandleCloudError(req, error)) {
352             filePtr->type = CLOUD_DISK_FILE_TYPE_CLOUD;
353             fuse_reply_open(req, fi);
354         } else {
355             filePtr->readSession = nullptr;
356             LOGE("open fail");
357         }
358     } else {
359         fuse_reply_err(req, EPERM);
360         LOGE("readSession is null");
361     }
362     return;
363 }
364 
Open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)365 void FileOperationsCloud::Open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
366 {
367     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
368         LOGE("wait move error");
369         return (void) fuse_reply_err(req, EBUSY);
370     }
371     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
372     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
373     std::unique_lock<std::shared_mutex> wLock(data->fileIdLock, std::defer_lock);
374     wLock.lock();
375     data->fileId++;
376     fi->fh = static_cast<uint64_t>(data->fileId);
377     wLock.unlock();
378     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
379     if (inoPtr == nullptr) {
380         LOGE("inode not found");
381         fuse_reply_err(req, EINVAL);
382         return;
383     }
384     string path = CloudFileUtils::GetLocalFilePath(inoPtr->cloudId, inoPtr->bundleName, data->userId);
385     unsigned int flags = static_cast<unsigned int>(fi->flags);
386     if (access(path.c_str(), F_OK) == 0) {
387         if ((flags & O_ACCMODE) & O_WRONLY) {
388             flags &= ~O_WRONLY;
389             flags |= O_RDWR;
390         }
391         if (flags & O_APPEND) {
392             flags &= ~O_APPEND;
393         }
394         if (flags & O_DIRECT) {
395             flags &= ~O_DIRECT;
396         }
397         int32_t fd = open(path.c_str(), flags);
398         if (fd < 0) {
399             LOGE("open file failed path:%{public}s errno:%{public}d", GetAnonyString(path).c_str(), errno);
400             return (void) fuse_reply_err(req, errno);
401         }
402         auto filePtr = InitFileAttr(data, fi);
403         filePtr->type = CLOUD_DISK_FILE_TYPE_LOCAL;
404         filePtr->fd = fd;
405         filePtr->isWriteOpen = (flags & O_RDWR) | (flags & O_WRONLY);
406         fuse_reply_open(req, fi);
407     } else {
408         CloudOpen(req, inoPtr, fi, path);
409     }
410 }
411 
CreateLocalFile(const string &cloudId, const string &bundleName, int32_t userId, mode_t mode)412 static int32_t CreateLocalFile(const string &cloudId, const string &bundleName, int32_t userId, mode_t mode)
413 {
414     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
415     string bucketPath = CloudFileUtils::GetLocalBucketPath(cloudId, bundleName, userId);
416     string path = CloudFileUtils::GetLocalFilePath(cloudId, bundleName, userId);
417     if (access(bucketPath.c_str(), F_OK) != 0) {
418         if (mkdir(bucketPath.c_str(), STAT_MODE_DIR) != 0) {
419             LOGE("mkdir bucketpath failed :%{public}s err:%{public}d", GetAnonyString(bucketPath).c_str(), errno);
420             return -errno;
421         }
422     }
423     int32_t fd = open(path.c_str(), (mode & O_NOFOLLOW) | O_CREAT | O_RDWR, STAT_MODE_REG);
424     if (fd < 0) {
425         LOGE("create file failed :%{public}s err:%{public}d", GetAnonyString(path).c_str(), errno);
426         return -errno;
427     }
428     return fd;
429 }
430 
RemoveLocalFile(const string &path)431 void RemoveLocalFile(const string &path)
432 {
433     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
434     int32_t err = remove(path.c_str());
435     if (err != 0) {
436         LOGE("remove file %{public}s failed, error:%{public}d", GetAnonyString(path).c_str(), errno);
437     }
438 }
439 
GenerateCloudId(int32_t userId, string &cloudId, const string &bundleName)440 int32_t GenerateCloudId(int32_t userId, string &cloudId, const string &bundleName)
441 {
442     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
443     auto dkDatabasePtr = GetDatabase(userId, bundleName);
444     if (dkDatabasePtr == nullptr) {
445         LOGE("Failed to get database");
446         return ENOSYS;
447     }
448 
449     vector<std::string> ids;
450     auto ret = dkDatabasePtr->GenerateIds(1, ids);
451     if (ret != 0 || ids.size() == 0) {
452         return ENOSYS;
453     }
454     cloudId = ids[0];
455     return 0;
456 }
457 
GetParentUpload(shared_ptr<CloudDiskInode> parentInode, struct CloudDiskFuseData *data, bool &parentNoUpload)458 static int32_t GetParentUpload(shared_ptr<CloudDiskInode> parentInode, struct CloudDiskFuseData *data,
459                                bool &parentNoUpload)
460 {
461     auto grandparentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(parentInode->parent));
462     if (grandparentInode == nullptr) {
463         LOGE("grandparentInode not found");
464         return EINVAL;
465     }
466     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId,
467         grandparentInode->bundleName, grandparentInode->cloudId);
468     MetaBase metaBase(parentInode->fileName);
469     auto ret = metaFile->DoLookup(metaBase);
470     if (ret != 0) {
471         LOGE("file %{public}s not found", parentInode->fileName.c_str());
472         return ret;
473     }
474     parentNoUpload = (metaBase.noUpload == NO_UPLOAD);
475     return 0;
476 }
477 
DoCreatFile(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_entry_param &e)478 int32_t DoCreatFile(fuse_req_t req, fuse_ino_t parent, const char *name,
479                     mode_t mode, struct fuse_entry_param &e)
480 {
481     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
482     struct CloudDiskFuseData *data =
483         reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
484     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
485         static_cast<int64_t>(parent));
486 
487     string cloudId;
488     int32_t err = GenerateCloudId(data->userId, cloudId, parentInode->bundleName);
489     if (err != 0) {
490         LOGE("Failed to generate cloud id");
491         return -err;
492     }
493 
494     int32_t fd = CreateLocalFile(cloudId, parentInode->bundleName, data->userId, mode);
495     if (fd < 0) {
496         LOGD("Create local file failed error:%{public}d", fd);
497         return fd;
498     }
499 
500     string path = CloudFileUtils::GetLocalFilePath(cloudId, parentInode->bundleName, data->userId);
501 
502     bool noNeedUpload = false;
503     if (parentInode->cloudId != ROOT_CLOUD_ID) {
504         err = GetParentUpload(parentInode, data, noNeedUpload);
505         if (err != 0) {
506             LOGE("Failed to get parent no upload");
507             close(fd);
508             RemoveLocalFile(path);
509             return err;
510         }
511     }
512 
513     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
514     shared_ptr<CloudDiskRdbStore> rdbStore =
515         databaseManager.GetRdbStore(parentInode->bundleName, data->userId);
516     err = rdbStore->Create(cloudId, parentInode->cloudId, name, noNeedUpload);
517     if (err != 0) {
518         close(fd);
519         RemoveLocalFile(path);
520         return -err;
521     }
522     err = DoCloudLookup(req, parent, name, &e);
523     if (err != 0) {
524         close(fd);
525         RemoveLocalFile(path);
526         return -err;
527     }
528     return fd;
529 }
530 
MkNod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)531 void FileOperationsCloud::MkNod(fuse_req_t req, fuse_ino_t parent, const char *name,
532                                 mode_t mode, dev_t rdev)
533 {
534     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
535         LOGE("wait move error");
536         return (void) fuse_reply_err(req, EBUSY);
537     }
538     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
539     struct fuse_entry_param e;
540     int32_t err = DoCreatFile(req, parent, name, mode, e);
541     if (err < 0) {
542         fuse_reply_err(req, -err);
543         return;
544     }
545     close(err);
546     fuse_reply_entry(req, &e);
547 }
548 
Create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)549 void FileOperationsCloud::Create(fuse_req_t req, fuse_ino_t parent, const char *name,
550                                  mode_t mode, struct fuse_file_info *fi)
551 {
552     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
553         LOGE("wait move error");
554         return (void) fuse_reply_err(req, EBUSY);
555     }
556     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
557     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
558     struct fuse_entry_param e;
559     int32_t err = DoCreatFile(req, parent, name, mode, e);
560     if (err < 0) {
561         fuse_reply_err(req, -err);
562         return;
563     }
564     auto filePtr = InitFileAttr(data, fi);
565     std::unique_lock<std::shared_mutex> wLock(data->fileIdLock, std::defer_lock);
566     wLock.lock();
567     data->fileId++;
568     fi->fh = static_cast<uint64_t>(data->fileId);
569     wLock.unlock();
570     filePtr->fd = err;
571     filePtr->type = CLOUD_DISK_FILE_TYPE_LOCAL;
572     filePtr->fileDirty = CLOUD_DISK_FILE_CREATE;
573     fuse_reply_create(req, &e, fi);
574 }
575 
FindNextPos(const vector<CloudDiskFileInfo> &childInfos, off_t off)576 static size_t FindNextPos(const vector<CloudDiskFileInfo> &childInfos, off_t off)
577 {
578     for (size_t i = 0; i < childInfos.size(); i++) {
579         /* Find the first valid offset beyond @off */
580         if (childInfos[i].nextOff > off) {
581             return i + 1;
582         }
583     }
584     /* If @off is beyond all valid offset, then return the index after the last info */
585     if (!childInfos.empty() && childInfos.back().nextOff < off) {
586         return childInfos.size();
587     }
588     return 0;
589 }
590 
FindNextPos(const vector<MetaBase> &childInfos, off_t off)591 static size_t FindNextPos(const vector<MetaBase> &childInfos, off_t off)
592 {
593     for (size_t i = 0; i < childInfos.size(); i++) {
594         /* Find the first valid offset beyond @off */
595         if (childInfos[i].nextOff > off) {
596             return i + 1;
597         }
598     }
599     /* If @off is beyond all valid offset, then return the index after the last info */
600     if (!childInfos.empty() && childInfos.back().nextOff < off) {
601         return childInfos.size();
602     }
603     return 0;
604 }
605 
GetChildInfos(fuse_req_t req, fuse_ino_t ino, vector<CloudDiskFileInfo> &childInfos)606 static int32_t GetChildInfos(fuse_req_t req, fuse_ino_t ino, vector<CloudDiskFileInfo> &childInfos)
607 {
608     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
609     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
610     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
611     if (inoPtr == nullptr) {
612         LOGE("inode not found");
613         return EINVAL;
614     }
615     string parentCloudId = inoPtr->cloudId;
616 
617     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
618     shared_ptr<CloudDiskRdbStore> rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
619     int32_t err = rdbStore->ReadDir(parentCloudId, childInfos);
620     if (err != 0) {
621         LOGE("Readdir failed cloudId:%{public}s err:%{public}d", parentCloudId.c_str(), err);
622         return err;
623     }
624     return 0;
625 }
626 
627 template<typename T>
CloudSeekDir(fuse_req_t req, fuse_ino_t ino, off_t off, const std::vector<T> &childInfos)628 static size_t CloudSeekDir(fuse_req_t req, fuse_ino_t ino, off_t off,
629                            const std::vector<T> &childInfos)
630 {
631     if (off == 0 || childInfos.empty()) {
632         return 0;
633     }
634 
635     size_t i = 0;
636     for (; i < childInfos.size(); i++) {
637         if (childInfos[i].nextOff == off) {
638             /* Start position should be the index of next entry */
639             return i + 1;
640         }
641     }
642     if (i == childInfos.size()) {
643         /* The directory may changed recently, find the next valid index for this offset */
644         return FindNextPos(childInfos, off);
645     }
646 
647     return 0;
648 }
649 
650 template<typename T>
AddDirEntryToBuf(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, const std::vector<T> &childInfos)651 static void AddDirEntryToBuf(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
652     const std::vector<T> &childInfos)
653 {
654     size_t startPos = CloudSeekDir<T>(req, ino, off, childInfos);
655     string buf;
656     buf.resize(size);
657     if (childInfos.empty() || startPos == childInfos.size()) {
658         LOGW("empty buffer replied");
659         return (void)fuse_reply_buf(req, buf.c_str(), 0);
660     }
661 
662     size_t nextOff = 0;
663     size_t remain = size;
664     static const struct stat statInfoDir = { .st_mode = S_IFDIR | STAT_MODE_DIR };
665     static const struct stat statInfoReg = { .st_mode = S_IFREG | STAT_MODE_REG };
666     for (size_t i = startPos; i < childInfos.size(); i++) {
667         size_t alignSize = CloudDiskRdbUtils::FuseDentryAlignSize(childInfos[i].name.c_str());
668         if (alignSize > remain) {
669             break;
670         }
671         alignSize = fuse_add_direntry(req, &buf[nextOff], alignSize, childInfos[i].name.c_str(),
672             childInfos[i].mode != S_IFREG ? &statInfoDir : &statInfoReg,
673             off + static_cast<off_t>(nextOff) + static_cast<off_t>(alignSize));
674         nextOff += alignSize;
675         remain -= alignSize;
676     }
677     (void)fuse_reply_buf(req, buf.c_str(), size - remain);
678 }
679 
ReadDirForRecycle(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)680 static void ReadDirForRecycle(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
681                               struct fuse_file_info *fi)
682 {
683     int32_t err = -1;
684     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
685     auto inode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
686     if (inode == nullptr) {
687         LOGE("inode not found");
688         fuse_reply_err(req, EINVAL);
689         return;
690     }
691     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId,
692         inode->bundleName, RECYCLE_NAME);
693     std::vector<MetaBase> childInfos;
694     err = metaFile->LoadChildren(childInfos);
695     if (err != 0) {
696         LOGE("load children failed, err=%{public}d", err);
697         fuse_reply_err(req, EINVAL);
698         return;
699     }
700     size_t nextOff = 0;
701     for (size_t i = 0; i < childInfos.size(); ++i) {
702         size_t alignSize = CloudDiskRdbUtils::FuseDentryAlignSize(childInfos[i].name.c_str());
703         nextOff += alignSize;
704         childInfos[i].nextOff = static_cast<off_t>(nextOff);
705     }
706     AddDirEntryToBuf(req, ino, size, off, childInfos);
707 }
708 
ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)709 void FileOperationsCloud::ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
710                                   struct fuse_file_info *fi)
711 {
712     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
713         LOGE("wait move error");
714         return (void) fuse_reply_err(req, EBUSY);
715     }
716     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
717     if (ino == RECYCLE_LOCAL_ID) {
718         ReadDirForRecycle(req, ino, size, off, fi);
719         return;
720     }
721 
722     vector<CloudDiskFileInfo> childInfos;
723     int32_t err = GetChildInfos(req, ino, childInfos);
724     if (err != 0) {
725         LOGE("failed to get child infos, err=%{public}d", err);
726         return (void)fuse_reply_err(req, err);
727     }
728     AddDirEntryToBuf(req, ino, size, off, childInfos);
729 }
730 
CheckXattr(const char *name)731 int32_t CheckXattr(const char *name)
732 {
733     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
734     LOGD("start CheckXattr name is:%{public}s", name);
735     if (CloudFileUtils::CheckIsHmdfsPermission(name)) {
736         return HMDFS_PERMISSION;
737     } else if (CloudFileUtils::CheckIsCloudLocation(name)) {
738         return CLOUD_LOCATION;
739     } else if (CloudFileUtils::CheckIsCloudRecycle(name)) {
740         return CLOUD_RECYCLE;
741     } else if (CloudFileUtils::CheckIsFavorite(name)) {
742         return IS_FAVORITE;
743     } else {
744         LOGD("no definition Xattr name:%{public}s", name);
745         return ERROR_CODE;
746     }
747 }
748 
HandleCloudLocation(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value)749 void HandleCloudLocation(fuse_req_t req, fuse_ino_t ino, const char *name,
750                          const char *value)
751 {
752     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
753     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
754     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
755     if (inoPtr == nullptr) {
756         fuse_reply_err(req, EINVAL);
757         LOGE("inode not found");
758         return;
759     }
760     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(inoPtr->parent));
761     if (parentInode == nullptr) {
762         LOGE("parent inode not found");
763         return (void) fuse_reply_err(req, EINVAL);
764     }
765     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
766     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
767     int32_t err = rdbStore->SetXAttr(inoPtr->cloudId, CLOUD_FILE_LOCATION, value, inoPtr->fileName,
768         parentInode->cloudId);
769     if (err != 0) {
770         LOGE("set cloud id fail %{public}d", err);
771         fuse_reply_err(req, EINVAL);
772         return;
773     }
774     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
775         NotifyOpsType::DAEMON_SETXATTR, inoPtr});
776     fuse_reply_err(req, 0);
777 }
778 
HandleCloudRecycle(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value)779 void HandleCloudRecycle(fuse_req_t req, fuse_ino_t ino, const char *name,
780                         const char *value)
781 {
782     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
783     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
784     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
785     if (inoPtr == nullptr) {
786         fuse_reply_err(req, EINVAL);
787         LOGE("inode not found");
788         return;
789     }
790     string parentCloudId;
791     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
792     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(inoPtr->parent));
793     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
794     if (parentInode == nullptr) {
795         int32_t ret = rdbStore->GetParentCloudId(inoPtr->cloudId, parentCloudId);
796         if (ret != 0) {
797             fuse_reply_err(req, EINVAL);
798             LOGE("fail to get parentCloudId");
799             return;
800         }
801     } else {
802         parentCloudId = parentInode->cloudId;
803     }
804     int32_t ret = MetaFileMgr::GetInstance().CreateRecycleDentry(data->userId, inoPtr->bundleName);
805     if (ret != 0) {
806         fuse_reply_err(req, EINVAL);
807         LOGE("create recycle dentry failed");
808         return;
809     }
810     ret = rdbStore->SetXAttr(inoPtr->cloudId, CLOUD_CLOUD_RECYCLE_XATTR, value,
811         inoPtr->fileName, parentCloudId);
812     if (ret != 0) {
813         LOGE("set cloud id fail %{public}d", ret);
814         fuse_reply_err(req, EINVAL);
815         return;
816     }
817     int32_t val = std::stoi(value);
818     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
819         val == 0 ? NotifyOpsType::DAEMON_RESTORE : NotifyOpsType::DAEMON_RECYCLE, inoPtr});
820     fuse_reply_err(req, 0);
821 }
822 
HandleFavorite(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value)823 void HandleFavorite(fuse_req_t req, fuse_ino_t ino, const char *name,
824                     const char *value)
825 {
826     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
827     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
828     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
829     if (inoPtr == nullptr) {
830         fuse_reply_err(req, EINVAL);
831         LOGE("inode not found");
832         return;
833     }
834     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
835     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
836     int32_t err = rdbStore->SetXAttr(inoPtr->cloudId, IS_FAVORITE_XATTR, value);
837     if (err != 0) {
838         LOGE("set cloud id fail %{public}d", err);
839         fuse_reply_err(req, EINVAL);
840         return;
841     }
842     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
843         NotifyOpsType::DAEMON_SETXATTR, inoPtr});
844     fuse_reply_err(req, 0);
845 }
846 
HandleExtAttribute(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value)847 void HandleExtAttribute(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value)
848 {
849     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
850     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
851     if (inoPtr == nullptr) {
852         fuse_reply_err(req, EINVAL);
853         LOGE("inode not found");
854         return;
855     }
856     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
857     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
858     int32_t err = rdbStore->SetXAttr(inoPtr->cloudId, CLOUD_EXT_ATTR, value, name);
859     if (err != 0) {
860         LOGE("set cloud id fail %{public}d", err);
861         fuse_reply_err(req, EINVAL);
862         return;
863     }
864     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
865         NotifyOpsType::DAEMON_SETXATTR, inoPtr});
866     fuse_reply_err(req, 0);
867 }
868 
SetXattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)869 void FileOperationsCloud::SetXattr(fuse_req_t req, fuse_ino_t ino, const char *name,
870                                    const char *value, size_t size, int flags)
871 {
872     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
873         LOGE("wait move error");
874         return (void) fuse_reply_err(req, EBUSY);
875     }
876     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
877     LOGD("Setxattr begin name:%{public}s", name);
878     int32_t checknum = CheckXattr(name);
879     switch (checknum) {
880         case HMDFS_PERMISSION:
881             fuse_reply_err(req, 0);
882             break;
883         case CLOUD_LOCATION:
884             HandleCloudLocation(req, ino, name, value);
885             break;
886         case CLOUD_RECYCLE:
887             HandleCloudRecycle(req, ino, name, value);
888             break;
889         case IS_FAVORITE:
890             HandleFavorite(req, ino, name, value);
891             break;
892         default:
893             HandleExtAttribute(req, ino, name, value);
894             break;
895     }
896 }
897 
GetIsFavorite(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr)898 string GetIsFavorite(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr)
899 {
900     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
901     string favorite;
902     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
903     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
904     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
905     int res = rdbStore->GetXAttr(inoPtr->cloudId, IS_FAVORITE_XATTR, favorite);
906     if (res != 0) {
907         LOGE("local file get isFavorite fail");
908         return "null";
909     }
910     return favorite;
911 }
912 
GetFileStatus(fuse_req_t req, struct CloudDiskInode *inoPtr)913 static string GetFileStatus(fuse_req_t req, struct CloudDiskInode *inoPtr)
914 {
915     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
916     string fileStatus;
917     if (inoPtr == nullptr) {
918         LOGE("inoPtr is null");
919         return "null";
920     }
921     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
922     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
923     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
924     int res = rdbStore->GetXAttr(inoPtr->cloudId, IS_FILE_STATUS_XATTR, fileStatus);
925     if (res != 0) {
926         LOGE("local file get file_status fail");
927         return "null";
928     }
929     return fileStatus;
930 }
931 
GetLocation(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr)932 string GetLocation(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr)
933 {
934     string location;
935     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
936     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
937     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
938     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(inoPtr->parent));
939     if (parentInode == nullptr) {
940         LOGE("parent inode not found");
941         return "null";
942     }
943     CacheNode newNode = {.parentCloudId = parentInode->cloudId, .fileName = inoPtr->fileName};
944     int res = rdbStore->GetXAttr(inoPtr->cloudId, CLOUD_FILE_LOCATION, location, newNode);
945     if (res != 0) {
946         LOGE("local file get location fail");
947         return "null";
948     }
949     return location;
950 }
951 
GetExtAttr(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr, const char *extAttrKey)952 string GetExtAttr(fuse_req_t req, shared_ptr<CloudDiskInode> inoPtr, const char *extAttrKey)
953 {
954     string extAttr;
955     if (inoPtr == nullptr) {
956         LOGE("get ext attr inoPtr is null");
957         return "null";
958     }
959 
960     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
961     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
962     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
963     CacheNode newNode = {};
964     int res = rdbStore->GetXAttr(inoPtr->cloudId, CLOUD_EXT_ATTR, extAttr, newNode, extAttrKey);
965     if (res != 0) {
966         LOGE("get ext attr is null");
967         return "null";
968     }
969     return extAttr;
970 }
971 
GetXattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)972 void FileOperationsCloud::GetXattr(fuse_req_t req, fuse_ino_t ino, const char *name,
973                                    size_t size)
974 {
975     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
976         LOGE("wait move error");
977         return (void) fuse_reply_err(req, EBUSY);
978     }
979     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
980     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
981     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
982     if (inoPtr == nullptr) {
983         fuse_reply_err(req, EINVAL);
984         LOGE("inode not found");
985         return;
986     }
987     string buf;
988     if (CloudFileUtils::CheckIsHmdfsPermission(name)) {
989         buf = to_string(inoPtr->layer + CLOUD_FILE_LAYER);
990     } else if (CloudFileUtils::CheckIsCloud(name)) {
991         buf = inoPtr->cloudId;
992     } else if (CloudFileUtils::CheckIsFavorite(name)) {
993         buf = GetIsFavorite(req, inoPtr);
994     } else if (CloudFileUtils::CheckFileStatus(name)) {
995         buf = GetFileStatus(req, inoPtr.get());
996     } else if (CloudFileUtils::CheckIsCloudLocation(name)) {
997         buf = GetLocation(req, inoPtr);
998     } else {
999         buf = GetExtAttr(req, inoPtr, name);
1000     }
1001     if (buf == "null") {
1002         fuse_reply_err(req, ENODATA);
1003         return;
1004     }
1005     if (size == 0) {
1006         fuse_reply_xattr(req, buf.size());
1007         return;
1008     }
1009     if (buf.size() > size) {
1010         fuse_reply_err(req, ERANGE);
1011         return;
1012     }
1013     if (buf.size() > 0) {
1014         fuse_reply_buf(req, buf.c_str(), buf.size());
1015     } else {
1016         fuse_reply_err(req, 0);
1017     }
1018 }
1019 
MkDir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)1020 void FileOperationsCloud::MkDir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
1021 {
1022     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
1023         LOGE("wait move error");
1024         return (void) fuse_reply_err(req, EBUSY);
1025     }
1026     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1027     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1028     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(parent));
1029     if (parentInode == nullptr) {
1030         LOGE("parent inode not found");
1031         return (void) fuse_reply_err(req, EINVAL);
1032     }
1033     string fileName = name;
1034     bool noNeedUpload;
1035     int32_t err = 0;
1036     if (fileName == ".thumbs" && parentInode->cloudId == ROOT_CLOUD_ID) {
1037         noNeedUpload = true;
1038     } else if (parentInode->cloudId != ROOT_CLOUD_ID) {
1039         err = GetParentUpload(parentInode, data, noNeedUpload);
1040         if (err != 0) {
1041             LOGE("Failed to get parent no upload");
1042             return (void) fuse_reply_err(req, err);
1043         }
1044     }
1045     string cloudId;
1046     err = GenerateCloudId(data->userId, cloudId, parentInode->bundleName);
1047     if (err != 0) {
1048         LOGE("Failed to generate cloud id");
1049         return (void) fuse_reply_err(req, err);
1050     }
1051 
1052     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1053     shared_ptr<CloudDiskRdbStore> rdbStore = databaseManager.GetRdbStore(parentInode->bundleName,
1054                                                                          data->userId);
1055     err = rdbStore->MkDir(cloudId, parentInode->cloudId, name, noNeedUpload);
1056     if (err != 0) {
1057         LOGE("Failed to mkdir to DB err:%{public}d", err);
1058         return (void) fuse_reply_err(req, ENOSYS);
1059     }
1060 
1061     struct fuse_entry_param e;
1062     err = DoCloudLookup(req, parent, name, &e);
1063     if (err != 0) {
1064         LOGE("Failed to find dir %{private}s", GetAnonyString(name).c_str());
1065         fuse_reply_err(req, err);
1066     } else {
1067         fuse_reply_entry(req, &e);
1068     }
1069     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1070         NotifyOpsType::DAEMON_MKDIR, parentInode, parent, name});
1071 }
1072 
DoCloudUnlink(fuse_req_t req, fuse_ino_t parent, const char *name)1073 int32_t DoCloudUnlink(fuse_req_t req, fuse_ino_t parent, const char *name)
1074 {
1075     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1076     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1077     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(parent));
1078     if (parentInode == nullptr) {
1079         LOGE("parent inode not found");
1080         return ENOSYS;
1081     }
1082     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1083     shared_ptr<CloudDiskRdbStore> rdbStore = databaseManager.GetRdbStore(parentInode->bundleName, data->userId);
1084     MetaBase metaBase(name);
1085     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId,
1086         parentInode->bundleName, parentInode->cloudId);
1087     int32_t ret = metaFile->DoLookup(metaBase);
1088     if (ret != 0) {
1089         LOGE("lookup denty failed, name:%{public}s", GetAnonyString(name).c_str());
1090         return EINVAL;
1091     }
1092     string cloudId = metaBase.cloudId;
1093     int32_t isDirectory = S_ISDIR(metaBase.mode);
1094     int32_t position = metaBase.position;
1095     ret = metaFile->DoRemove(metaBase);
1096     if (ret != 0) {
1097         LOGE("remove dentry failed, ret = %{public}d", ret);
1098         return ret;
1099     }
1100     LOGD("doUnlink, dentry file has been deleted");
1101     if (isDirectory == FILE && position != CLOUD) {
1102         string localPath = CloudFileUtils::GetLocalFilePath(cloudId, parentInode->bundleName, data->userId);
1103         LOGI("unlink %{public}s", GetAnonyString(localPath).c_str());
1104         ret = unlink(localPath.c_str());
1105         if (errno == ENOENT) {
1106             std::string errMsg = "doCloudUnlink, unlink local file ret ENOENT.";
1107             CLOUD_FILE_FAULT_REPORT(CloudFileFaultInfo{parentInode->bundleName, CloudFile::FaultOperation::UNLINK,
1108                 CloudFile::FaultType::DENTRY_FILE, errno, errMsg});
1109         } else if (ret != 0) {
1110             LOGE("Failed to unlink cloudId:%{private}s, errno:%{public}d", cloudId.c_str(), errno);
1111             (void)metaFile->DoCreate(metaBase);
1112             return ret;
1113         }
1114     }
1115     function<void()> rdbUnlink = [rdbStore, cloudId, position] {
1116         int32_t err = rdbStore->Unlink(cloudId, position);
1117         if (err != 0) {
1118             LOGE("Failed to unlink DB cloudId:%{private}s err:%{public}d", cloudId.c_str(), err);
1119         }
1120     };
1121     ffrt::thread(rdbUnlink).detach();
1122     return 0;
1123 }
1124 
RmDir(fuse_req_t req, fuse_ino_t parent, const char *name)1125 void FileOperationsCloud::RmDir(fuse_req_t req, fuse_ino_t parent, const char *name)
1126 {
1127     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
1128         LOGE("wait move error");
1129         return (void) fuse_reply_err(req, EBUSY);
1130     }
1131     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1132     int32_t err = -1;
1133     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1134     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(parent));
1135     if (parentInode == nullptr) {
1136         LOGE("parent inode not found");
1137         return (void) fuse_reply_err(req, EINVAL);
1138     }
1139     auto parentMetaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId,
1140         parentInode->bundleName, parentInode->cloudId);
1141     MetaBase metaBase(name);
1142     err = parentMetaFile->DoLookup(metaBase);
1143     if (err != 0) {
1144         LOGE("lookup dir failed, err=%{public}d", err);
1145         return (void) fuse_reply_err(req, EINVAL);
1146     }
1147     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId,
1148         parentInode->bundleName, metaBase.cloudId);
1149     std::vector<MetaBase> bases;
1150     err = metaFile->LoadChildren(bases);
1151     if (err != 0) {
1152         LOGE("load children failed, err=%{public}d", err);
1153         return (void) fuse_reply_err(req, EINVAL);
1154     }
1155     if (!bases.empty()) {
1156         LOGE("Directory not empty");
1157         fuse_reply_err(req, ENOTEMPTY);
1158         return;
1159     }
1160     err = DoCloudUnlink(req, parent, name);
1161     if (err != 0) {
1162         fuse_reply_err(req, err);
1163         return;
1164     }
1165     MetaFileMgr::GetInstance()
1166         .Clear(static_cast<uint32_t>(data->userId), parentInode->bundleName, metaBase.cloudId);
1167     string dentryPath = metaFile->GetDentryFilePath();
1168     if (unlink(dentryPath.c_str()) != 0) {
1169         LOGE("fail to delete dentry: %{public}d", errno);
1170     }
1171     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1172         NotifyOpsType::DAEMON_RMDIR, nullptr, parent, name});
1173     return (void) fuse_reply_err(req, 0);
1174 }
1175 
Unlink(fuse_req_t req, fuse_ino_t parent, const char *name)1176 void FileOperationsCloud::Unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
1177 {
1178     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
1179         LOGE("wait move error");
1180         return (void) fuse_reply_err(req, EBUSY);
1181     }
1182     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1183     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1184     int32_t err = DoCloudUnlink(req, parent, name);
1185     if (err != 0) {
1186         fuse_reply_err(req, err);
1187         return;
1188     }
1189     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1190         NotifyOpsType::DAEMON_UNLINK, nullptr, parent, name});
1191     return (void) fuse_reply_err(req, 0);
1192 }
1193 
Rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newParent, const char *newName, unsigned int flags)1194 void FileOperationsCloud::Rename(fuse_req_t req, fuse_ino_t parent, const char *name,
1195                                  fuse_ino_t newParent, const char *newName, unsigned int flags)
1196 {
1197     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
1198         LOGE("wait move error");
1199         return (void) fuse_reply_err(req, EBUSY);
1200     }
1201     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1202     if (flags) {
1203         LOGE("Fuse failed to support flag");
1204         fuse_reply_err(req, EINVAL);
1205         return;
1206     }
1207     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1208     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(parent));
1209     auto newParentInode = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(newParent));
1210     if (!parentInode || !newParentInode) {
1211         LOGE("rename old or new parent not found");
1212         fuse_reply_err(req, EINVAL);
1213         return;
1214     }
1215 
1216     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1217     shared_ptr<CloudDiskRdbStore> rdbStore = databaseManager.GetRdbStore(parentInode->bundleName,
1218                                                                          data->userId);
1219     int32_t err = rdbStore->Rename(parentInode->cloudId, name, newParentInode->cloudId, newName);
1220     if (err != 0) {
1221         fuse_reply_err(req, err);
1222         LOGE("Failed to Rename DB name:%{private}s err:%{public}d", GetAnonyString(name).c_str(), err);
1223         return;
1224     }
1225     bool isDir = false;
1226     string key = std::to_string(parent) + name;
1227     int64_t localId = FileOperationsHelper::FindLocalId(data, key);
1228     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, localId);
1229     if (inoPtr != nullptr) {
1230         inoPtr->fileName = newName;
1231         inoPtr->parent = newParent;
1232         isDir = S_ISDIR(inoPtr->stat.st_mode);
1233     }
1234     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1235         NotifyOpsType::DAEMON_RENAME, nullptr, parent, name, newParent, newName},
1236         {FileStatus::UNKNOW, isDir});
1237     return (void) fuse_reply_err(req, 0);
1238 }
1239 
Read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi)1240 void FileOperationsCloud::Read(fuse_req_t req, fuse_ino_t ino, size_t size,
1241                                off_t offset, struct fuse_file_info *fi)
1242 {
1243     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1244     if (size > MAX_READ_SIZE) {
1245         fuse_reply_err(req, EINVAL);
1246         LOGE("Read size is larger than the kernel pre-read window");
1247         return;
1248     }
1249     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1250     auto filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
1251     if (filePtr == nullptr) {
1252         fuse_reply_err(req, EINVAL);
1253         LOGE("file not found");
1254         return;
1255     }
1256     if (filePtr->type == CLOUD_DISK_FILE_TYPE_LOCAL) {
1257         struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
1258 
1259         buf.buf[0].flags = static_cast<fuse_buf_flags> (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1260         buf.buf[0].fd = filePtr->fd;
1261         buf.buf[0].pos = offset;
1262 
1263         fuse_reply_data(req, &buf, static_cast<fuse_buf_copy_flags> (0));
1264         return;
1265     }
1266 
1267     int64_t readSize;
1268     shared_ptr<char> buf = nullptr;
1269 
1270     buf.reset(new char[size], [](char* ptr) {
1271         delete[] ptr;
1272     });
1273 
1274     if (!buf) {
1275         fuse_reply_err(req, ENOMEM);
1276         LOGE("buffer is null");
1277         return;
1278     }
1279 
1280     CloudError preadError;
1281     readSize = filePtr->readSession->PRead(offset, size, buf.get(), preadError);
1282     if (!HandleCloudError(req, preadError)) {
1283         LOGD("read success, %lld bytes", static_cast<long long>(readSize));
1284         fuse_reply_buf(req, buf.get(), readSize);
1285     } else {
1286         LOGE("read fail");
1287     }
1288 }
1289 
UpdateCloudDiskInode(shared_ptr<CloudDiskRdbStore> rdbStore, shared_ptr<CloudDiskInode> inoPtr)1290 static void UpdateCloudDiskInode(shared_ptr<CloudDiskRdbStore> rdbStore, shared_ptr<CloudDiskInode> inoPtr)
1291 {
1292     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1293     CloudDiskFileInfo childInfo;
1294     int32_t err = rdbStore->GetAttr(inoPtr->cloudId, childInfo);
1295     if (err != 0) {
1296         LOGE("update file fail");
1297         return;
1298     }
1299     inoPtr->stat.st_size = childInfo.size;
1300     inoPtr->stat.st_mtime = childInfo.mtime / MILLISECOND_TO_SECONDS_TIMES;
1301 }
1302 
UpdateCloudStore(CloudDiskFuseData *data, const std::string &fileName, const std::string &parentCloudId, int fileDirty, shared_ptr<CloudDiskInode> inoPtr)1303 static void UpdateCloudStore(CloudDiskFuseData *data, const std::string &fileName, const std::string &parentCloudId,
1304     int fileDirty, shared_ptr<CloudDiskInode> inoPtr)
1305 {
1306     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1307     DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1308     auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
1309     int32_t dirtyType;
1310     int res = rdbStore->GetDirtyType(inoPtr->cloudId, dirtyType);
1311     if (res != 0) {
1312         LOGE("get file status fail, err: %{public}d", res);
1313     }
1314     res = rdbStore->Write(fileName, parentCloudId, inoPtr->cloudId);
1315     if (res != 0) {
1316         LOGE("write file fail");
1317     }
1318     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1319         NotifyOpsType::DAEMON_WRITE, inoPtr}, {dirtyType, false, fileDirty});
1320     UpdateCloudDiskInode(rdbStore, inoPtr);
1321 }
1322 
UpdateCacheDentrySize(CloudDiskFuseData *data, fuse_ino_t ino)1323 static int32_t UpdateCacheDentrySize(CloudDiskFuseData *data, fuse_ino_t ino)
1324 {
1325     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
1326     if (inoPtr == nullptr) {
1327         LOGE("inode not found");
1328         return ENOMEM;
1329     }
1330     string filePath = CloudFileUtils::GetLocalFilePath(inoPtr->cloudId, inoPtr->bundleName, data->userId);
1331     struct stat statInfo {};
1332     int32_t ret = stat(filePath.c_str(), &statInfo);
1333     if (ret) {
1334         LOGE("filePath %{public}s is invalid", GetAnonyString(filePath).c_str());
1335         return ret;
1336     }
1337     MetaBase metaBase(inoPtr->fileName);
1338     metaBase.mtime = static_cast<uint64_t>(CloudFileUtils::Timespec2Milliseconds(statInfo.st_mtim));
1339     metaBase.size = static_cast<uint64_t>(statInfo.st_size);
1340     auto callback = [&metaBase] (MetaBase &m) {
1341         m.size = metaBase.size;
1342         m.mtime = metaBase.mtime;
1343     };
1344     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
1345         static_cast<int64_t>(inoPtr->parent));
1346     if (parentInode == nullptr) {
1347         LOGE("fail to find parent inode");
1348         return ENOMEM;
1349     }
1350     string parentCloudId = parentInode->cloudId;
1351     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId, inoPtr->bundleName, parentCloudId);
1352     ret = metaFile->DoChildUpdate(inoPtr->fileName, callback);
1353     if (ret != 0) {
1354         LOGE("update new dentry failed, ret = %{public}d", ret);
1355         return ret;
1356     }
1357     inoPtr->stat.st_size = static_cast<decltype(inoPtr->stat.st_size)>(metaBase.size);
1358     inoPtr->stat.st_mtime =
1359         static_cast<decltype(inoPtr->stat.st_mtime)>(metaBase.mtime / MILLISECOND_TO_SECONDS_TIMES);
1360     return 0;
1361 }
1362 
WriteBuf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)1363 void FileOperationsCloud::WriteBuf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv,
1364                                    off_t off, struct fuse_file_info *fi)
1365 {
1366     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1367     struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(bufv));
1368     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1369     auto filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
1370     if (filePtr == nullptr) {
1371         fuse_reply_err(req, EINVAL);
1372         LOGE("file not found");
1373         return;
1374     }
1375     if (filePtr->type != CLOUD_DISK_FILE_TYPE_LOCAL) {
1376         fuse_reply_err(req, EINVAL);
1377         LOGE("write on cloud file not supported");
1378         return;
1379     }
1380     out_buf.buf[0].flags = (fuse_buf_flags)(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
1381     out_buf.buf[0].fd = filePtr->fd;
1382     out_buf.buf[0].pos = off;
1383     int res = fuse_buf_copy(&out_buf, bufv, (fuse_buf_copy_flags)(0));
1384     if (res < 0) {
1385         fuse_reply_err(req, -res);
1386     } else {
1387         if (filePtr != nullptr) { filePtr->fileDirty = CLOUD_DISK_FILE_WRITE; }
1388         int32_t ret = UpdateCacheDentrySize(data, ino);
1389         if (ret != 0) {
1390             LOGE("write size in cache and dentry fail, ret = %{public}d", ret);
1391         }
1392         fuse_reply_write(req, (size_t) res);
1393     }
1394 }
1395 
UploadLocalFile(CloudDiskFuseData *data, const std::string &fileName, const std::string &parentCloudId, int fileDirty, shared_ptr<CloudDiskInode> inoPtr)1396 static void UploadLocalFile(CloudDiskFuseData *data, const std::string &fileName, const std::string &parentCloudId,
1397     int fileDirty, shared_ptr<CloudDiskInode> inoPtr)
1398 {
1399     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1400     MetaBase metaBase(fileName);
1401     auto metaFile = MetaFileMgr::GetInstance().GetCloudDiskMetaFile(data->userId, inoPtr->bundleName, parentCloudId);
1402     int32_t ret = metaFile->DoLookup(metaBase);
1403     if (ret != 0) {
1404         LOGE("local file get location from dentryfile fail, ret = %{public}d", ret);
1405     } else if (metaBase.position == LOCAL) {
1406         DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1407         auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
1408         int32_t dirtyType;
1409         ret = rdbStore->GetDirtyType(inoPtr->cloudId, dirtyType);
1410         if (ret != 0) {
1411             LOGE("get file status fail, err: %{public}d", ret);
1412         }
1413         ret = rdbStore->Write(fileName, parentCloudId, inoPtr->cloudId);
1414         if (ret != 0) {
1415             LOGE("write file fail");
1416         }
1417         CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1418             NotifyOpsType::DAEMON_WRITE, inoPtr}, {dirtyType, false, fileDirty});
1419         UpdateCloudDiskInode(rdbStore, inoPtr);
1420     }
1421 }
1422 
Release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)1423 void FileOperationsCloud::Release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1424 {
1425     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1426     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1427     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
1428     if (inoPtr == nullptr) {
1429         LOGE("inode not found");
1430         fuse_reply_err(req, EINVAL);
1431         return;
1432     }
1433     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
1434         static_cast<int64_t>(inoPtr->parent));
1435     if (parentInode == nullptr) {
1436         fuse_reply_err(req, EINVAL);
1437         LOGE("fail to find parent inode");
1438         return;
1439     }
1440     string parentCloudId = parentInode->cloudId;
1441     shared_ptr<CloudDiskFile> filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
1442     if (filePtr == nullptr) {
1443         fuse_reply_err(req, EINVAL);
1444         LOGE("file not found");
1445         return;
1446     }
1447     filePtr->refCount--;
1448     if (filePtr->refCount == 0) {
1449         if (filePtr->type == CLOUD_DISK_FILE_TYPE_LOCAL) {
1450             close(filePtr->fd);
1451             if (filePtr->fileDirty != CLOUD_DISK_FILE_UNKNOWN) {
1452                 UpdateCloudStore(data, inoPtr->fileName, parentCloudId, filePtr->fileDirty, inoPtr);
1453             } else if (filePtr->isWriteOpen) {
1454                 UploadLocalFile(data, inoPtr->fileName, parentCloudId, filePtr->fileDirty, inoPtr);
1455             }
1456         } else if (filePtr->type == CLOUD_DISK_FILE_TYPE_CLOUD &&
1457             filePtr->readSession != nullptr) {
1458             bool res = filePtr->readSession->Close(false);
1459             if (!res) {
1460                 LOGE("close error");
1461                 fuse_reply_err(req, ENOSYS);
1462                 return;
1463             }
1464             filePtr->readSession = nullptr;
1465             LOGD("readSession released");
1466         }
1467         FileOperationsHelper::PutCloudDiskFile(data, filePtr, fi->fh);
1468     }
1469     fuse_reply_err(req, 0);
1470 }
1471 
SetAttr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, struct fuse_file_info *fi)1472 void FileOperationsCloud::SetAttr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
1473                                   int valid, struct fuse_file_info *fi)
1474 {
1475     if (WaitParameter("persist.kernel.move.finish", "true", MOVE_FILE_TIME_DAEMON) != 0) {
1476         LOGE("wait move error");
1477         return (void) fuse_reply_err(req, EBUSY);
1478     }
1479     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1480     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1481     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
1482     if (inoPtr == nullptr) {
1483         LOGE("get an invalid inode!");
1484         return (void) fuse_reply_err(req, EINVAL);
1485     }
1486     auto parentInode = FileOperationsHelper::FindCloudDiskInode(data,
1487         static_cast<int64_t>(inoPtr->parent));
1488     if (static_cast<unsigned int>(valid) & FUSE_SET_ATTR_SIZE) {
1489         DatabaseManager &databaseManager = DatabaseManager::GetInstance();
1490         auto rdbStore = databaseManager.GetRdbStore(inoPtr->bundleName, data->userId);
1491         int32_t res = rdbStore->SetAttr(inoPtr->fileName, parentInode->cloudId, inoPtr->cloudId, attr->st_size);
1492         if (res != 0) {
1493             LOGE("update rdb size failed, res: %{public}d", res);
1494             return (void) fuse_reply_err(req, ENOSYS);
1495         }
1496         if (fi) {
1497             auto filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
1498             if (filePtr == nullptr) {
1499                 LOGE("file not found");
1500                 return (void) fuse_reply_err(req, EINVAL);
1501             }
1502             res = ftruncate(filePtr->fd, attr->st_size);
1503         } else {
1504             string path = CloudFileUtils::GetLocalFilePath(inoPtr->cloudId, inoPtr->bundleName, data->userId);
1505             res = truncate(path.c_str(), attr->st_size);
1506         }
1507         if (res == -1) {
1508             LOGE("truncate failed, err: %{public}d", errno);
1509             res = rdbStore->SetAttr(inoPtr->fileName, parentInode->cloudId, inoPtr->cloudId, inoPtr->stat.st_size);
1510             if (res != 0) {
1511                 LOGE("update rdb size failed, res: %{public}d", res);
1512                 fuse_reply_err(req, ENOSYS);
1513             } else {
1514                 fuse_reply_err(req, errno);
1515             }
1516             return;
1517         }
1518         UpdateCloudDiskInode(rdbStore, inoPtr);
1519     }
1520     CloudDiskNotify::GetInstance().TryNotify({data, FileOperationsHelper::FindCloudDiskInode,
1521         NotifyOpsType::DAEMON_SETATTR, inoPtr});
1522     fuse_reply_attr(req, &inoPtr->stat, 0);
1523 }
1524 
Lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)1525 void FileOperationsCloud::Lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1526                                 struct fuse_file_info *fi)
1527 {
1528     HITRACE_METER_NAME(HITRACE_TAG_CLOUD_FILE, __PRETTY_FUNCTION__);
1529     auto data = reinterpret_cast<struct CloudDiskFuseData *>(fuse_req_userdata(req));
1530     auto inoPtr = FileOperationsHelper::FindCloudDiskInode(data, static_cast<int64_t>(ino));
1531     if (inoPtr == nullptr) {
1532         LOGE("get an invalid inode!");
1533         fuse_reply_err(req, EINVAL);
1534         return;
1535     }
1536     shared_ptr<CloudDiskFile> filePtr = FileOperationsHelper::FindCloudDiskFile(data, fi->fh);
1537     if (filePtr == nullptr) {
1538         fuse_reply_err(req, EINVAL);
1539         LOGE("file not found");
1540         return;
1541     }
1542     if (filePtr->type != CLOUD_DISK_FILE_TYPE_LOCAL) {
1543         fuse_reply_err(req, ENOSYS);
1544         LOGE("lseek on cloud file not supported");
1545         return;
1546     }
1547     off_t res = lseek(filePtr->fd, off, whence);
1548     if (res != -1)
1549         fuse_reply_lseek(req, res);
1550     else
1551         fuse_reply_err(req, errno);
1552 }
1553 } // namespace CloudDisk
1554 } // namespace FileManagement
1555 } // namespace OHOS
1556