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 #define HMDFS_IOC_GET_WRITEOPEN_CNT _IOR(HMDFS_IOC, WRITEOPEN_CMD, uint32_t)
43 const string CloudFileUtils::TMP_SUFFIX = ".temp.download";
44 
IsDotDotdot(const std::string & name)45 bool CloudFileUtils::IsDotDotdot(const std::string &name)
46 {
47     return name == "." || name == "..";
48 }
49 
Str2HashBuf(const char * msg,size_t len,uint32_t * buf,int num)50 void CloudFileUtils::Str2HashBuf(const char *msg, size_t len, uint32_t *buf, int num)
51 {
52     const int32_t shift8 = 8;
53     const int32_t shift16 = 16;
54     const int32_t three = 3;
55     const int32_t mod = 4;
56     uint32_t pad = static_cast<uint32_t>(len) | (static_cast<uint32_t>(len) << shift8);
57     pad |= pad << shift16;
58 
59     uint32_t val = pad;
60     len = std::min(len, static_cast<size_t>(num * sizeof(int)));
61     for (uint32_t i = 0; i < len; i++) {
62         if ((i % sizeof(int)) == 0) {
63             val = pad;
64         }
65         uint8_t c = static_cast<uint8_t>(tolower(msg[i]));
66         val = c + (val << shift8);
67         if ((i % mod) == three) {
68             *buf++ = val;
69             val = pad;
70             num--;
71         }
72     }
73     if (--num >= 0) {
74         *buf++ = val;
75     }
76     while (--num >= 0) {
77         *buf++ = pad;
78     }
79 }
80 
TeaTransform(uint32_t buf[4],uint32_t const in[])81 void CloudFileUtils::TeaTransform(uint32_t buf[4], uint32_t const in[]) __attribute__((no_sanitize(
82     "unsigned-integer-overflow")))
83 {
84     int n = 16;
85     uint32_t a = in[0];
86     uint32_t b = in[1];
87     uint32_t c = in[2];
88     uint32_t d = in[3];
89     uint32_t b0 = buf[0];
90     uint32_t b1 = buf[1];
91     uint32_t sum = 0;
92     const int32_t LEFT_SHIFT = 4;
93     const int32_t RIGHT_SHIFT = 5;
94     do {
95         sum += DELTA_DISK;
96         b0 += ((b1 << LEFT_SHIFT) + a) ^ (b1 + sum) ^ ((b1 >> RIGHT_SHIFT) + b);
97         b1 += ((b0 << LEFT_SHIFT) + c) ^ (b0 + sum) ^ ((b0 >> RIGHT_SHIFT) + d);
98     } while (--n);
99 
100     buf[0] += b0;
101     buf[1] += b1;
102 }
103 
DentryHash(const std::string & inputStr)104 uint32_t CloudFileUtils::DentryHash(const std::string &inputStr)
105 {
106     if (IsDotDotdot(inputStr)) {
107         return 0;
108     }
109     constexpr int inLen = 8;
110     constexpr int bufLen = 4;
111     uint32_t in[inLen];
112     uint32_t buf[bufLen] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
113     auto len = inputStr.length();
114     constexpr decltype(len) hashWidth = 16;
115     const char *p = inputStr.c_str();
116 
117     bool loopFlag = true;
118     while (loopFlag) {
119         Str2HashBuf(p, len, in, bufLen);
120         TeaTransform(buf, in);
121 
122         if (len <= hashWidth) {
123             break;
124         }
125         p += hashWidth;
126         len -= hashWidth;
127     };
128     uint32_t hash = buf[0];
129     uint32_t hmdfsHash = hash & ~HMDFS_HASH_COL_BIT_DISK;
130     return hmdfsHash;
131 }
132 
GetBucketId(string cloudId)133 uint32_t CloudFileUtils::GetBucketId(string cloudId)
134 {
135     size_t size = cloudId.size();
136     if (size < CLOUD_ID_MIN_SIZE) {
137         return 0;
138     }
139 
140     char first = cloudId[0];
141     char last = cloudId[size - 1];
142     char middle = cloudId[size / CLOUD_ID_BUCKET_MID_TIMES];
143     return (first + last + middle) % CLOUD_ID_BUCKET_MAX_SIZE;
144 }
145 
Timespec2Milliseconds(const struct timespec & time)146 int64_t CloudFileUtils::Timespec2Milliseconds(const struct timespec &time)
147 {
148     return time.tv_sec * SECOND_TO_MILLISECOND + time.tv_nsec / MILLISECOND_TO_NANOSECOND;
149 }
150 
GetLocalBucketPath(string cloudId,string bundleName,int32_t userId)151 string CloudFileUtils::GetLocalBucketPath(string cloudId, string bundleName, int32_t userId)
152 {
153     string baseDir = LOCAL_PATH_DATA_SERVICE_EL2 + to_string(userId) +
154                      LOCAL_PATH_HMDFS_CLOUD_DATA + bundleName + "/";
155     uint32_t bucketId = GetBucketId(cloudId);
156     string bucketPath = baseDir + to_string(bucketId);
157     return bucketPath;
158 }
159 
GetLocalFilePath(string cloudId,string bundleName,int32_t userId)160 string CloudFileUtils::GetLocalFilePath(string cloudId, string bundleName, int32_t userId)
161 {
162     return GetLocalBucketPath(cloudId, bundleName, userId) + "/" + cloudId;
163 }
164 
GetPathWithoutTmp(const string & path)165 string CloudFileUtils::GetPathWithoutTmp(const string &path)
166 {
167     string ret = path;
168     if (EndsWith(path, TMP_SUFFIX)) {
169         ret = path.substr(0, path.length() - TMP_SUFFIX.length());
170     }
171     return ret;
172 }
173 
EndsWith(const string & fullString,const string & ending)174 bool CloudFileUtils::EndsWith(const string &fullString, const string &ending)
175 {
176     if (fullString.length() >= ending.length()) {
177         return (!fullString.compare(fullString.length() - ending.length(),
178                                     ending.length(),
179                                     ending));
180     }
181     return false;
182 }
183 
GetCloudId(const string & path)184 string CloudFileUtils::GetCloudId(const string &path)
185 {
186     auto idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), nullptr, 0);
187     if (idSize <= 0) {
188         return "";
189     }
190     char cloudId[idSize + 1];
191     idSize = getxattr(path.c_str(), CLOUD_FILE_CLOUD_ID_XATTR.c_str(), cloudId, idSize);
192     if (idSize <= 0) {
193         return "";
194     }
195     return string(cloudId);
196 }
197 
CheckIsCloud(const string & key)198 bool CloudFileUtils::CheckIsCloud(const string &key)
199 {
200     return key == CLOUD_CLOUD_ID_XATTR;
201 }
202 
CheckIsCloudLocation(const string & key)203 bool CloudFileUtils::CheckIsCloudLocation(const string &key)
204 {
205     return key == CLOUD_FILE_LOCATION;
206 }
207 
CheckIsHmdfsPermission(const string & key)208 bool CloudFileUtils::CheckIsHmdfsPermission(const string &key)
209 {
210     return key == HMDFS_PERMISSION_XATTR;
211 }
212 
CheckIsCloudRecycle(const string & key)213 bool CloudFileUtils::CheckIsCloudRecycle(const string &key)
214 {
215     return key == CLOUD_CLOUD_RECYCLE_XATTR;
216 }
217 
CheckIsFavorite(const string & key)218 bool CloudFileUtils::CheckIsFavorite(const string &key)
219 {
220     return key == IS_FAVORITE_XATTR;
221 }
222 
CheckFileStatus(const string & key)223 bool CloudFileUtils::CheckFileStatus(const string &key)
224 {
225     return key == IS_FILE_STATUS_XATTR;
226 }
227 
LocalWriteOpen(const string & dfsPath)228 bool CloudFileUtils::LocalWriteOpen(const string &dfsPath)
229 {
230     unique_ptr<char[]> absPath = make_unique<char[]>(PATH_MAX + 1);
231     if (realpath(dfsPath.c_str(), absPath.get()) == nullptr) {
232         return false;
233     }
234     string realPath = absPath.get();
235     char resolvedPath[PATH_MAX] = {'\0'};
236     char *realPaths = realpath(realPath.c_str(), resolvedPath);
237     if (realPaths == NULL) {
238         LOGE("realpath failed");
239         return false;
240     }
241     int fd = open(realPaths, O_RDONLY);
242     if (fd < 0) {
243         LOGE("open failed, errno:%{public}d", errno);
244         return false;
245     }
246     uint32_t writeOpenCnt = 0;
247     int ret = ioctl(fd, HMDFS_IOC_GET_WRITEOPEN_CNT, &writeOpenCnt);
248     close(fd);
249     if (ret < 0) {
250         LOGE("ioctl failed, errno:%{public}d", errno);
251         return false;
252     }
253 
254     return writeOpenCnt != 0;
255 }
256 } // namespace CloudDisk
257 } // namespace FileManagement
258 } // namespace OHOS
259