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