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 #include <cstring>
18 #include <climits>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <type_traits>
22 #include "media_errors.h"
23 #include "media_log.h"
24
25 namespace {
26 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "UriHelper"};
27 }
28
29 namespace OHOS {
30 namespace Media {
31 static const std::map<std::string_view, uint8_t> g_validUriTypes = {
32 {"", UriHelper::UriType::URI_TYPE_FILE }, // empty uri head is treated as the file type uri.
33 {"file", UriHelper::UriType::URI_TYPE_FILE},
34 {"fd", UriHelper::UriType::URI_TYPE_FD},
35 {"http", UriHelper::UriType::URI_TYPE_HTTP}
36 };
37
PathToRealFileUrl(const std::string_view & path,std::string & realPath)38 static bool PathToRealFileUrl(const std::string_view &path, std::string &realPath)
39 {
40 CHECK_AND_RETURN_RET_LOG(!path.empty(), false, "path is empty!");
41 CHECK_AND_RETURN_RET_LOG(path.length() < PATH_MAX, false,
42 "path len is error, the len is: [%{public}zu]", path.length());
43
44 char tmpPath[PATH_MAX] = {0};
45 char *pathAddr = realpath(path.data(), tmpPath);
46 CHECK_AND_RETURN_RET_LOG(pathAddr != nullptr, false,
47 "path to realpath error, %{public}s", path.data());
48
49 int ret = access(tmpPath, F_OK);
50 CHECK_AND_RETURN_RET_LOG(ret == 0, false,
51 "check realpath (%{private}s) error", tmpPath);
52
53 realPath = std::string("file://") + tmpPath;
54 return true;
55 }
56
57 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)58 bool StrToInt(const std::string_view& str, T& value)
59 {
60 if (str.empty() || (!isdigit(str.front()) && (str.front() != '-'))) {
61 return false;
62 }
63 std::string valStr(str);
64 char* end = nullptr;
65 errno = 0;
66 const char* addr = valStr.c_str();
67 long long result = strtoll(addr, &end, 10); /* 10 means decimal */
68 CHECK_AND_RETURN_RET_LOG(result >= LLONG_MIN && result <= LLONG_MAX, false,
69 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
70 CHECK_AND_RETURN_RET_LOG(end != addr && end[0] == '\0' && errno != ERANGE, false,
71 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
72 if constexpr (std::is_same<int32_t, T>::value) {
73 CHECK_AND_RETURN_RET_LOG(result >= INT_MIN && result <= INT_MAX, false,
74 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
75 value = static_cast<int32_t>(result);
76 return true;
77 }
78 value = result;
79 return true;
80 }
81
82 __attribute__((no_sanitize("cfi")))
SplitUriHeadAndBody(const std::string_view & str)83 std::pair<std::string_view, std::string_view> SplitUriHeadAndBody(const std::string_view &str)
84 {
85 std::string_view::size_type start = str.find_first_not_of(' ');
86 std::string_view::size_type end = str.find_last_not_of(' ');
87 std::pair<std::string_view, std::string_view> result;
88 std::string_view noSpaceStr;
89 if (start == std::string_view::npos && end == std::string_view::npos) {
90 result.first = "";
91 result.second = "";
92 return result;
93 }
94 if (end == std::string_view::npos) {
95 noSpaceStr = str.substr(start);
96 } else {
97 if (end >= start) {
98 noSpaceStr = str.substr(start, end - start + 1);
99 }
100 }
101 std::string_view delimiter = "://";
102 std::string_view::size_type pos = noSpaceStr.find(delimiter);
103 if (pos == std::string_view::npos) {
104 result.first = "";
105 result.second = noSpaceStr;
106 } else {
107 result.first = noSpaceStr.substr(0, pos);
108 result.second = noSpaceStr.substr(pos + delimiter.size());
109 }
110 return result;
111 }
112
UriHelper(const std::string_view & uri)113 UriHelper::UriHelper(const std::string_view &uri)
114 {
115 FormatMeForUri(uri);
116 }
117
UriHelper(int32_t fd,int64_t offset,int64_t size)118 UriHelper::UriHelper(int32_t fd, int64_t offset, int64_t size) : fd_(dup(fd)), offset_(offset), size_(size)
119 {
120 FormatMeForFd();
121 }
122
~UriHelper()123 UriHelper::~UriHelper()
124 {
125 if (fd_ >= 0) {
126 (void)::close(fd_);
127 fd_ = -1;
128 }
129 }
130
FormatMeForUri(const std::string_view & uri)131 void UriHelper::FormatMeForUri(const std::string_view &uri) noexcept
132 {
133 CHECK_AND_RETURN_LOG(formattedUri_.empty(),
134 "formattedUri is valid:%{public}s", formattedUri_.c_str());
135 CHECK_AND_RETURN_LOG(!uri.empty(), "uri is empty");
136 auto [head, body] = SplitUriHeadAndBody(uri);
137 CHECK_AND_RETURN(g_validUriTypes.count(head) != 0);
138 type_ = g_validUriTypes.at(head);
139 // verify whether the uri is readable and generate the formatted uri.
140 switch (type_) {
141 case URI_TYPE_FILE: {
142 if (!PathToRealFileUrl(body, formattedUri_)) {
143 type_ = URI_TYPE_UNKNOWN;
144 formattedUri_ = body;
145 }
146 rawFileUri_ = formattedUri_;
147 if (rawFileUri_.size() > strlen("file://")) {
148 rawFileUri_ = rawFileUri_.substr(strlen("file://"));
149 }
150 break;
151 }
152 case URI_TYPE_FD: {
153 if (!ParseFdUri(body)) {
154 type_ = URI_TYPE_UNKNOWN;
155 formattedUri_ = "";
156 }
157 break;
158 }
159 default:
160 formattedUri_ = std::string(head);
161 formattedUri_ += body;
162 break;
163 }
164 MEDIA_LOGD("0x%{public}06" PRIXPTR " formatted uri: %{private}s", FAKE_POINTER(this), formattedUri_.c_str());
165 }
166
FormatMeForFd()167 void UriHelper::FormatMeForFd() noexcept
168 {
169 MEDIA_LOGI("0x%{public}06" PRIXPTR " UriHelper FormatMeForFd fd is %{public}d", FAKE_POINTER(this), fd_);
170 CHECK_AND_RETURN_LOG(formattedUri_.empty(),
171 "formattedUri is valid:%{public}s", formattedUri_.c_str());
172 type_ = URI_TYPE_FD;
173 (void)CorrectFdParam();
174 }
175
CorrectFdParam()176 bool UriHelper::CorrectFdParam()
177 {
178 int flags = fcntl(fd_, F_GETFL);
179 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags");
180 struct stat64 st;
181 CHECK_AND_RETURN_RET_LOG(fstat64(fd_, &st) == 0, false,
182 "can not get file state");
183 int64_t fdSize = static_cast<int64_t>(st.st_size);
184 int64_t stIno = static_cast<int64_t>(st.st_ino);
185 int64_t stSec = static_cast<int64_t>(st.st_atim.tv_sec);
186 MEDIA_LOGI("CorrectFdParam fd: %{public}d, fdSize: %{public}" PRId64 ", stIno: %{public}" PRId64
187 ", stSec: %{public}" PRId64, fd_, fdSize, stIno, stSec);
188 if (offset_ < 0 || offset_ > fdSize) {
189 offset_ = 0;
190 }
191 if ((size_ <= 0) || (size_ > fdSize - offset_)) {
192 size_ = fdSize - offset_;
193 }
194 formattedUri_ = std::string("fd://") + std::to_string(fd_) + "?offset=" +
195 std::to_string(offset_) + "&size=" + std::to_string(size_);
196 MEDIA_LOGI("CorrectFdParam formattedUri: %{public}s", formattedUri_.c_str());
197 return true;
198 }
199
UriType() const200 uint8_t UriHelper::UriType() const
201 {
202 return type_;
203 }
204
FormattedUri() const205 std::string UriHelper::FormattedUri() const
206 {
207 return formattedUri_;
208 }
209
AccessCheck(uint8_t flag) const210 bool UriHelper::AccessCheck(uint8_t flag) const
211 {
212 CHECK_AND_RETURN_RET_LOG(type_ != URI_TYPE_UNKNOWN, false, "type is unknown");
213 if (type_ == URI_TYPE_FILE) {
214 uint32_t mode = (flag & URI_READ) ? R_OK : 0;
215 mode |= (flag & URI_WRITE) ? W_OK : 0;
216 int ret = access(rawFileUri_.data(), static_cast<int>(mode));
217 CHECK_AND_RETURN_RET_LOG(ret == 0, false,
218 "Fail to access path: %{public}s", rawFileUri_.data());
219 return true;
220 } else if (type_ == URI_TYPE_FD) {
221 CHECK_AND_RETURN_RET_LOG(fd_ > 0, false, "Fail to get file descriptor from uri, fd %{public}d", fd_);
222 int flags = fcntl(fd_, F_GETFL);
223 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags, fd %{public}d", fd_);
224 uint32_t mode = (flag & URI_WRITE) ? O_RDWR : O_RDONLY;
225 return ((static_cast<unsigned int>(flags) & mode) != mode) ? false : true;
226 }
227 return true; // Not implemented, defaultly return true.
228 }
229
ParseFdUri(std::string_view uri)230 bool UriHelper::ParseFdUri(std::string_view uri)
231 {
232 static constexpr std::string_view::size_type delim1Len = std::string_view("?offset=").size();
233 static constexpr std::string_view::size_type delim2Len = std::string_view("&size=").size();
234 std::string_view::size_type delim1 = uri.find("?");
235 std::string_view::size_type delim2 = uri.find("&");
236 if (delim1 == std::string_view::npos && delim2 == std::string_view::npos) {
237 CHECK_AND_RETURN_RET_LOG(StrToInt(uri, fd_), false, "Invalid fd url");
238 } else if (delim1 != std::string_view::npos && delim2 != std::string_view::npos) {
239 std::string_view fdstr = uri.substr(0, delim1);
240 int32_t fd = -1;
241 CHECK_AND_RETURN_RET_LOG(StrToInt(fdstr, fd) && delim1 + delim1Len < uri.size()
242 && delim2 - delim1 - delim1Len > 0, false, "Invalid fd url");
243 std::string_view offsetStr = uri.substr(delim1 + delim1Len, delim2 - delim1 - delim1Len);
244 CHECK_AND_RETURN_RET_LOG(StrToInt(offsetStr, offset_) && delim2 + delim2Len < uri.size(), false,
245 "Invalid fd url");
246 std::string_view sizeStr = uri.substr(delim2 + delim2Len);
247 CHECK_AND_RETURN_RET_LOG(StrToInt(sizeStr, size_), false, "Invalid fd url");
248 MEDIA_LOGI("UriHelper ParseFdUri try close fd, fd is %{public}d, Set fd: %{public}d", fd_, fd);
249 if (fd_ >= 0) {
250 close(fd_);
251 fd_ = -1;
252 }
253 fd_ = dup(fd);
254 MEDIA_LOGI("UriHelper ParseFdUri dup, fd is %{public}d", fd_);
255 } else {
256 MEDIA_LOGE("invalid fd uri: %{private}s", uri.data());
257 return false;
258 }
259
260 MEDIA_LOGD("parse fd uri, fd: %{public}d, offset: %{public}" PRIi64 ", size: %{public}" PRIi64,
261 fd_, offset_, size_);
262 return CorrectFdParam();
263 }
264 } // namespace Media
265 } // namespace OHOS
266