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 specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <errno.h>
19 
20 #include <optional>
21 #include <string>
22 
23 #include "incfs.h"
24 
25 namespace android::incfs {
26 
27 constexpr char kIdAttrName[] = INCFS_XATTR_ID_NAME;
28 constexpr char kSizeAttrName[] = INCFS_XATTR_SIZE_NAME;
29 constexpr char kMetadataAttrName[] = INCFS_XATTR_METADATA_NAME;
30 
31 constexpr char kIndexDir[] = ".index";
32 
33 namespace details {
34 
35 class CStrWrapper {
36 public:
CStrWrapper(std::string_view sv)37     CStrWrapper(std::string_view sv) {
38         if (sv[sv.size()] == '\0') {
39             mCstr = sv.data();
40         } else {
41             mCopy.emplace(sv);
42             mCstr = mCopy->c_str();
43         }
44     }
45 
46     CStrWrapper(const CStrWrapper&) = delete;
47     void operator=(const CStrWrapper&) = delete;
48     CStrWrapper(CStrWrapper&&) = delete;
49     void operator=(CStrWrapper&&) = delete;
50 
get()51     const char* get() const { return mCstr; }
52     operator const char*() const { return get(); }
53 
54 private:
55     const char* mCstr;
56     std::optional<std::string> mCopy;
57 };
58 
c_str(std::string_view sv)59 inline CStrWrapper c_str(std::string_view sv) {
60     return {sv};
61 }
62 
63 } // namespace details
64 
enabled()65 inline bool enabled() {
66     return IncFs_IsEnabled();
67 }
68 
features()69 inline Features features() {
70     return Features(IncFs_Features());
71 }
72 
isIncFsPath(std::string_view path)73 inline bool isIncFsPath(std::string_view path) {
74     return IncFs_IsIncFsPath(details::c_str(path));
75 }
76 
isValidFileId(FileId fileId)77 inline bool isValidFileId(FileId fileId) {
78     return IncFs_IsValidFileId(fileId);
79 }
80 
toString(FileId fileId)81 inline std::string toString(FileId fileId) {
82     std::string res(kIncFsFileIdStringLength, '\0');
83     auto err = IncFs_FileIdToString(fileId, res.data());
84     if (err) {
85         errno = err;
86         return {};
87     }
88     return res;
89 }
90 
toFileId(std::string_view str)91 inline IncFsFileId toFileId(std::string_view str) {
92     if (str.size() != kIncFsFileIdStringLength) {
93         return kIncFsInvalidFileId;
94     }
95     return IncFs_FileIdFromString(str.data());
96 }
97 
close()98 inline void UniqueControl::close() {
99     IncFs_DeleteControl(mControl);
100     mControl = nullptr;
101 }
102 
cmd()103 inline IncFsFd UniqueControl::cmd() const {
104     return IncFs_GetControlFd(mControl, CMD);
105 }
106 
pendingReads()107 inline IncFsFd UniqueControl::pendingReads() const {
108     return IncFs_GetControlFd(mControl, PENDING_READS);
109 }
110 
logs()111 inline IncFsFd UniqueControl::logs() const {
112     return IncFs_GetControlFd(mControl, LOGS);
113 }
114 
releaseFds()115 inline UniqueControl::Fds UniqueControl::releaseFds() {
116     Fds result;
117     IncFsFd fds[result.size()];
118     auto count = IncFs_ReleaseControlFds(mControl, fds, std::size(fds));
119     for (auto i = 0; i < count; ++i) {
120         result[i] = UniqueFd(fds[i]);
121     }
122     return result;
123 }
124 
mount(std::string_view backingPath,std::string_view targetDir,MountOptions options)125 inline UniqueControl mount(std::string_view backingPath, std::string_view targetDir,
126                            MountOptions options) {
127     auto control = IncFs_Mount(details::c_str(backingPath), details::c_str(targetDir), options);
128     return UniqueControl(control);
129 }
130 
open(std::string_view dir)131 inline UniqueControl open(std::string_view dir) {
132     auto control = IncFs_Open(details::c_str(dir));
133     return UniqueControl(control);
134 }
135 
createControl(IncFsFd cmd,IncFsFd pendingReads,IncFsFd logs)136 inline UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) {
137     return UniqueControl(IncFs_CreateControl(cmd, pendingReads, logs));
138 }
139 
setOptions(const Control & control,MountOptions newOptions)140 inline ErrorCode setOptions(const Control& control, MountOptions newOptions) {
141     return IncFs_SetOptions(control, newOptions);
142 }
143 
bindMount(std::string_view sourceDir,std::string_view targetDir)144 inline ErrorCode bindMount(std::string_view sourceDir, std::string_view targetDir) {
145     return IncFs_BindMount(details::c_str(sourceDir), details::c_str(targetDir));
146 }
147 
unmount(std::string_view dir)148 inline ErrorCode unmount(std::string_view dir) {
149     return IncFs_Unmount(details::c_str(dir));
150 }
151 
root(const Control & control)152 inline std::string root(const Control& control) {
153     std::string result;
154     result.resize(PATH_MAX);
155     size_t size = result.size();
156     if (auto err = IncFs_Root(control, result.data(), &size); err < 0) {
157         errno = -err;
158         return {};
159     }
160     result.resize(size);
161     return result;
162 }
163 
makeFile(const Control & control,std::string_view path,int mode,FileId fileId,NewFileParams params)164 inline ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId fileId,
165                           NewFileParams params) {
166     return IncFs_MakeFile(control, details::c_str(path), mode, fileId, params);
167 }
makeDir(const Control & control,std::string_view path,int mode)168 inline ErrorCode makeDir(const Control& control, std::string_view path, int mode) {
169     return IncFs_MakeDir(control, details::c_str(path), mode);
170 }
makeDirs(const Control & control,std::string_view path,int mode)171 inline ErrorCode makeDirs(const Control& control, std::string_view path, int mode) {
172     return IncFs_MakeDirs(control, details::c_str(path), mode);
173 }
174 
getMetadata(const Control & control,FileId fileId)175 inline RawMetadata getMetadata(const Control& control, FileId fileId) {
176     RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE);
177     size_t size = metadata.size();
178     if (IncFs_GetMetadataById(control, fileId, metadata.data(), &size) < 0) {
179         return {};
180     }
181     metadata.resize(size);
182     return metadata;
183 }
184 
getMetadata(const Control & control,std::string_view path)185 inline RawMetadata getMetadata(const Control& control, std::string_view path) {
186     RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE);
187     size_t size = metadata.size();
188     if (IncFs_GetMetadataByPath(control, details::c_str(path), metadata.data(), &size) < 0) {
189         return {};
190     }
191     metadata.resize(size);
192     return metadata;
193 }
194 
getSignature(const Control & control,FileId fileId)195 inline RawSignature getSignature(const Control& control, FileId fileId) {
196     RawSignature signature(INCFS_MAX_SIGNATURE_SIZE);
197     size_t size = signature.size();
198     if (IncFs_GetSignatureById(control, fileId, signature.data(), &size) < 0) {
199         return {};
200     }
201     signature.resize(size);
202     return signature;
203 }
204 
getSignature(const Control & control,std::string_view path)205 inline RawSignature getSignature(const Control& control, std::string_view path) {
206     RawSignature signature(INCFS_MAX_SIGNATURE_SIZE);
207     size_t size = signature.size();
208     if (IncFs_GetSignatureByPath(control, details::c_str(path), signature.data(), &size) < 0) {
209         return {};
210     }
211     signature.resize(size);
212     return signature;
213 }
214 
getFileId(const Control & control,std::string_view path)215 inline FileId getFileId(const Control& control, std::string_view path) {
216     return IncFs_GetId(control, details::c_str(path));
217 }
218 
link(const Control & control,std::string_view sourcePath,std::string_view targetPath)219 inline ErrorCode link(const Control& control, std::string_view sourcePath,
220                       std::string_view targetPath) {
221     return IncFs_Link(control, details::c_str(sourcePath), details::c_str(targetPath));
222 }
223 
unlink(const Control & control,std::string_view path)224 inline ErrorCode unlink(const Control& control, std::string_view path) {
225     return IncFs_Unlink(control, details::c_str(path));
226 }
227 
waitForPendingReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfo> * pendingReadsBuffer)228 inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
229                                       std::vector<ReadInfo>* pendingReadsBuffer) {
230     static constexpr auto kDefaultBufferSize = INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE;
231     if (pendingReadsBuffer->empty()) {
232         pendingReadsBuffer->resize(kDefaultBufferSize);
233     }
234     size_t size = pendingReadsBuffer->size();
235     IncFsErrorCode err =
236             IncFs_WaitForPendingReads(control, timeout.count(), pendingReadsBuffer->data(), &size);
237     pendingReadsBuffer->resize(size);
238     switch (err) {
239         case 0:
240             return WaitResult::HaveData;
241         case -ETIMEDOUT:
242             return WaitResult::Timeout;
243     }
244     return WaitResult(err);
245 }
246 
waitForPageReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfo> * pageReadsBuffer)247 inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
248                                    std::vector<ReadInfo>* pageReadsBuffer) {
249     static constexpr auto kDefaultBufferSize =
250             INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfo);
251     if (pageReadsBuffer->empty()) {
252         pageReadsBuffer->resize(kDefaultBufferSize);
253     }
254     size_t size = pageReadsBuffer->size();
255     IncFsErrorCode err =
256             IncFs_WaitForPageReads(control, timeout.count(), pageReadsBuffer->data(), &size);
257     pageReadsBuffer->resize(size);
258     switch (err) {
259         case 0:
260             return WaitResult::HaveData;
261         case -ETIMEDOUT:
262             return WaitResult::Timeout;
263     }
264     return WaitResult(err);
265 }
266 
openForSpecialOps(const Control & control,FileId fileId)267 inline UniqueFd openForSpecialOps(const Control& control, FileId fileId) {
268     return UniqueFd(IncFs_OpenForSpecialOpsById(control, fileId));
269 }
openForSpecialOps(const Control & control,std::string_view path)270 inline UniqueFd openForSpecialOps(const Control& control, std::string_view path) {
271     return UniqueFd(IncFs_OpenForSpecialOpsByPath(control, details::c_str(path)));
272 }
273 
writeBlocks(Span<const DataBlock> blocks)274 inline ErrorCode writeBlocks(Span<const DataBlock> blocks) {
275     return IncFs_WriteBlocks(blocks.data(), blocks.size());
276 }
277 
getFilledRanges(int fd)278 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd) {
279     return getFilledRanges(fd, FilledRanges());
280 }
281 
getFilledRanges(int fd,FilledRanges::RangeBuffer && buffer)282 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd,
283                                                           FilledRanges::RangeBuffer&& buffer) {
284     return getFilledRanges(fd, FilledRanges(std::move(buffer), {}));
285 }
286 
getFilledRanges(int fd,FilledRanges && resumeFrom)287 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd, FilledRanges&& resumeFrom) {
288     auto rawRanges = resumeFrom.internalRawRanges();
289     auto buffer = resumeFrom.extractInternalBufferAndClear();
290     auto totalRanges = resumeFrom.dataRanges().size() + resumeFrom.hashRanges().size();
291     auto remainingSpace = buffer.size() - totalRanges;
292     const bool loadAll = remainingSpace == 0;
293     int res;
294     do {
295         if (remainingSpace == 0) {
296             remainingSpace = std::max<size_t>(32, buffer.size() / 2);
297             buffer.resize(buffer.size() + remainingSpace);
298         }
299         auto outBuffer = IncFsSpan{(const char*)(buffer.data() + rawRanges.dataRangesCount +
300                                                  rawRanges.hashRangesCount),
301                                    IncFsSize(remainingSpace * sizeof(buffer[0]))};
302         IncFsFilledRanges newRanges;
303         res = IncFs_GetFilledRangesStartingFrom(fd, rawRanges.endIndex, outBuffer, &newRanges);
304         if (res && res != -ERANGE) {
305             return {res, FilledRanges(std::move(buffer), {})};
306         }
307 
308         rawRanges.dataRangesCount += newRanges.dataRangesCount;
309         rawRanges.hashRangesCount += newRanges.hashRangesCount;
310         rawRanges.endIndex = newRanges.endIndex;
311         remainingSpace = buffer.size() - rawRanges.dataRangesCount - rawRanges.hashRangesCount;
312     } while (res && loadAll);
313 
314     rawRanges.dataRanges = buffer.data();
315     rawRanges.hashRanges = buffer.data() + rawRanges.dataRangesCount;
316     return {res, FilledRanges(std::move(buffer), rawRanges)};
317 }
318 
isFullyLoaded(int fd)319 inline LoadingState isFullyLoaded(int fd) {
320     auto res = IncFs_IsFullyLoaded(fd);
321     switch (res) {
322         case 0:
323             return LoadingState::Full;
324         case -ENODATA:
325             return LoadingState::MissingBlocks;
326         default:
327             return LoadingState(res);
328     }
329 }
330 
331 } // namespace android::incfs
332 
333 inline bool operator==(const IncFsFileId& l, const IncFsFileId& r) {
334     return memcmp(&l, &r, sizeof(l)) == 0;
335 }
336