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