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, "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
64 std::string valStr(str);
65 char* end = nullptr;
66 errno = 0;
67 const char* addr = valStr.c_str();
68 long long result = strtoll(addr, &end, 10); /* 10 means decimal */
69 CHECK_AND_RETURN_RET_LOG(result >= LLONG_MIN && result <= LLONG_MAX, false,
70 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
71 CHECK_AND_RETURN_RET_LOG(end != addr && end[0] == '\0' && errno != ERANGE, false,
72 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
73
74 if constexpr (std::is_same<int32_t, T>::value) {
75 CHECK_AND_RETURN_RET_LOG(result >= INT_MIN && result <= INT_MAX, false,
76 "call StrToInt func false, input str is: %{public}s!", valStr.c_str());
77 value = static_cast<int32_t>(result);
78 return true;
79 }
80
81 value = result;
82 return true;
83 }
84
SplitUriHeadAndBody(const std::string_view & str)85 std::pair<std::string_view, std::string_view> SplitUriHeadAndBody(const std::string_view &str)
86 {
87 std::string_view::size_type start = str.find_first_not_of(' ');
88 std::string_view::size_type end = str.find_last_not_of(' ');
89 std::pair<std::string_view, std::string_view> result;
90 std::string_view noSpaceStr;
91
92 if (start == std::string_view::npos && end == std::string_view::npos) {
93 result.first = "";
94 result.second = "";
95 return result;
96 }
97
98 if (end == std::string_view::npos) {
99 noSpaceStr = str.substr(start);
100 } else {
101 noSpaceStr = str.substr(start, end - start + 1);
102 }
103 std::string_view delimiter = "://";
104 std::string_view::size_type pos = noSpaceStr.find(delimiter);
105 if (pos == std::string_view::npos) {
106 result.first = "";
107 result.second = noSpaceStr;
108 } else {
109 result.first = noSpaceStr.substr(0, pos);
110 result.second = noSpaceStr.substr(pos + delimiter.size());
111 }
112 return result;
113 }
114
UriHelper(const std::string_view & uri)115 UriHelper::UriHelper(const std::string_view &uri)
116 {
117 FormatMeForUri(uri);
118 }
119
UriHelper(int32_t fd,int64_t offset,int64_t size)120 UriHelper::UriHelper(int32_t fd, int64_t offset, int64_t size) : fd_(fd), offset_(offset), size_(size)
121 {
122 FormatMeForFd();
123 }
124
~UriHelper()125 UriHelper::~UriHelper()
126 {
127 if (fd_ > 0) {
128 (void)::close(fd_);
129 }
130 }
131
FormatMeForUri(const std::string_view & uri)132 void UriHelper::FormatMeForUri(const std::string_view &uri) noexcept
133 {
134 CHECK_AND_RETURN_LOG(formattedUri_.empty(),
135 "formattedUri is valid:%{public}s", formattedUri_.c_str());
136
137 auto [head, body] = SplitUriHeadAndBody(uri);
138 CHECK_AND_RETURN(g_validUriTypes.count(head) != 0);
139 type_ = g_validUriTypes.at(head);
140
141 // verify whether the uri is readable and generate the formatted uri.
142 switch (type_) {
143 case URI_TYPE_FILE: {
144 if (!PathToRealFileUrl(body, formattedUri_)) {
145 type_ = URI_TYPE_UNKNOWN;
146 formattedUri_ = body;
147 }
148 rawFileUri_ = formattedUri_;
149 if (rawFileUri_.size() > strlen("file://")) {
150 rawFileUri_ = rawFileUri_.substr(strlen("file://"));
151 }
152 break;
153 }
154 case URI_TYPE_FD: {
155 if (!ParseFdUri(body)) {
156 type_ = URI_TYPE_UNKNOWN;
157 formattedUri_ = "";
158 }
159 break;
160 }
161 default:
162 formattedUri_ = std::string(head);
163 formattedUri_ += body;
164 break;
165 }
166
167 MEDIA_LOGI("formatted uri: %{public}s", formattedUri_.c_str());
168 }
169
FormatMeForFd()170 void UriHelper::FormatMeForFd() noexcept
171 {
172 CHECK_AND_RETURN_LOG(formattedUri_.empty(),
173 "formattedUri is valid:%{public}s", formattedUri_.c_str());
174 type_ = URI_TYPE_FD;
175 (void)CorrectFdParam();
176 }
177
CorrectFdParam()178 bool UriHelper::CorrectFdParam()
179 {
180 int flags = fcntl(fd_, F_GETFL);
181 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags");
182
183 struct stat64 st;
184 CHECK_AND_RETURN_RET_LOG(fstat64(fd_, &st) == 0, false,
185 "can not get file state");
186
187 int64_t fdSize = static_cast<int64_t>(st.st_size);
188 if (offset_ < 0 || offset_ > fdSize) {
189 offset_ = 0;
190 }
191
192 if ((size_ <= 0) || (size_ > fdSize - offset_)) {
193 size_ = fdSize - offset_;
194 }
195
196 fd_ = ::dup(fd_);
197 formattedUri_ = std::string("fd://") + std::to_string(fd_) + "?offset=" +
198 std::to_string(offset_) + "&size=" + std::to_string(size_);
199 return true;
200 }
201
UriType() const202 uint8_t UriHelper::UriType() const
203 {
204 return type_;
205 }
206
FormattedUri() const207 std::string UriHelper::FormattedUri() const
208 {
209 return formattedUri_;
210 }
211
AccessCheck(uint8_t flag) const212 bool UriHelper::AccessCheck(uint8_t flag) const
213 {
214 CHECK_AND_RETURN_RET_LOG(type_ != URI_TYPE_UNKNOWN, false, "type is unknown");
215
216 if (type_ == URI_TYPE_FILE) {
217 uint32_t mode = (flag & URI_READ) ? R_OK : 0;
218 mode |= (flag & URI_WRITE) ? W_OK : 0;
219 int ret = access(rawFileUri_.data(), static_cast<int>(mode));
220 CHECK_AND_RETURN_RET_LOG(ret == 0, false,
221 "Fail to access path: %{public}s", rawFileUri_.data());
222 return true;
223 } else if (type_ == URI_TYPE_FD) {
224 CHECK_AND_RETURN_RET_LOG(fd_ > 0, false, "Fail to get file descriptor from uri, fd %{public}d", fd_);
225
226 int flags = fcntl(fd_, F_GETFL);
227 CHECK_AND_RETURN_RET_LOG(flags != -1, false, "Fail to get File Status Flags, fd %{public}d", fd_);
228
229 uint32_t mode = (flag & URI_WRITE) ? O_RDWR : O_RDONLY;
230 return ((static_cast<unsigned int>(flags) & mode) != mode) ? false : true;
231 }
232
233 return true; // Not implemented, defaultly return true.
234 }
235
ParseFdUri(std::string_view uri)236 bool UriHelper::ParseFdUri(std::string_view uri)
237 {
238 static constexpr std::string_view::size_type delim1Len = std::string_view("?offset=").size();
239 static constexpr std::string_view::size_type delim2Len = std::string_view("&size=").size();
240 std::string_view::size_type delim1 = uri.find("?");
241 std::string_view::size_type delim2 = uri.find("&");
242
243 if (delim1 == std::string_view::npos && delim2 == std::string_view::npos) {
244 CHECK_AND_RETURN_RET_LOG(StrToInt(uri, fd_), false, "Invalid fd url");
245 } else if (delim1 != std::string_view::npos && delim2 != std::string_view::npos) {
246 std::string_view fdstr = uri.substr(0, delim1);
247 int32_t fd = -1;
248 CHECK_AND_RETURN_RET_LOG(StrToInt(fdstr, fd) && delim1 + delim1Len < uri.size()
249 && delim2 - delim1 - delim1Len > 0, false, "Invalid fd url");
250 std::string_view offsetStr = uri.substr(delim1 + delim1Len, delim2 - delim1 - delim1Len);
251 CHECK_AND_RETURN_RET_LOG(StrToInt(offsetStr, offset_) && delim2 + delim2Len < uri.size(), false,
252 "Invalid fd url");
253 std::string_view sizeStr = uri.substr(delim2 + delim2Len);
254 CHECK_AND_RETURN_RET_LOG(StrToInt(sizeStr, size_), false, "Invalid fd url");
255 fd_ = fd;
256 } else {
257 MEDIA_LOGE("invalid fd uri: %{public}s", uri.data());
258 return false;
259 }
260
261 MEDIA_LOGD("parse fd uri, fd: %{public}d, offset: %{public}" PRIi64 ", size: %{public}" PRIi64,
262 fd_, offset_, size_);
263
264 return CorrectFdParam();
265 }
266 } // namespace Media
267 } // namespace OHOS
268