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 #include "utils_log.h"
23 namespace OHOS {
24 namespace FileManagement {
25 namespace CloudDisk {
26 using namespace std;
27 namespace {
28 static const string LOCAL_PATH_DATA_SERVICE_EL2 = "/data/service/el2/";
29 static const string LOCAL_PATH_HMDFS_CLOUD_DATA = "/hmdfs/cloud/data/";
30 static const string CLOUD_FILE_CLOUD_ID_XATTR = "user.cloud.cloudid";
31 static const uint32_t CLOUD_ID_MIN_SIZE = 3;
32 static const uint32_t CLOUD_ID_BUCKET_MID_TIMES = 2;
33 static const uint32_t CLOUD_ID_BUCKET_MAX_SIZE = 32;
34 static const int64_t SECOND_TO_MILLISECOND = 1e3;
35 static const int64_t MILLISECOND_TO_NANOSECOND = 1e6;
36 static const uint64_t DELTA_DISK = 0x9E3779B9;
37 static const uint64_t HMDFS_HASH_COL_BIT_DISK = (0x1ULL) << 63;
38 }
39
40 constexpr unsigned HMDFS_IOC = 0xf2;
41 constexpr unsigned WRITEOPEN_CMD = 0x02;
42 constexpr unsigned CLOUD_ENABLE_CMD = 0x0b;
43 #define HMDFS_IOC_GET_WRITEOPEN_CNT _IOR(HMDFS_IOC, WRITEOPEN_CMD, uint32_t)
44 #define HMDFS_IOC_SET_CLOUD_GENERATION _IOR(HMDFS_IOC, CLOUD_ENABLE_CMD, uint32_t)
45 const string CloudFileUtils::TMP_SUFFIX = ".temp.download";
46
IsDotDotdot(const std::string & name)47 bool CloudFileUtils::IsDotDotdot(const std::string &name)
48 {
49 return name == "." || name == "..";
50 }
51
Str2HashBuf(const char * msg,size_t len,uint32_t * buf,int num)52 void CloudFileUtils::Str2HashBuf(const char *msg, size_t len, uint32_t *buf, int num)
53 {
54 const int32_t shift8 = 8;
55 const int32_t shift16 = 16;
56 const int32_t three = 3;
57 const int32_t mod = 4;
58 uint32_t pad = static_cast<uint32_t>(len) | (static_cast<uint32_t>(len) << shift8);
59 pad |= pad << shift16;
60
61 uint32_t val = pad;
62 len = std::min(len, static_cast<size_t>(num * sizeof(int)));
63 for (uint32_t i = 0; i < len; i++) {
64 if ((i % sizeof(int)) == 0) {
65 val = pad;
66 }
67 uint8_t c = static_cast<uint8_t>(tolower(msg[i]));
68 val = c + (val << shift8);
69 if ((i % mod) == three) {
70 *buf++ = val;
71 val = pad;
72 num--;
73 }
74 }
75 if (--num >= 0) {
76 *buf++ = val;
77 }
78 while (--num >= 0) {
79 *buf++ = pad;
80 }
81 }
82
TeaTransform(uint32_t buf[4],uint32_t const in[])83 void CloudFileUtils::TeaTransform(uint32_t buf[4], uint32_t const in[]) __attribute__((no_sanitize(
84 "unsigned-integer-overflow")))
85 {
86 int n = 16;
87 uint32_t a = in[0];
88 uint32_t b = in[1];
89 uint32_t c = in[2];
90 uint32_t d = in[3];
91 uint32_t b0 = buf[0];
92 uint32_t b1 = buf[1];
93 uint32_t sum = 0;
94 const int32_t LEFT_SHIFT = 4;
95 const int32_t RIGHT_SHIFT = 5;
96 do {
97 sum += DELTA_DISK;
98 b0 += ((b1 << LEFT_SHIFT) + a) ^ (b1 + sum) ^ ((b1 >> RIGHT_SHIFT) + b);
99 b1 += ((b0 << LEFT_SHIFT) + c) ^ (b0 + sum) ^ ((b0 >> RIGHT_SHIFT) + d);
100 } while (--n);
101
102 buf[0] += b0;
103 buf[1] += b1;
104 }
105
DentryHash(const std::string & inputStr)106 uint32_t CloudFileUtils::DentryHash(const std::string &inputStr)
107 {
108 if (IsDotDotdot(inputStr)) {
109 return 0;
110 }
111 constexpr int inLen = 8;
112 constexpr int bufLen = 4;
113 uint32_t in[inLen];
114 uint32_t buf[bufLen] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
115 auto len = inputStr.length();
116 constexpr decltype(len) hashWidth = 16;
117 const char *p = inputStr.c_str();
118
119 bool loopFlag = true;
120 while (loopFlag) {
121 Str2HashBuf(p, len, in, bufLen);
122 TeaTransform(buf, in);
123
124 if (len <= hashWidth) {
125 break;
126 }
127 p += hashWidth;
128 len -= hashWidth;
129 };
130 uint32_t hash = buf[0];
131 uint32_t hmdfsHash = hash & ~HMDFS_HASH_COL_BIT_DISK;
132 return hmdfsHash;
133 }
134
GetBucketId(string cloudId)135 uint32_t CloudFileUtils::GetBucketId(string cloudId)
136 {
137 size_t size = cloudId.size();
138 if (size < CLOUD_ID_MIN_SIZE) {
139 return 0;
140 }
141
142 char first = cloudId[0];
143 char last = cloudId[size - 1];
144 char middle = cloudId[size / CLOUD_ID_BUCKET_MID_TIMES];
145 return (first + last + middle) % CLOUD_ID_BUCKET_MAX_SIZE;
146 }
147
Timespec2Milliseconds(const struct timespec & time)148 int64_t CloudFileUtils::Timespec2Milliseconds(const struct timespec &time)
149 {
150 return time.tv_sec * SECOND_TO_MILLISECOND + time.tv_nsec / MILLISECOND_TO_NANOSECOND;
151 }
152
GetLocalBucketPath(string cloudId,string bundleName,int32_t userId)153 string CloudFileUtils::GetLocalBucketPath(string cloudId, string bundleName, int32_t userId)
154 {
155 string baseDir = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) +
156 LOCAL_PATH_HMDFS_CLOUD_DATA + bundleName + "/";
157 uint32_t bucketId = GetBucketId(cloudId);
158 string bucketPath = baseDir + to_string(bucketId);
159 return bucketPath;
160 }
161
GetLocalFilePath(string cloudId,string bundleName,int32_t userId)162 string CloudFileUtils::GetLocalFilePath(string cloudId, string bundleName, int32_t userId)
163 {
164 return GetLocalBucketPath(cloudId, bundleName, userId) + "/" + cloudId;
165 }
166
GetPathWithoutTmp(const string & path)167 string CloudFileUtils::GetPathWithoutTmp(const string &path)
168 {
169 string ret = path;
170 if (EndsWith(path, TMP_SUFFIX)) {
171 ret = path.substr(0, path.length() - TMP_SUFFIX.length());
172 }
173 return ret;
174 }
175
EndsWith(const string & fullString,const string & ending)176 bool CloudFileUtils::EndsWith(const string &fullString, const string &ending)
177 {
178 if (fullString.length() >= ending.length()) {
179 return (!fullString.compare(fullString.length() - ending.length(),
180 ending.length(),
181 ending));
182 }
183 return false;
184 }
185
GetCloudId(const string & path)186 string CloudFileUtils::GetCloudId(const string &path)
187 {
188 auto idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), nullptr, 0);
189 if (idSize <= 0) {
190 return "";
191 }
192 char cloudId[idSize + 1];
193 idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), cloudId, idSize);
194 if (idSize <= 0) {
195 return "";
196 }
197 return string(cloudId);
198 }
199
CheckIsCloud(const string & key)200 bool CloudFileUtils::CheckIsCloud(const string &key)
201 {
202 return key == CLOUD_CLOUD_ID_XATTR;
203 }
204
CheckIsCloudLocation(const string & key)205 bool CloudFileUtils::CheckIsCloudLocation(const string &key)
206 {
207 return key == CLOUD_FILE_LOCATION;
208 }
209
CheckIsHmdfsPermission(const string & key)210 bool CloudFileUtils::CheckIsHmdfsPermission(const string &key)
211 {
212 return key == HMDFS_PERMISSION_XATTR;
213 }
214
CheckIsCloudRecycle(const string & key)215 bool CloudFileUtils::CheckIsCloudRecycle(const string &key)
216 {
217 return key == CLOUD_CLOUD_RECYCLE_XATTR;
218 }
219
CheckIsFavorite(const string & key)220 bool CloudFileUtils::CheckIsFavorite(const string &key)
221 {
222 return key == IS_FAVORITE_XATTR;
223 }
224
CheckIsTimeRecycled(const string & key)225 bool CloudFileUtils::CheckIsTimeRecycled(const string &key)
226 {
227 return key == CLOUD_TIME_RECYCLED;
228 }
229
CheckIsHasLCD(const string & key)230 bool CloudFileUtils::CheckIsHasLCD(const string &key)
231 {
232 return key == CLOUD_HAS_LCD;
233 }
234
CheckIsHasTHM(const string & key)235 bool CloudFileUtils::CheckIsHasTHM(const string &key)
236 {
237 return key == CLOUD_HAS_THM;
238 }
239
CheckFileStatus(const string & key)240 bool CloudFileUtils::CheckFileStatus(const string &key)
241 {
242 return key == IS_FILE_STATUS_XATTR;
243 }
244
LocalWriteOpen(const string & dfsPath)245 bool CloudFileUtils::LocalWriteOpen(const string &dfsPath)
246 {
247 unique_ptr<char[]> absPath = make_unique<char[]>(PATH_MAX + 1);
248 if (realpath(dfsPath.c_str(), absPath.get()) == nullptr) {
249 return false;
250 }
251 string realPath = absPath.get();
252 char resolvedPath[PATH_MAX] = {'\0'};
253 char *realPaths = realpath(realPath.c_str(), resolvedPath);
254 if (realPaths == NULL) {
255 LOGE("realpath failed");
256 return false;
257 }
258 int fd = open(realPaths, O_RDONLY);
259 if (fd < 0) {
260 LOGE("open failed, errno:%{public}d", errno);
261 return false;
262 }
263 uint32_t writeOpenCnt = 0;
264 int ret = ioctl(fd, HMDFS_IOC_GET_WRITEOPEN_CNT, &writeOpenCnt);
265 close(fd);
266 if (ret < 0) {
267 LOGE("ioctl failed, errno:%{public}d", errno);
268 return false;
269 }
270
271 return writeOpenCnt != 0;
272 }
273
ClearCache(const string & dfsPath)274 bool CloudFileUtils::ClearCache(const string &dfsPath)
275 {
276 auto resolvedPath = realpath(dfsPath.c_str(), NULL);
277 if (resolvedPath == NULL) {
278 LOGE("realpath failed");
279 return false;
280 }
281 int fd = open(resolvedPath, O_RDONLY);
282 free(resolvedPath);
283 if (fd < 0) {
284 LOGE("open failed, errno:%{public}d", errno);
285 return false;
286 }
287 int ret = ioctl(fd, HMDFS_IOC_SET_CLOUD_GENERATION);
288 if (ret < 0) {
289 LOGE("ioctl failed, errno:%{public}d", errno);
290 close(fd);
291 return false;
292 }
293 close(fd);
294 return true;
295 }
296
GetRealPath(const string & path)297 string CloudFileUtils::GetRealPath(const string &path)
298 {
299 filesystem::path tempPath(path);
300 filesystem::path realPath{};
301 for (const auto& component : tempPath) {
302 if (component == ".") {
303 continue;
304 } else if (component == "..") {
305 realPath = realPath.parent_path();
306 } else {
307 realPath /= component;
308 }
309 }
310 return realPath.string();
311 }
312 } // namespace CloudDisk
313 } // namespace FileManagement
314 } // namespace OHOS
315