• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #define MLOG_TAG "MediaPrivacyManager"
16 
17 #include "media_privacy_manager.h"
18 
19 #include <algorithm>
20 #include <cerrno>
21 #include <mntent.h>
22 #include <securec.h>
23 #include <unistd.h>
24 #include <unordered_map>
25 #include <fcntl.h>
26 
27 #include "epfs.h"
28 #include "image_source.h"
29 #include "media_file_utils.h"
30 #include "media_log.h"
31 #include "medialibrary_errno.h"
32 #include "medialibrary_type_const.h"
33 #include "permission_utils.h"
34 
35 using namespace std;
36 using PrivacyRanges = vector<pair<uint32_t, uint32_t>>;
37 
38 namespace OHOS {
39 namespace Media {
40 
41 constexpr uint32_t E_NO_EXIF = 1;
42 constexpr uint32_t E_NO_PRIVACY_EXIF_TAG = 2;
43 
MediaPrivacyManager(const string & path,const string & mode)44 MediaPrivacyManager::MediaPrivacyManager(const string &path, const string &mode) : path_(path), mode_(mode)
45 {}
46 
~MediaPrivacyManager()47 MediaPrivacyManager::~MediaPrivacyManager()
48 {}
49 
50 const unordered_map<PrivacyType, string> PRIVACY_PERMISSION_MAP = {
51     { PrivacyType::PRIVACY_LOCATION, PERMISSION_NAME_MEDIA_LOCATION },
52 };
53 
54 const vector<string> EXIF_SUPPORTED_EXTENSION = {
55     IMAGE_CONTAINER_TYPE_JPG,
56     IMAGE_CONTAINER_TYPE_JPEG,
57     IMAGE_CONTAINER_TYPE_JPE
58 };
59 
IsTargetExtension(const string & path)60 static bool IsTargetExtension(const string &path)
61 {
62     const string ext = MediaFileUtils::GetExtensionFromPath(path);
63     return find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
64         EXIF_SUPPORTED_EXTENSION.end();
65 }
66 
IsWriteMode(const string & mode)67 static bool IsWriteMode(const string &mode)
68 {
69     return mode.find(MEDIA_FILEMODE_WRITEONLY) != string::npos;
70 }
71 
CheckFsMounted(const string & fsType,const string & mountPoint)72 static bool CheckFsMounted(const string &fsType, const string &mountPoint)
73 {
74     struct mntent mountEntry;
75     constexpr uint32_t mntEntrySize = 1024;
76     char entryStr[mntEntrySize] = {0};
77     FILE *mountTable = setmntent("/proc/mounts", "r");
78     if (mountTable == nullptr) {
79         MEDIA_ERR_LOG("Failed to get mount table, errno:%{public}d", errno);
80         return false;
81     }
82 
83     do {
84         struct mntent *mnt = getmntent_r(mountTable, &mountEntry, entryStr, sizeof(entryStr));
85         if (mnt == nullptr) {
86             endmntent(mountTable);
87             break;
88         }
89         if ((mountEntry.mnt_type != nullptr) &&
90             (mountEntry.mnt_dir != nullptr) &&
91             (strcmp(mountEntry.mnt_type, fsType.c_str()) == 0) &&
92             (strcmp(mountEntry.mnt_dir, mountPoint.c_str()) == 0)) {
93             endmntent(mountTable);
94             return true;
95         }
96     } while (true);
97     return false;
98 }
99 
BindFilterProxyFdToOrigin(const int32_t originFd,int32_t & proxyFd)100 static int32_t BindFilterProxyFdToOrigin(const int32_t originFd, int32_t &proxyFd)
101 {
102     int ret = ioctl(proxyFd, IOC_SET_ORIGIN_FD, &originFd);
103     if (ret < 0) {
104         MEDIA_ERR_LOG("Failed to set origin fd: %{public}d to filter proxy fd: %{public}d, error: %{public}d",
105                       originFd, proxyFd, errno);
106         return ret;
107     }
108     return ret;
109 }
110 
SendRangesToIoctl(const int32_t originFd,const int32_t proxyFd,const PrivacyRanges & rans)111 static int32_t SendRangesToIoctl(const int32_t originFd, const int32_t proxyFd, const PrivacyRanges &rans)
112 {
113     FilterProxyRanges *ranges = (FilterProxyRanges *)malloc(sizeof(*ranges) + sizeof(ranges->range[0]) * rans.size());
114     if (ranges == nullptr) {
115         MEDIA_ERR_LOG("Failed to malloc ranges, errno: %{public}d", errno);
116         return -ENOMEM;
117     }
118     ranges->size = static_cast<uint64_t>(rans.size());
119     ranges->reserved = 0;
120     for (size_t i = 0; i < rans.size(); i++) {
121         // first: offset, second: end
122         ranges->range[i].begin = static_cast<uint64_t>(rans[i].first);
123         ranges->range[i].end = static_cast<uint64_t>(rans[i].second);
124     }
125     int err = ioctl(proxyFd, IOC_SET_FILTER_PROXY_RANGE, ranges);
126     if (err < 0) {
127         MEDIA_ERR_LOG("Failed to set ranges to fd: %{public}d, error: %{public}d", proxyFd, errno);
128     }
129     free(ranges);
130     return err;
131 }
132 
133 /* Caller is responsible to close the returned fd */
OpenOriginFd(const string & path,const string & mode)134 static int32_t OpenOriginFd(const string &path, const string &mode)
135 {
136     return MediaFileUtils::OpenFile(path, mode);
137 }
138 
139 /*
140  * Read to the returned @filterProxyFd will redirect to the file specified by @path, but the privacy ranges(@ranges) in
141  * read buffer will be filtered out and filled with 0.
142  *
143  * Caller is responsible to close the returned @filterProxyFd.
144  */
OpenFilterProxyFd(const string & path,const string & mode,const PrivacyRanges & ranges)145 static int32_t OpenFilterProxyFd(const string &path, const string &mode, const PrivacyRanges &ranges)
146 {
147     if (!CheckFsMounted(FS_TYPE_EPFS, EPFS_MOUNT_POINT)) {
148         MEDIA_INFO_LOG("Epfs is currently not supported yet");
149         return OpenOriginFd(path, mode);
150     }
151 
152     int32_t originFd = open(path.c_str(), O_RDONLY);
153     if (originFd < 0) {
154         MEDIA_ERR_LOG("Failed to open file, errno: %{public}d, path: %{public}s", errno, path.c_str());
155         return originFd;
156     }
157     constexpr mode_t epfsFileMode = 0400;
158     // filterProxyFd_ will be returned to user, so there is no need to close it here.
159     int32_t filterProxyFd = open(EPFS_MOUNT_POINT.c_str(), O_TMPFILE | O_RDWR, epfsFileMode);
160     if (filterProxyFd < 0) {
161         MEDIA_ERR_LOG("Failed to open epfs, error: %{public}d", errno);
162         close(originFd);
163         return filterProxyFd;
164     }
165     int32_t ret = BindFilterProxyFdToOrigin(originFd, filterProxyFd);
166     if (ret < 0) {
167         close(originFd);
168         return ret;
169     }
170     ret = SendRangesToIoctl(originFd, filterProxyFd, ranges);
171     if (ret < 0) {
172         close(originFd);
173         return ret;
174     }
175     close(originFd);
176     MEDIA_INFO_LOG("FilterProxyFd will be returned: %{private}d", filterProxyFd);
177     return filterProxyFd;
178 }
179 
ShowRanges(const PrivacyRanges & ranges)180 static void ShowRanges(const PrivacyRanges &ranges)
181 {
182     for (auto range : ranges) {
183         MEDIA_DEBUG_LOG("Range: [%{public}u, %{public}u)", range.first, range.second);
184     }
185 }
186 
CmpMode(pair<uint32_t,uint32_t> pairA,pair<uint32_t,uint32_t> pairB)187 static bool CmpMode(pair<uint32_t, uint32_t> pairA, pair<uint32_t, uint32_t> pairB)
188 {
189     return pairA.first < pairB.first;
190 }
191 
SortRangesAndCheck(PrivacyRanges & ranges)192 static int32_t SortRangesAndCheck(PrivacyRanges &ranges)
193 {
194     if (ranges.empty()) {
195         return E_SUCCESS;
196     }
197     uint32_t size = ranges.size();
198     if (size > PRIVACY_MAX_RANGES) {
199         MEDIA_ERR_LOG("Privacy ranges size invalid: %{public}d", size);
200         return -EINVAL;
201     }
202     sort(ranges.begin(), ranges.end(), CmpMode);
203 
204     if (ranges[0].first >= ranges[0].second) {
205         MEDIA_ERR_LOG("Incorrect fileter ranges: begin(%{public}u) is not less than end(%{public}u)",
206                       ranges[0].first, ranges[0].second);
207         return -EINVAL;
208     }
209 
210     for (uint32_t i = 1; i < size; i++) {
211         if ((ranges[i].first >= ranges[i].second) || (ranges[i].first < ranges[i - 1].second)) {
212             MEDIA_ERR_LOG("Invalid ranges: [%{public}u, %{public}u), last range is [%{public}u, %{public}u)",
213                           ranges[i].first, ranges[i].second, ranges[i - 1].first, ranges[i - 1].second);
214             return -EINVAL;
215         }
216     }
217     ShowRanges(ranges);
218     return E_SUCCESS;
219 }
220 
CollectRanges(const string & path,const PrivacyType & type,PrivacyRanges & ranges)221 static int32_t CollectRanges(const string &path, const PrivacyType &type, PrivacyRanges &ranges)
222 {
223     SourceOptions opts;
224     opts.formatHint = "image/jpeg";
225     uint32_t err = -1;
226     auto imageSource = ImageSource::CreateImageSource(path, opts, err);
227     if (imageSource == nullptr) {
228         MEDIA_ERR_LOG("Failed to create image source, err: %{public}u", err);
229         return -ENOMEM;
230     }
231 
232     PrivacyRanges areas;
233     err = imageSource->GetFilterArea(type, areas);
234     if ((err != E_SUCCESS) && (err != E_NO_EXIF) && (err != E_NO_PRIVACY_EXIF_TAG)) {
235         MEDIA_ERR_LOG("Failed to get privacy area with type %{public}d, err: %{public}u", type, err);
236         return E_ERR;
237     }
238     for (auto &range : areas) {
239         ranges.insert(ranges.end(), std::make_pair(range.first, range.first + range.second));
240     }
241     return E_SUCCESS;
242 }
243 
244 /*
245  * @path: [Input], the real path of the target file
246  * @mode: [Input], the mode specified by user
247  * @ranges: [Output], the privacy ranges of the target file
248  *
249  * The return value is listed below:
250  * o Not a jpeg file: return success with empty ranges
251  * o Write jpeg with no MEDIA_LOCATION: return permission denied
252  * o Write jpeg with MEDIA_LOCATION: return success with empty ranges
253  * o Read jpeg with no MEDIA_LOCATION: return success with privacy ranges if have any
254  * o Read jpeg with MEDIA_LOCATION: return success with empty ranges
255  * o Other cases: return negative error code.
256  */
GetPrivacyRanges(const string & path,const string & mode,PrivacyRanges & ranges)257 static int32_t GetPrivacyRanges(const string &path, const string &mode, PrivacyRanges &ranges)
258 {
259     if (!IsTargetExtension(path)) {
260         return E_SUCCESS;
261     }
262 
263     if (mode.find('w') != string::npos) {
264         return E_SUCCESS;
265     }
266 
267     for (auto &item : PRIVACY_PERMISSION_MAP) {
268         const string &perm = item.second;
269         bool result = PermissionUtils::CheckCallerPermission(perm);
270         if ((result == false) && (perm == PERMISSION_NAME_MEDIA_LOCATION) && IsWriteMode(mode)) {
271             MEDIA_ERR_LOG("Write is not allowed if have no location permission");
272             return E_PERMISSION_DENIED;
273         }
274         if (result) {
275             continue;
276         }
277         int32_t err = CollectRanges(path, item.first, ranges);
278         if (err < 0) {
279             return err;
280         }
281     }
282     return SortRangesAndCheck(ranges);
283 }
284 
Open()285 int32_t MediaPrivacyManager::Open()
286 {
287     int err = GetPrivacyRanges(path_, mode_, ranges_);
288     if (err < 0) {
289         return err;
290     }
291     if (ranges_.size() > 0) {
292         return OpenFilterProxyFd(path_, mode_, ranges_);
293     }
294     return OpenOriginFd(path_, mode_);
295 }
296 } // namespace Media
297 } // namespace OHOS
298