1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "cloud_file_utils.h"
16 #include <ctime>
17 #include <fcntl.h>
18 #include <filesystem>
19 #include <sys/xattr.h>
20 #include <sys/ioctl.h>
21 #include <unistd.h>
22
23 #include "fuse_ioctl.h"
24 #include "utils_log.h"
25
26 namespace OHOS {
27 namespace FileManagement {
28 namespace CloudDisk {
29 using namespace std;
30 using namespace CloudFile;
31 namespace {
32 static const string LOCAL_PATH_DATA_SERVICE_EL2 = "/data/service/el2/";
33 static const string LOCAL_PATH_HMDFS_CLOUD_DATA = "/hmdfs/cloud/data/";
34 static const string LOCAL_PATH_HMDFS_CLOUD_CACHE = "/hmdfs/cache/cloud_cache";
35 static const string CLOUDDISK_CACHE_DIR = "/disk_drivekit_cache/";
36 static const string CLOUD_FILE_CLOUD_ID_XATTR = "user.cloud.cloudid";
37 static const uint32_t CLOUD_ID_MIN_SIZE = 3;
38 static const uint32_t CLOUD_ID_BUCKET_MID_TIMES = 2;
39 static const uint32_t CLOUD_ID_BUCKET_MAX_SIZE = 32;
40 static const int64_t SECOND_TO_MILLISECOND = 1e3;
41 static const int64_t MILLISECOND_TO_NANOSECOND = 1e6;
42 static const uint64_t DELTA_DISK = 0x9E3779B9;
43 static const uint64_t HMDFS_HASH_COL_BIT_DISK = (0x1ULL) << 63;
44 }
45
46 const string CloudFileUtils::TMP_SUFFIX = ".temp.download";
47
IsDotDotdot(const std::string & name)48 bool CloudFileUtils::IsDotDotdot(const std::string &name)
49 {
50 return name == "." || name == "..";
51 }
52
Str2HashBuf(const char * msg,size_t len,uint32_t * buf,int num)53 void CloudFileUtils::Str2HashBuf(const char *msg, size_t len, uint32_t *buf, int num)
54 {
55 const int32_t shift8 = 8;
56 const int32_t shift16 = 16;
57 const int32_t three = 3;
58 const int32_t mod = 4;
59 uint32_t pad = static_cast<uint32_t>(len) | (static_cast<uint32_t>(len) << shift8);
60 pad |= pad << shift16;
61
62 uint32_t val = pad;
63 len = std::min(len, static_cast<size_t>(num * sizeof(int)));
64 for (uint32_t i = 0; i < len; i++) {
65 if ((i % sizeof(int)) == 0) {
66 val = pad;
67 }
68 uint8_t c = static_cast<uint8_t>(tolower(msg[i]));
69 val = c + (val << shift8);
70 if ((i % mod) == three) {
71 *buf++ = val;
72 val = pad;
73 num--;
74 }
75 }
76 if (--num >= 0) {
77 *buf++ = val;
78 }
79 while (--num >= 0) {
80 *buf++ = pad;
81 }
82 }
83
TeaTransform(uint32_t buf[4],uint32_t const in[])84 void CloudFileUtils::TeaTransform(uint32_t buf[4], uint32_t const in[]) __attribute__((no_sanitize(
85 "unsigned-integer-overflow")))
86 {
87 int n = 16;
88 uint32_t a = in[0];
89 uint32_t b = in[1];
90 uint32_t c = in[2];
91 uint32_t d = in[3];
92 uint32_t b0 = buf[0];
93 uint32_t b1 = buf[1];
94 uint32_t sum = 0;
95 const int32_t LEFT_SHIFT = 4;
96 const int32_t RIGHT_SHIFT = 5;
97 do {
98 sum += DELTA_DISK;
99 b0 += ((b1 << LEFT_SHIFT) + a) ^ (b1 + sum) ^ ((b1 >> RIGHT_SHIFT) + b);
100 b1 += ((b0 << LEFT_SHIFT) + c) ^ (b0 + sum) ^ ((b0 >> RIGHT_SHIFT) + d);
101 } while (--n);
102
103 buf[0] += b0;
104 buf[1] += b1;
105 }
106
DentryHash(const std::string & inputStr)107 uint32_t CloudFileUtils::DentryHash(const std::string &inputStr)
108 {
109 if (IsDotDotdot(inputStr)) {
110 return 0;
111 }
112 constexpr int inLen = 8;
113 constexpr int bufLen = 4;
114 uint32_t in[inLen];
115 uint32_t buf[bufLen] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
116 auto len = inputStr.length();
117 constexpr decltype(len) hashWidth = 16;
118 const char *p = inputStr.c_str();
119
120 bool loopFlag = true;
121 while (loopFlag) {
122 Str2HashBuf(p, len, in, bufLen);
123 TeaTransform(buf, in);
124
125 if (len <= hashWidth) {
126 break;
127 }
128 p += hashWidth;
129 len -= hashWidth;
130 };
131 uint32_t hash = buf[0];
132 uint32_t hmdfsHash = hash & ~HMDFS_HASH_COL_BIT_DISK;
133 return hmdfsHash;
134 }
135
GetBucketId(string cloudId)136 uint32_t CloudFileUtils::GetBucketId(string cloudId)
137 {
138 size_t size = cloudId.size();
139 if (size < CLOUD_ID_MIN_SIZE) {
140 return 0;
141 }
142
143 char first = cloudId[0];
144 char last = cloudId[size - 1];
145 char middle = cloudId[size / CLOUD_ID_BUCKET_MID_TIMES];
146 return (first + last + middle) % CLOUD_ID_BUCKET_MAX_SIZE;
147 }
148
Timespec2Milliseconds(const struct timespec & time)149 int64_t CloudFileUtils::Timespec2Milliseconds(const struct timespec &time)
150 {
151 return time.tv_sec * SECOND_TO_MILLISECOND + time.tv_nsec / MILLISECOND_TO_NANOSECOND;
152 }
153
GetLocalBaseDir(string bundleName,int32_t userId)154 string CloudFileUtils::GetLocalBaseDir(string bundleName, int32_t userId)
155 {
156 string baseDir = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) +
157 LOCAL_PATH_HMDFS_CLOUD_DATA + bundleName + "/";
158 return baseDir;
159 }
160
GetLocalBucketPath(string cloudId,string bundleName,int32_t userId)161 string CloudFileUtils::GetLocalBucketPath(string cloudId, string bundleName, int32_t userId)
162 {
163 string baseDir = GetLocalBaseDir(bundleName, userId);
164 uint32_t bucketId = GetBucketId(cloudId);
165 string bucketPath = baseDir + to_string(bucketId);
166 return bucketPath;
167 }
168
GetLocalDKCachePath(string cloudId,string bundleName,int32_t userId)169 string CloudFileUtils::GetLocalDKCachePath(string cloudId, string bundleName, int32_t userId)
170 {
171 string baseDir = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) + LOCAL_PATH_HMDFS_CLOUD_CACHE +
172 CLOUDDISK_CACHE_DIR + bundleName + "/";
173 uint32_t bucketId = GetBucketId(cloudId);
174 string cachePath = baseDir + to_string(bucketId) + "/" + cloudId;
175 return cachePath;
176 }
177
GetLocalFilePath(string cloudId,string bundleName,int32_t userId)178 string CloudFileUtils::GetLocalFilePath(string cloudId, string bundleName, int32_t userId)
179 {
180 return GetLocalBucketPath(cloudId, bundleName, userId) + "/" + cloudId;
181 }
182
GetPathWithoutTmp(const string & path)183 string CloudFileUtils::GetPathWithoutTmp(const string &path)
184 {
185 string ret = path;
186 if (EndsWith(path, TMP_SUFFIX)) {
187 ret = path.substr(0, path.length() - TMP_SUFFIX.length());
188 }
189 return ret;
190 }
191
EndsWith(const string & fullString,const string & ending)192 bool CloudFileUtils::EndsWith(const string &fullString, const string &ending)
193 {
194 if (fullString.length() >= ending.length()) {
195 return (!fullString.compare(fullString.length() - ending.length(),
196 ending.length(),
197 ending));
198 }
199 return false;
200 }
201
GetCloudId(const string & path)202 string CloudFileUtils::GetCloudId(const string &path)
203 {
204 auto idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), nullptr, 0);
205 if (idSize <= 0) {
206 return "";
207 }
208 char cloudId[idSize + 1];
209 idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), cloudId, idSize);
210 if (idSize <= 0) {
211 return "";
212 }
213 return string(cloudId);
214 }
215
CheckIsCloud(const string & key)216 bool CloudFileUtils::CheckIsCloud(const string &key)
217 {
218 return key == CLOUD_CLOUD_ID_XATTR;
219 }
220
CheckIsCloudLocation(const string & key)221 bool CloudFileUtils::CheckIsCloudLocation(const string &key)
222 {
223 return key == CLOUD_FILE_LOCATION;
224 }
225
CheckIsHmdfsPermission(const string & key)226 bool CloudFileUtils::CheckIsHmdfsPermission(const string &key)
227 {
228 return key == HMDFS_PERMISSION_XATTR;
229 }
230
CheckIsCloudRecycle(const string & key)231 bool CloudFileUtils::CheckIsCloudRecycle(const string &key)
232 {
233 return key == CLOUD_CLOUD_RECYCLE_XATTR;
234 }
235
CheckIsFavorite(const string & key)236 bool CloudFileUtils::CheckIsFavorite(const string &key)
237 {
238 return key == IS_FAVORITE_XATTR;
239 }
240
CheckIsTimeRecycled(const string & key)241 bool CloudFileUtils::CheckIsTimeRecycled(const string &key)
242 {
243 return key == CLOUD_TIME_RECYCLED;
244 }
245
CheckIsHasLCD(const string & key)246 bool CloudFileUtils::CheckIsHasLCD(const string &key)
247 {
248 return key == CLOUD_HAS_LCD;
249 }
250
CheckIsHasTHM(const string & key)251 bool CloudFileUtils::CheckIsHasTHM(const string &key)
252 {
253 return key == CLOUD_HAS_THM;
254 }
255
CheckFileStatus(const string & key)256 bool CloudFileUtils::CheckFileStatus(const string &key)
257 {
258 return key == IS_FILE_STATUS_XATTR;
259 }
260
CheckIsRecyclePath(const string & key)261 bool CloudFileUtils::CheckIsRecyclePath(const string &key)
262 {
263 return key == CLOUD_RECYCLE_PATH;
264 }
265
LocalWriteOpen(const string & dfsPath)266 bool CloudFileUtils::LocalWriteOpen(const string &dfsPath)
267 {
268 unique_ptr<char[]> absPath = make_unique<char[]>(PATH_MAX + 1);
269 if (realpath(dfsPath.c_str(), absPath.get()) == nullptr) {
270 return false;
271 }
272 string realPath = absPath.get();
273 char resolvedPath[PATH_MAX] = {'\0'};
274 char *realPaths = realpath(realPath.c_str(), resolvedPath);
275 if (realPaths == NULL) {
276 LOGE("realpath failed");
277 return false;
278 }
279 std::FILE *file = fopen(realPaths, "r");
280 if (file == nullptr) {
281 LOGE("fopen failed, errno:%{public}d", errno);
282 return false;
283 }
284 int fd = fileno(file);
285 /*
286 * In the implementation of fopen, if the contained fd < 0, reutrn nullptr.
287 * There is no case where the fd < 0 when the pointer is non-null.
288 * This is to judge the exception where the file carrying the fd has been closed.
289 * In such cases, fclose is not needed.
290 */
291 if (fd < 0) {
292 LOGE("get fd failed, errno:%{public}d", errno);
293 return false;
294 }
295 uint32_t writeOpenCnt = 0;
296 int ret = ioctl(fd, HMDFS_IOC_GET_WRITEOPEN_CNT, &writeOpenCnt);
297 if (ret < 0) {
298 LOGE("ioctl failed, errno:%{public}d", errno);
299 if (fclose(file)) {
300 LOGE("fclose failed, errno:%{public}d", errno);
301 }
302 return false;
303 }
304
305 if (fclose(file)) {
306 LOGE("fclose failed, errno:%{public}d", errno);
307 return false;
308 }
309 return writeOpenCnt != 0;
310 }
311
ClearHmdfsCache(const string & dfsPath)312 static bool ClearHmdfsCache(const string &dfsPath)
313 {
314 char resolvedPath[PATH_MAX];
315 if (realpath(dfsPath.c_str(), resolvedPath) == nullptr) {
316 LOGE("realpath failed");
317 return false;
318 }
319 std::FILE *file = fopen(resolvedPath, "r");
320 if (file == nullptr) {
321 LOGE("fopen failed, errno:%{public}d", errno);
322 return false;
323 }
324 int fd = fileno(file);
325 /*
326 * In the implementation of fopen, if the contained fd < 0, reutrn nullptr.
327 * There is no case where the fd < 0 when the pointer is non-null.
328 * This is to judge the exception where the file carrying the fd has been closed.
329 * In such cases, fclose is not needed.
330 */
331 if (fd < 0) {
332 LOGE("get fd failed, errno:%{public}d", errno);
333 return false;
334 }
335 int ret = ioctl(fd, HMDFS_IOC_SET_CLOUD_GENERATION);
336 if (ret < 0) {
337 LOGE("ioctl failed, errno:%{public}d", errno);
338 if (fclose(file)) {
339 LOGE("fclose failed, errno:%{public}d", errno);
340 }
341 return false;
342 }
343 if (fclose(file)) {
344 LOGE("fclose failed, errno:%{public}d", errno);
345 return false;
346 }
347 return true;
348 }
349
ClearCloudCache(const string & cloudPath)350 static bool ClearCloudCache(const string &cloudPath)
351 {
352 char resolvedPath[PATH_MAX];
353 if (realpath(cloudPath.c_str(), resolvedPath) == nullptr) {
354 LOGE("realpath failed");
355 return false;
356 }
357
358 int fd = open(resolvedPath, O_RDONLY | O_DIRECTORY);
359 if (fd == -1) {
360 LOGE("open failed %{public}d", errno);
361 return false;
362 }
363 int ret = ioctl(fd, HMDFS_IOC_CLEAN_CACHE_DAEMON);
364 if (ret < 0) {
365 LOGE("ioctl failed, errno:%{public}d", errno);
366 close(fd);
367 return false;
368 }
369
370 close(fd);
371 return true;
372 }
373
ClearCache(const string & dfsPath,const string & cloudPath)374 bool CloudFileUtils::ClearCache(const string &dfsPath, const string &cloudPath)
375 {
376 if (!ClearHmdfsCache(dfsPath)) {
377 return false;
378 }
379
380 if (!ClearCloudCache(cloudPath)) {
381 return false;
382 }
383
384 return true;
385 }
386
GetRealPath(const string & path)387 string CloudFileUtils::GetRealPath(const string &path)
388 {
389 filesystem::path tempPath(path);
390 filesystem::path realPath{};
391 for (const auto& component : tempPath) {
392 if (component == ".") {
393 continue;
394 } else if (component == "..") {
395 realPath = realPath.parent_path();
396 } else {
397 realPath /= component;
398 }
399 }
400 return realPath.string();
401 }
402 } // namespace CloudDisk
403 } // namespace FileManagement
404 } // namespace OHOS
405