1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "include/libfuse_jni/RedactionInfo.h"
18 
19 using std::unique_ptr;
20 using std::vector;
21 
22 namespace mediaprovider {
23 namespace fuse {
24 
25 /**
26  * Merges any overlapping ranges into 1 range.
27  *
28  * Given ranges should be sorted, and they remain sorted.
29  */
mergeOverlappingRedactionRanges(vector<RedactionRange> & ranges)30 static void mergeOverlappingRedactionRanges(vector<RedactionRange>& ranges) {
31     int newRangesSize = ranges.size();
32     for (int i = 0; i < ranges.size() - 1; ++i) {
33         if (ranges[i].second >= ranges[i + 1].first) {
34             ranges[i + 1].first = ranges[i].first;
35             ranges[i + 1].second = std::max(ranges[i].second, ranges[i + 1].second);
36             // Invalidate the redundant range
37             ranges[i].first = LONG_MAX;
38             ranges[i].second = LONG_MAX;
39             newRangesSize--;
40         }
41     }
42     if (newRangesSize < ranges.size()) {
43         // Move invalid ranges to end of array
44         std::sort(ranges.begin(), ranges.end());
45         ranges.resize(newRangesSize);
46     }
47 }
48 
49 /**
50  * Determine whether the read request overlaps with the redaction ranges
51  * defined by the given RedactionInfo.
52  *
53  * This function assumes redaction_ranges_ within RedactionInfo is sorted.
54  */
hasOverlapWithReadRequest(size_t size,off64_t off) const55 bool RedactionInfo::hasOverlapWithReadRequest(size_t size, off64_t off) const {
56     if (!isRedactionNeeded() || off > redaction_ranges_.back().second ||
57         off + size < redaction_ranges_.front().first) {
58         return false;
59     }
60     return true;
61 }
62 
63 /**
64  * Sets the redaction ranges in RedactionInfo, sort the ranges and merge
65  * overlapping ranges.
66  */
processRedactionRanges(int redaction_ranges_num,const off64_t * redaction_ranges)67 void RedactionInfo::processRedactionRanges(int redaction_ranges_num,
68                                            const off64_t* redaction_ranges) {
69     redaction_ranges_.resize(redaction_ranges_num);
70     for (int i = 0; i < redaction_ranges_num; ++i) {
71         redaction_ranges_[i].first = static_cast<off64_t>(redaction_ranges[2 * i]);
72         redaction_ranges_[i].second = static_cast<off64_t>(redaction_ranges[2 * i + 1]);
73     }
74     std::sort(redaction_ranges_.begin(), redaction_ranges_.end());
75     mergeOverlappingRedactionRanges(redaction_ranges_);
76 }
77 
size() const78 int RedactionInfo::size() const {
79     return redaction_ranges_.size();
80 }
81 
isRedactionNeeded() const82 bool RedactionInfo::isRedactionNeeded() const {
83     return size() > 0;
84 }
85 
RedactionInfo(int redaction_ranges_num,const off64_t * redaction_ranges)86 RedactionInfo::RedactionInfo(int redaction_ranges_num, const off64_t* redaction_ranges) {
87     if (redaction_ranges == 0) return;
88     processRedactionRanges(redaction_ranges_num, redaction_ranges);
89 }
90 
getOverlappingRedactionRanges(size_t size,off64_t off) const91 unique_ptr<vector<RedactionRange>> RedactionInfo::getOverlappingRedactionRanges(size_t size,
92                                                                                 off64_t off) const {
93     if (hasOverlapWithReadRequest(size, off)) {
94         auto first_redaction = redaction_ranges_.end();
95         auto last_redaction = redaction_ranges_.end();
96         for (auto iter = redaction_ranges_.begin(); iter != redaction_ranges_.end(); ++iter) {
97             const RedactionRange& rr = *iter;
98             // Look for the first range that overlaps with the read request
99             if (first_redaction == redaction_ranges_.end() && off <= rr.second &&
100                 off + size >= rr.first) {
101                 first_redaction = iter;
102             } else if (first_redaction != redaction_ranges_.end() && off + size < rr.first) {
103                 // Once we're in the read request range, we start checking if
104                 // we're out of it so we can return the result to the caller
105                 break;
106             }
107             last_redaction = iter;
108         }
109         if (first_redaction != redaction_ranges_.end()) {
110             return std::make_unique<vector<RedactionRange>>(first_redaction, last_redaction + 1);
111         }
112     }
113     return std::make_unique<vector<RedactionRange>>();
114 }
115 }  // namespace fuse
116 }  // namespace mediaprovider
117