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 25namespace { 26 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "UriHelper"}; 27} 28 29namespace OHOS { 30namespace Media { 31static 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 38static 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 57template<typename T, typename = std::enable_if_t<std::is_same_v<int64_t, T> || std::is_same_v<int32_t, T>>> 58bool 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"))) 83std::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 113UriHelper::UriHelper(const std::string_view &uri) 114{ 115 FormatMeForUri(uri); 116} 117 118UriHelper::UriHelper(int32_t fd, int64_t offset, int64_t size) : fd_(dup(fd)), offset_(offset), size_(size) 119{ 120 FormatMeForFd(); 121} 122 123UriHelper::~UriHelper() 124{ 125 if (fd_ >= 0) { 126 (void)::close(fd_); 127 fd_ = -1; 128 } 129} 130 131void 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 167void 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 176bool 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 200uint8_t UriHelper::UriType() const 201{ 202 return type_; 203} 204 205std::string UriHelper::FormattedUri() const 206{ 207 return formattedUri_; 208} 209 210bool 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 230bool 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