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