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