1 /*
2 * Copyright (c) 2025 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
16 #include "copy/file_size_utils.h"
17
18 #include <sys/stat.h>
19 #include <filesystem>
20
21 #include "dfs_error.h"
22 #include "file_uri.h"
23 #include "utils_log.h"
24
25 #undef LOG_DOMAIN
26 #undef LOG_TAG
27 #define LOG_DOMAIN 0xD004315
28 #define LOG_TAG "distributedfile_daemon"
29
30 namespace OHOS {
31 namespace Storage {
32 namespace DistributedFile {
33 using namespace AppFileService;
34 using namespace AppFileService::ModuleFileUri;
35 using namespace FileManagement;
36 static constexpr int DISMATCH = 0;
37 static constexpr int MATCH = 1;
38 static constexpr char PATH_INVALID_FLAG1[] = "../";
39 static constexpr char PATH_INVALID_FLAG2[] = "/..";
40 static const uint32_t PATH_INVALID_FLAG_LEN = 3;
41 static const char FILE_SEPARATOR_CHAR = '/';
42 static constexpr char NETWORK_ID[] = "?networkid=";
43
GetSize(const std::string & uri,bool isSrcUri,uint64_t & size)44 int32_t FileSizeUtils::GetSize(const std::string &uri, bool isSrcUri, uint64_t &size)
45 {
46 if (!IsFilePathValid(GetRealUri(uri))) {
47 LOGE("path is forbidden");
48 return OHOS::FileManagement::E_ILLEGAL_URI;
49 }
50 auto path = GetPathFromUri(uri, isSrcUri);
51 if (path.empty()) {
52 return ERR_BAD_VALUE;
53 }
54
55 bool isDirectory;
56 auto ret = IsDirectory(uri, isSrcUri, isDirectory);
57 if (ret != E_OK) {
58 return ret;
59 }
60 if (isDirectory) {
61 return GetDirSize(path, size);
62 } else {
63 return GetFileSize(path, size);
64 }
65 }
66
IsDirectory(const std::string & uri,bool isSrcUri,bool & isDirectory)67 int32_t FileSizeUtils::IsDirectory(const std::string &uri, bool isSrcUri, bool &isDirectory)
68 {
69 if (!IsFilePathValid(GetRealUri(uri))) {
70 LOGE("path is forbidden");
71 return OHOS::FileManagement::E_ILLEGAL_URI;
72 }
73 auto path = GetPathFromUri(uri, isSrcUri);
74 if (path.empty()) {
75 return ERR_BAD_VALUE;
76 }
77
78 bool isDir = false;
79 auto ret = IsDirectory(path, isDir);
80 if (ret != E_OK) {
81 return ret;
82 }
83
84 bool isFile = false;
85 ret = IsFile(path, isFile);
86 if (ret != E_OK) {
87 return ret;
88 }
89
90 if (isDir == isFile) {
91 return ERR_BAD_VALUE;
92 }
93 isDirectory = isDir;
94 return E_OK;
95 }
96
FilterFunc(const struct dirent * filename)97 int FileSizeUtils::FilterFunc(const struct dirent *filename)
98 {
99 if (std::string_view(filename->d_name) == "." || std::string_view(filename->d_name) == "..") {
100 return DISMATCH;
101 }
102 return MATCH;
103 }
104
Deleter(struct NameList * arg)105 void FileSizeUtils::Deleter(struct NameList *arg)
106 {
107 for (int i = 0; i < arg->direntNum; i++) {
108 if ((arg->namelist)[i]) {
109 free((arg->namelist)[i]);
110 }
111 (arg->namelist)[i] = nullptr;
112 }
113 free(arg->namelist);
114 arg->namelist = nullptr;
115 delete arg;
116 arg = nullptr;
117 }
118
GetDirNameList(const std::string & path)119 std::unique_ptr<struct NameList, decltype(FileSizeUtils::Deleter) *> FileSizeUtils::GetDirNameList(
120 const std::string &path)
121 {
122 std::unique_ptr<struct NameList, decltype(Deleter) *> pNameList = { new (std::nothrow) struct NameList, Deleter };
123 if (pNameList == nullptr) {
124 LOGE("Failed to request heap memory.");
125 return pNameList;
126 }
127 int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
128 pNameList->direntNum = num;
129 return pNameList;
130 }
131
GetFileSize(const std::string & path,uint64_t & size)132 int32_t FileSizeUtils::GetFileSize(const std::string &path, uint64_t &size)
133 {
134 struct stat buf {};
135 int ret = stat(path.c_str(), &buf);
136 if (ret == -1) {
137 LOGI("Stat failed.");
138 size = 0;
139 return errno;
140 }
141 size = static_cast<uint64_t>(buf.st_size);
142 return E_OK;
143 }
144
GetDirSize(const std::string & path,uint64_t & size)145 int32_t FileSizeUtils::GetDirSize(const std::string &path, uint64_t &size)
146 {
147 auto pNameList = GetDirNameList(path);
148 if (pNameList == nullptr) {
149 return ENOMEM;
150 }
151 size = 0;
152 for (int i = 0; i < pNameList->direntNum; i++) {
153 if ((pNameList->namelist[i]) == nullptr) {
154 continue;
155 }
156 std::string dest = path + '/' + std::string((pNameList->namelist[i])->d_name);
157 if ((pNameList->namelist[i])->d_type == DT_LNK) {
158 continue;
159 }
160 if ((pNameList->namelist[i])->d_type == DT_DIR) {
161 uint64_t subSize = 0;
162 auto err = GetDirSize(dest, subSize);
163 if (err != E_OK) {
164 LOGE("GetDirSize failed");
165 return err;
166 }
167 size += subSize;
168 } else {
169 struct stat st {};
170 if (stat(dest.c_str(), &st) == -1) {
171 return errno;
172 }
173 size += static_cast<uint64_t>(st.st_size);
174 }
175 }
176 return E_OK;
177 }
178
IsFile(const std::string & path,bool & result)179 int32_t FileSizeUtils::IsFile(const std::string &path, bool &result)
180 {
181 struct stat buf {};
182 int ret = stat(path.c_str(), &buf);
183 if (ret == -1) {
184 return errno;
185 }
186 result = (buf.st_mode & S_IFMT) == S_IFREG;
187 return E_OK;
188 }
189
IsDirectory(const std::string & path,bool & result)190 int32_t FileSizeUtils::IsDirectory(const std::string &path, bool &result)
191 {
192 struct stat buf {};
193 int ret = stat(path.c_str(), &buf);
194 if (ret == -1) {
195 return errno;
196 }
197 result = (buf.st_mode & S_IFMT) == S_IFDIR;
198 return E_OK;
199 }
200
GetPathFromUri(const std::string & uri,bool isSrcUri)201 std::string FileSizeUtils::GetPathFromUri(const std::string &uri, bool isSrcUri)
202 {
203 std::string path;
204 FileUri fileUri(uri);
205 if (isSrcUri) {
206 path = GetRealPath(fileUri.GetRealPath());
207 } else {
208 path = GetRealPath(fileUri.GetPath());
209 }
210 return path;
211 }
212
GetRealPath(const std::string & path)213 std::string FileSizeUtils::GetRealPath(const std::string &path)
214 {
215 std::filesystem::path tempPath(path);
216 std::filesystem::path realPath{};
217 for (const auto& component : tempPath) {
218 if (component == ".") {
219 continue;
220 } else if (component == "..") {
221 realPath = realPath.parent_path();
222 } else {
223 realPath /= component;
224 }
225 }
226 return realPath.string();
227 }
228
GetRealUri(const std::string & uri)229 std::string FileSizeUtils::GetRealUri(const std::string &uri)
230 {
231 std::string realUri = uri;
232 auto pos = uri.find(NETWORK_ID);
233 if (pos != std::string::npos) {
234 realUri = uri.substr(0, pos);
235 }
236 return realUri;
237 }
238
IsFilePathValid(const std::string & filePath)239 bool FileSizeUtils::IsFilePathValid(const std::string &filePath)
240 {
241 size_t pos = filePath.find(PATH_INVALID_FLAG1);
242 while (pos != std::string::npos) {
243 if (pos == 0 || filePath[pos - 1] == FILE_SEPARATOR_CHAR) {
244 LOGE("Relative path is not allowed, path contain ../");
245 return false;
246 }
247 pos = filePath.find(PATH_INVALID_FLAG1, pos + PATH_INVALID_FLAG_LEN);
248 }
249 pos = filePath.rfind(PATH_INVALID_FLAG2);
250 if ((pos != std::string::npos) && (filePath.size() - pos == PATH_INVALID_FLAG_LEN)) {
251 LOGE("Relative path is not allowed, path tail is /..");
252 return false;
253 }
254 return true;
255 }
256 } // namespace DistributedFile
257 } // namespace Storage
258 } // namespace OHOS
259