1 /*
2 * Copyright (C) 2021 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 "uri_helper.h"
17
18 #include <fcntl.h>
19 #include <map>
20 #include <sys/stat.h>
21 #include <type_traits>
22 #include <unistd.h>
23
24 #include "media_log.h"
25 #include "medialibrary_errno.h"
26
27 namespace OHOS {
28 namespace Media {
29 static const std::map<std::string_view, uint8_t> g_validUriTypes = {
30 {"", UriHelper::UriType::URI_TYPE_FILE }, // empty uri head is treated as the file type uri.
31 {"file", UriHelper::UriType::URI_TYPE_FILE},
32 {"fd", UriHelper::UriType::URI_TYPE_FD},
33 {"http", UriHelper::UriType::URI_TYPE_HTTP}
34 };
35
PathToRealFileUrl(const std::string_view & path,std::string & realPath)36 static bool PathToRealFileUrl(const std::string_view &path, std::string &realPath)
37 {
38 if (path.empty()) {
39 MEDIA_INFO_LOG("path is empty!");
40 return false;
41 }
42
43 if ((path.length() >= PATH_MAX)) {
44 MEDIA_INFO_LOG("path len is error, the len is: [%{public}zu]", path.length());
45 return false;
46 }
47
48 char tmpPath[PATH_MAX] = {0};
49 if (realpath(path.data(), tmpPath) == nullptr) {
50 MEDIA_INFO_LOG("path to realpath error, %{public}s", path.data());
51 return false;
52 }
53
54 if (access(tmpPath, F_OK) != 0) {
55 MEDIA_INFO_LOG("check realpath (%{private}s) error", tmpPath);
56 return false;
57 }
58
59 realPath = std::string("file://") + tmpPath;
60 return true;
61 }
62
63 template<typename T, typename = std::enable_if_t<std::is_same_v<int64_t, T> || std::is_same_v<int32_t, T>>>
StrToInt(const std::string_view & str,T & value)64 bool StrToInt(const std::string_view& str, T& value)
65 {
66 if (str.empty() || (!isdigit(str.front()) && (str.front() != '-'))) {
67 return false;
68 }
69
70 std::string valStr(str);
71 char* end = nullptr;
72 errno = 0;
73 const char* addr = valStr.c_str();
74 long long result = strtoll(addr, &end, 10); /* 10 means decimal */
75 if ((end == addr) || (end[0] != '\0') || (errno == ERANGE) ||
76 (result > LLONG_MAX) || (result < LLONG_MIN)) {
77 MEDIA_INFO_LOG("call StrToInt func false, input str is: %{public}s!", valStr.c_str());
78 return false;
79 }
80
81 if constexpr (std::is_same<int32_t, T>::value) {
82 if ((result > INT_MAX) || (result < INT_MIN)) {
83 MEDIA_INFO_LOG("call StrToInt func false, input str is: %{public}s!", valStr.c_str());
84 return false;
85 }
86 value = static_cast<int32_t>(result);
87 return true;
88 }
89
90 value = result;
91 return true;
92 }
93
SplitUriHeadAndBody(const std::string_view & str)94 std::pair<std::string_view, std::string_view> SplitUriHeadAndBody(const std::string_view &str)
95 {
96 std::string_view::size_type start = str.find_first_not_of(' ');
97 std::string_view::size_type end = str.find_last_not_of(' ');
98 std::pair<std::string_view, std::string_view> result;
99 std::string_view noSpaceStr;
100
101 if (end == std::string_view::npos) {
102 noSpaceStr = str.substr(start);
103 } else {
104 noSpaceStr = str.substr(start, end - start + 1);
105 }
106
107 std::string_view delimiter = "://";
108 std::string_view::size_type pos = noSpaceStr.find(delimiter);
109 if (pos == std::string_view::npos) {
110 result.first = "";
111 result.second = noSpaceStr;
112 } else {
113 result.first = noSpaceStr.substr(0, pos);
114 result.second = noSpaceStr.substr(pos + delimiter.size());
115 }
116
117 return result;
118 }
119
Swap(UriHelper && rhs)120 void UriHelper::Swap(UriHelper &&rhs) noexcept
121 {
122 formattedUri_.swap(rhs.formattedUri_);
123 type_ = rhs.type_;
124 rawFileUri_ = rhs.rawFileUri_;
125 std::swap(fd_, rhs.fd_);
126 offset_ = rhs.offset_;
127 size_ = rhs.size_;
128 }
129
Copy(const UriHelper & rhs)130 void UriHelper::Copy(const UriHelper &rhs) noexcept
131 {
132 formattedUri_ = rhs.formattedUri_;
133 type_ = rhs.type_;
134 if (type_ == UriHelper::URI_TYPE_FILE) {
135 rawFileUri_ = formattedUri_;
136 rawFileUri_ = rawFileUri_.substr(strlen("file://"));
137 }
138 if (rhs.fd_ > 0) {
139 fd_ = ::dup(rhs.fd_);
140 }
141 offset_ = rhs.offset_;
142 size_ = rhs.size_;
143 }
144
UriHelper(const std::string_view & uri)145 UriHelper::UriHelper(const std::string_view &uri)
146 {
147 FormatMeForUri(uri);
148 }
149
UriHelper(int32_t fd,int64_t offset,int64_t size)150 UriHelper::UriHelper(int32_t fd, int64_t offset, int64_t size) : fd_(fd), offset_(offset), size_(size)
151 {
152 FormatMeForFd();
153 }
154
~UriHelper()155 UriHelper::~UriHelper()
156 {
157 if (fd_ > 0) {
158 (void)::close(fd_);
159 }
160 }
161
UriHelper(const UriHelper & rhs)162 UriHelper::UriHelper(const UriHelper &rhs)
163 {
164 Copy(rhs);
165 }
166
operator =(const UriHelper & rhs)167 UriHelper &UriHelper::operator=(const UriHelper &rhs)
168 {
169 if (&rhs == this) {
170 return *this;
171 }
172
173 Copy(rhs);
174 return *this;
175 }
176
UriHelper(UriHelper && rhs)177 UriHelper::UriHelper(UriHelper &&rhs) noexcept
178 {
179 Swap(std::forward<UriHelper &&>(rhs));
180 }
181
operator =(UriHelper && rhs)182 UriHelper &UriHelper::operator=(UriHelper &&rhs) noexcept
183 {
184 if (&rhs == this) {
185 return *this;
186 }
187
188 Swap(std::forward<UriHelper &&>(rhs));
189 return *this;
190 }
191
FormatMeForUri(const std::string_view & uri)192 void UriHelper::FormatMeForUri(const std::string_view &uri) noexcept
193 {
194 if (!formattedUri_.empty()) {
195 return;
196 }
197
198 auto [head, body] = SplitUriHeadAndBody(uri);
199 if (g_validUriTypes.count(head) == 0) {
200 return;
201 }
202 type_ = g_validUriTypes.at(head);
203
204 // verify whether the uri is readable and generate the formatted uri.
205 switch (type_) {
206 case URI_TYPE_FILE: {
207 if (!PathToRealFileUrl(body, formattedUri_)) {
208 type_ = URI_TYPE_UNKNOWN;
209 formattedUri_ = body;
210 }
211 rawFileUri_ = formattedUri_;
212 rawFileUri_ = rawFileUri_.substr(strlen("file://"));
213 break;
214 }
215 case URI_TYPE_FD: {
216 if (!ParseFdUri(body)) {
217 type_ = URI_TYPE_UNKNOWN;
218 formattedUri_ = "";
219 }
220 break;
221 }
222 default:
223 formattedUri_ = std::string(head);
224 formattedUri_ += body;
225 break;
226 }
227
228 MEDIA_INFO_LOG("formatted uri: %{public}s", formattedUri_.c_str());
229 }
230
FormatMeForFd()231 void UriHelper::FormatMeForFd() noexcept
232 {
233 if (!formattedUri_.empty()) {
234 return;
235 }
236
237 type_ = URI_TYPE_FD;
238 (void)CorrectFdParam();
239 }
240
CorrectFdParam()241 bool UriHelper::CorrectFdParam()
242 {
243 int flags = fcntl(fd_, F_GETFL);
244 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags");
245
246 struct stat64 st;
247 if (fstat64(fd_, &st) != 0) {
248 MEDIA_INFO_LOG("can not get file state");
249 return false;
250 }
251
252 int64_t fdSize = static_cast<int64_t>(st.st_size);
253 if (offset_ < 0 || offset_ > fdSize) {
254 offset_ = 0;
255 }
256
257 if ((size_ <= 0) || (size_ > fdSize - offset_)) {
258 size_ = fdSize - offset_;
259 }
260
261 fd_ = ::dup(fd_);
262 formattedUri_ = std::string("fd://") + std::to_string(fd_) + "?offset=" +
263 std::to_string(offset_) + "&size=" + std::to_string(size_);
264 return true;
265 }
266
ParseFdUri(int32_t & fd,int64_t & offset,int64_t size)267 bool UriHelper::ParseFdUri(int32_t &fd, int64_t &offset, int64_t size)
268 {
269 if (type_ != URI_TYPE_FD) {
270 return false;
271 }
272
273 fd = fd_;
274 offset = offset_;
275 size = size_;
276 return true;
277 }
278
UriType() const279 uint8_t UriHelper::UriType() const
280 {
281 return type_;
282 }
283
FormattedUri() const284 std::string UriHelper::FormattedUri() const
285 {
286 return formattedUri_;
287 }
288
AccessCheck(uint8_t flag) const289 bool UriHelper::AccessCheck(uint8_t flag) const
290 {
291 if (type_ == URI_TYPE_UNKNOWN) {
292 return false;
293 }
294
295 if (type_ == URI_TYPE_FILE) {
296 uint32_t mode = (flag & URI_READ) ? R_OK : 0;
297 mode |= (flag & URI_WRITE) ? W_OK : 0;
298 int ret = access(rawFileUri_.data(), static_cast<int>(mode));
299 if (ret != 0) {
300 MEDIA_INFO_LOG("Fail to access path: %{public}s", rawFileUri_.data());
301 return false;
302 }
303 return true;
304 } else if (type_ == URI_TYPE_FD) {
305 CHECK_AND_RETURN_RET_LOG(fd_ > 0, false, "Fail to get file descriptor from uri");
306
307 int flags = fcntl(fd_, F_GETFL);
308 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags");
309
310 uint32_t mode = (flag & URI_WRITE) ? O_RDWR : O_RDONLY;
311 if ((static_cast<unsigned int>(flags) & mode) != mode) {
312 return false;
313 }
314 return true;
315 }
316
317 return true; // Not implemented, defaultly return true.
318 }
319
ParseFdUri(std::string_view uri)320 bool UriHelper::ParseFdUri(std::string_view uri)
321 {
322 static constexpr std::string_view::size_type delim1Len = std::string_view("?offset=").size();
323 static constexpr std::string_view::size_type delim2Len = std::string_view("&size=").size();
324 std::string_view::size_type delim1 = uri.find("?");
325 std::string_view::size_type delim2 = uri.find("&");
326
327 if (delim1 == std::string_view::npos && delim1 == std::string_view::npos) {
328 CHECK_AND_RETURN_RET_LOG(StrToInt(uri, fd_), false, "Invalid fd url");
329 } else if (delim1 != std::string_view::npos && delim2 != std::string_view::npos) {
330 std::string_view fdstr = uri.substr(0, delim1);
331 int32_t fd = -1;
332 CHECK_AND_RETURN_RET_LOG(StrToInt(fdstr, fd), false, "Invalid fd url");
333 std::string_view offsetStr = uri.substr(delim1 + delim1Len, delim2 - delim1 - delim1Len);
334 CHECK_AND_RETURN_RET_LOG(StrToInt(offsetStr, offset_), false, "Invalid fd url");
335 std::string_view sizeStr = uri.substr(delim2 + delim2Len);
336 CHECK_AND_RETURN_RET_LOG(StrToInt(sizeStr, size_), false, "Invalid fd url");
337 fd_ = fd;
338 } else {
339 MEDIA_INFO_LOG("invalid fd uri: %{public}s", uri.data());
340 return false;
341 }
342 return CorrectFdParam();
343 }
344 } // namespace Media
345 } // namespace OHOS
346