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 namespace details {
32 
33 class CStrWrapper {
34 public:
CStrWrapper(std::string_view sv)35     CStrWrapper(std::string_view sv) {
36         if (!sv.data()) {
37             mCstr = "";
38         } else 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 
isIncFsFd(int fd)73 inline bool isIncFsFd(int fd) {
74     return IncFs_IsIncFsFd(fd);
75 }
76 
isIncFsPath(std::string_view path)77 inline bool isIncFsPath(std::string_view path) {
78     return IncFs_IsIncFsPath(details::c_str(path));
79 }
80 
isValidFileId(FileId fileId)81 inline bool isValidFileId(FileId fileId) {
82     return IncFs_IsValidFileId(fileId);
83 }
84 
toString(FileId fileId)85 inline std::string toString(FileId fileId) {
86     std::string res(kIncFsFileIdStringLength, '\0');
87     auto err = IncFs_FileIdToString(fileId, res.data());
88     if (err) {
89         errno = err;
90         return {};
91     }
92     return res;
93 }
94 
toFileId(std::string_view str)95 inline IncFsFileId toFileId(std::string_view str) {
96     if (str.size() != kIncFsFileIdStringLength) {
97         return kIncFsInvalidFileId;
98     }
99     return IncFs_FileIdFromString(str.data());
100 }
101 
close()102 inline void UniqueControl::close() {
103     IncFs_DeleteControl(mControl);
104     mControl = nullptr;
105 }
106 
cmd()107 inline IncFsFd UniqueControl::cmd() const {
108     return IncFs_GetControlFd(mControl, CMD);
109 }
110 
pendingReads()111 inline IncFsFd UniqueControl::pendingReads() const {
112     return IncFs_GetControlFd(mControl, PENDING_READS);
113 }
114 
logs()115 inline IncFsFd UniqueControl::logs() const {
116     return IncFs_GetControlFd(mControl, LOGS);
117 }
118 
blocksWritten()119 inline IncFsFd UniqueControl::blocksWritten() const {
120     return IncFs_GetControlFd(mControl, BLOCKS_WRITTEN);
121 }
122 
releaseFds()123 inline UniqueControl::Fds UniqueControl::releaseFds() {
124     Fds result;
125     IncFsFd fds[result.size()];
126     auto count = IncFs_ReleaseControlFds(mControl, fds, std::size(fds));
127     for (auto i = 0; i < count; ++i) {
128         result[i] = UniqueFd(fds[i]);
129     }
130     return result;
131 }
132 
mount(std::string_view backingPath,std::string_view targetDir,MountOptions options)133 inline UniqueControl mount(std::string_view backingPath, std::string_view targetDir,
134                            MountOptions options) {
135     auto control = IncFs_Mount(details::c_str(backingPath), details::c_str(targetDir), options);
136     return UniqueControl(control);
137 }
138 
open(std::string_view dir)139 inline UniqueControl open(std::string_view dir) {
140     auto control = IncFs_Open(details::c_str(dir));
141     return UniqueControl(control);
142 }
143 
createControl(IncFsFd cmd,IncFsFd pendingReads,IncFsFd logs,IncFsFd blocksWritten)144 inline UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
145                                    IncFsFd blocksWritten) {
146     return UniqueControl(IncFs_CreateControl(cmd, pendingReads, logs, blocksWritten));
147 }
148 
setOptions(const Control & control,MountOptions newOptions)149 inline ErrorCode setOptions(const Control& control, MountOptions newOptions) {
150     return IncFs_SetOptions(control, newOptions);
151 }
152 
bindMount(std::string_view sourceDir,std::string_view targetDir)153 inline ErrorCode bindMount(std::string_view sourceDir, std::string_view targetDir) {
154     return IncFs_BindMount(details::c_str(sourceDir), details::c_str(targetDir));
155 }
156 
unmount(std::string_view dir)157 inline ErrorCode unmount(std::string_view dir) {
158     return IncFs_Unmount(details::c_str(dir));
159 }
160 
root(const Control & control)161 inline std::string root(const Control& control) {
162     std::string result;
163     result.resize(PATH_MAX);
164     size_t size = result.size();
165     if (auto err = IncFs_Root(control, result.data(), &size); err < 0) {
166         errno = -err;
167         return {};
168     }
169     result.resize(size);
170     return result;
171 }
172 
makeFile(const Control & control,std::string_view path,int mode,FileId fileId,NewFileParams params)173 inline ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId fileId,
174                           NewFileParams params) {
175     return IncFs_MakeFile(control, details::c_str(path), mode, fileId, params);
176 }
makeMappedFile(const Control & control,std::string_view path,int mode,NewMappedFileParams params)177 inline ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
178                                 NewMappedFileParams params) {
179     return IncFs_MakeMappedFile(control, details::c_str(path), mode, params);
180 }
makeDir(const Control & control,std::string_view path,int mode)181 inline ErrorCode makeDir(const Control& control, std::string_view path, int mode) {
182     return IncFs_MakeDir(control, details::c_str(path), mode);
183 }
makeDirs(const Control & control,std::string_view path,int mode)184 inline ErrorCode makeDirs(const Control& control, std::string_view path, int mode) {
185     return IncFs_MakeDirs(control, details::c_str(path), mode);
186 }
187 
getMetadata(const Control & control,FileId fileId)188 inline RawMetadata getMetadata(const Control& control, FileId fileId) {
189     RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE);
190     size_t size = metadata.size();
191     if (IncFs_GetMetadataById(control, fileId, metadata.data(), &size) < 0) {
192         return {};
193     }
194     metadata.resize(size);
195     return metadata;
196 }
197 
getMetadata(const Control & control,std::string_view path)198 inline RawMetadata getMetadata(const Control& control, std::string_view path) {
199     RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE);
200     size_t size = metadata.size();
201     if (IncFs_GetMetadataByPath(control, details::c_str(path), metadata.data(), &size) < 0) {
202         return {};
203     }
204     metadata.resize(size);
205     return metadata;
206 }
207 
getSignature(const Control & control,FileId fileId)208 inline RawSignature getSignature(const Control& control, FileId fileId) {
209     RawSignature signature(INCFS_MAX_SIGNATURE_SIZE);
210     size_t size = signature.size();
211     if (IncFs_GetSignatureById(control, fileId, signature.data(), &size) < 0) {
212         return {};
213     }
214     signature.resize(size);
215     return signature;
216 }
217 
getSignature(const Control & control,std::string_view path)218 inline RawSignature getSignature(const Control& control, std::string_view path) {
219     RawSignature signature(INCFS_MAX_SIGNATURE_SIZE);
220     size_t size = signature.size();
221     if (IncFs_GetSignatureByPath(control, details::c_str(path), signature.data(), &size) < 0) {
222         return {};
223     }
224     signature.resize(size);
225     return signature;
226 }
227 
getFileId(const Control & control,std::string_view path)228 inline FileId getFileId(const Control& control, std::string_view path) {
229     return IncFs_GetId(control, details::c_str(path));
230 }
231 
link(const Control & control,std::string_view sourcePath,std::string_view targetPath)232 inline ErrorCode link(const Control& control, std::string_view sourcePath,
233                       std::string_view targetPath) {
234     return IncFs_Link(control, details::c_str(sourcePath), details::c_str(targetPath));
235 }
236 
unlink(const Control & control,std::string_view path)237 inline ErrorCode unlink(const Control& control, std::string_view path) {
238     return IncFs_Unlink(control, details::c_str(path));
239 }
240 
241 template <class ReadInfoStruct, class Impl>
waitForReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfoStruct> * pendingReadsBuffer,size_t defaultBufferSize,Impl impl)242 WaitResult waitForReads(const Control& control, std::chrono::milliseconds timeout,
243                         std::vector<ReadInfoStruct>* pendingReadsBuffer, size_t defaultBufferSize,
244                         Impl impl) {
245     if (pendingReadsBuffer->empty()) {
246         pendingReadsBuffer->resize(defaultBufferSize);
247     }
248     size_t size = pendingReadsBuffer->size();
249     IncFsErrorCode err = impl(control, timeout.count(), pendingReadsBuffer->data(), &size);
250     pendingReadsBuffer->resize(size);
251     switch (err) {
252         case 0:
253             return WaitResult::HaveData;
254         case -ETIMEDOUT:
255             return WaitResult::Timeout;
256     }
257     return WaitResult(err);
258 }
259 
waitForPendingReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfo> * pendingReadsBuffer)260 inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
261                                       std::vector<ReadInfo>* pendingReadsBuffer) {
262     return waitForReads(control, timeout, pendingReadsBuffer,
263                         INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReads);
264 }
265 
waitForPendingReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfoWithUid> * pendingReadsBuffer)266 inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
267                                       std::vector<ReadInfoWithUid>* pendingReadsBuffer) {
268     return waitForReads(control, timeout, pendingReadsBuffer,
269                         INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReadsWithUid);
270 }
271 
waitForPageReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfo> * pageReadsBuffer)272 inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
273                                    std::vector<ReadInfo>* pageReadsBuffer) {
274     static constexpr auto kDefaultBufferSize =
275             INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfo);
276     return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize,
277                         IncFs_WaitForPageReads);
278 }
279 
waitForPageReads(const Control & control,std::chrono::milliseconds timeout,std::vector<ReadInfoWithUid> * pageReadsBuffer)280 inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout,
281                                    std::vector<ReadInfoWithUid>* pageReadsBuffer) {
282     static constexpr auto kDefaultBufferSize =
283             INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfoWithUid);
284     return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize,
285                         IncFs_WaitForPageReadsWithUid);
286 }
287 
openForSpecialOps(const Control & control,FileId fileId)288 inline UniqueFd openForSpecialOps(const Control& control, FileId fileId) {
289     return UniqueFd(IncFs_OpenForSpecialOpsById(control, fileId));
290 }
openForSpecialOps(const Control & control,std::string_view path)291 inline UniqueFd openForSpecialOps(const Control& control, std::string_view path) {
292     return UniqueFd(IncFs_OpenForSpecialOpsByPath(control, details::c_str(path)));
293 }
294 
writeBlocks(Span<const DataBlock> blocks)295 inline ErrorCode writeBlocks(Span<const DataBlock> blocks) {
296     return IncFs_WriteBlocks(blocks.data(), blocks.size());
297 }
298 
getFilledRanges(int fd)299 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd) {
300     return getFilledRanges(fd, FilledRanges());
301 }
302 
getFilledRanges(int fd,FilledRanges::RangeBuffer && buffer)303 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd,
304                                                           FilledRanges::RangeBuffer&& buffer) {
305     return getFilledRanges(fd, FilledRanges(std::move(buffer), {}));
306 }
307 
getFilledRanges(int fd,FilledRanges && resumeFrom)308 inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd, FilledRanges&& resumeFrom) {
309     auto rawRanges = resumeFrom.internalRawRanges();
310     auto buffer = resumeFrom.extractInternalBufferAndClear();
311     auto totalRanges = resumeFrom.dataRanges().size() + resumeFrom.hashRanges().size();
312     auto remainingSpace = buffer.size() - totalRanges;
313     const bool loadAll = remainingSpace == 0;
314     int res;
315     do {
316         if (remainingSpace == 0) {
317             remainingSpace = std::max<size_t>(32, buffer.size() / 2);
318             buffer.resize(buffer.size() + remainingSpace);
319         }
320         auto outBuffer = IncFsSpan{(const char*)(buffer.data() + rawRanges.dataRangesCount +
321                                                  rawRanges.hashRangesCount),
322                                    IncFsSize(remainingSpace * sizeof(buffer[0]))};
323         IncFsFilledRanges newRanges;
324         res = IncFs_GetFilledRangesStartingFrom(fd, rawRanges.endIndex, outBuffer, &newRanges);
325         if (res && res != -ERANGE) {
326             return {res, FilledRanges(std::move(buffer), {})};
327         }
328 
329         rawRanges.dataRangesCount += newRanges.dataRangesCount;
330         rawRanges.hashRangesCount += newRanges.hashRangesCount;
331         rawRanges.endIndex = newRanges.endIndex;
332         remainingSpace = buffer.size() - rawRanges.dataRangesCount - rawRanges.hashRangesCount;
333     } while (res && loadAll);
334 
335     rawRanges.dataRanges = buffer.data();
336     rawRanges.hashRanges = buffer.data() + rawRanges.dataRangesCount;
337     return {res, FilledRanges(std::move(buffer), rawRanges)};
338 }
339 
toLoadingState(IncFsErrorCode res)340 inline LoadingState toLoadingState(IncFsErrorCode res) {
341     switch (res) {
342         case 0:
343             return LoadingState::Full;
344         case -ENODATA:
345             return LoadingState::MissingBlocks;
346         default:
347             return LoadingState(res);
348     }
349 }
350 
isFullyLoaded(int fd)351 inline LoadingState isFullyLoaded(int fd) {
352     return toLoadingState(IncFs_IsFullyLoaded(fd));
353 }
isFullyLoaded(const Control & control,std::string_view path)354 inline LoadingState isFullyLoaded(const Control& control, std::string_view path) {
355     return toLoadingState(IncFs_IsFullyLoadedByPath(control, details::c_str(path)));
356 }
isFullyLoaded(const Control & control,FileId fileId)357 inline LoadingState isFullyLoaded(const Control& control, FileId fileId) {
358     return toLoadingState(IncFs_IsFullyLoadedById(control, fileId));
359 }
360 
isEverythingFullyLoaded(const Control & control)361 inline LoadingState isEverythingFullyLoaded(const Control& control) {
362     return toLoadingState(IncFs_IsEverythingFullyLoaded(control));
363 }
364 
listIncompleteFiles(const Control & control)365 inline std::optional<std::vector<FileId>> listIncompleteFiles(const Control& control) {
366     std::vector<FileId> ids(32);
367     size_t count = ids.size();
368     auto err = IncFs_ListIncompleteFiles(control, ids.data(), &count);
369     if (err == -E2BIG) {
370         ids.resize(count);
371         err = IncFs_ListIncompleteFiles(control, ids.data(), &count);
372     }
373     if (err) {
374         errno = -err;
375         return {};
376     }
377     ids.resize(count);
378     return std::move(ids);
379 }
380 
381 template <class Callback>
forEachFile(const Control & control,Callback && cb)382 inline ErrorCode forEachFile(const Control& control, Callback&& cb) {
383     struct Context {
384         const Control& c;
385         const Callback& cb;
386     } context = {control, cb};
387     return IncFs_ForEachFile(control, &context, [](void* pcontext, const IncFsControl*, FileId id) {
388         const auto context = (Context*)pcontext;
389         return context->cb(context->c, id);
390     });
391 }
392 template <class Callback>
forEachIncompleteFile(const Control & control,Callback && cb)393 inline ErrorCode forEachIncompleteFile(const Control& control, Callback&& cb) {
394     struct Context {
395         const Control& c;
396         const Callback& cb;
397     } context = {control, cb};
398     return IncFs_ForEachIncompleteFile(control, &context,
399                                        [](void* pcontext, const IncFsControl*, FileId id) {
400                                            const auto context = (Context*)pcontext;
401                                            return context->cb(context->c, id);
402                                        });
403 }
404 
waitForLoadingComplete(const Control & control,std::chrono::milliseconds timeout)405 inline WaitResult waitForLoadingComplete(const Control& control,
406                                          std::chrono::milliseconds timeout) {
407     const auto res = IncFs_WaitForLoadingComplete(control, timeout.count());
408     switch (res) {
409         case 0:
410             return WaitResult::HaveData;
411         case -ETIMEDOUT:
412             return WaitResult::Timeout;
413         default:
414             return WaitResult(res);
415     }
416 }
417 
getBlockCount(const Control & control,FileId fileId)418 inline std::optional<BlockCounts> getBlockCount(const Control& control, FileId fileId) {
419     BlockCounts counts;
420     auto res = IncFs_GetFileBlockCountById(control, fileId, &counts);
421     if (res) {
422         errno = -res;
423         return {};
424     }
425     return counts;
426 }
427 
getBlockCount(const Control & control,std::string_view path)428 inline std::optional<BlockCounts> getBlockCount(const Control& control, std::string_view path) {
429     BlockCounts counts;
430     auto res = IncFs_GetFileBlockCountByPath(control, details::c_str(path), &counts);
431     if (res) {
432         errno = -res;
433         return {};
434     }
435     return counts;
436 }
437 
setUidReadTimeouts(const Control & control,Span<const UidReadTimeouts> timeouts)438 inline ErrorCode setUidReadTimeouts(const Control& control, Span<const UidReadTimeouts> timeouts) {
439     return IncFs_SetUidReadTimeouts(control, timeouts.data(), timeouts.size());
440 }
441 
getUidReadTimeouts(const Control & control)442 inline std::optional<std::vector<UidReadTimeouts>> getUidReadTimeouts(const Control& control) {
443     std::vector<UidReadTimeouts> timeouts(32);
444     size_t count = timeouts.size();
445     auto res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count);
446     if (res == -E2BIG) {
447         timeouts.resize(count);
448         res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count);
449     }
450     if (res) {
451         errno = -res;
452         return {};
453     }
454     timeouts.resize(count);
455     return std::move(timeouts);
456 }
457 
reserveSpace(const Control & control,std::string_view path,Size size)458 inline ErrorCode reserveSpace(const Control& control, std::string_view path, Size size) {
459     return IncFs_ReserveSpaceByPath(control, details::c_str(path), size);
460 }
reserveSpace(const Control & control,FileId id,Size size)461 inline ErrorCode reserveSpace(const Control& control, FileId id, Size size) {
462     return IncFs_ReserveSpaceById(control, id, size);
463 }
464 
getMetrics(std::string_view sysfsName)465 inline std::optional<Metrics> getMetrics(std::string_view sysfsName) {
466     Metrics metrics;
467     if (const auto res = IncFs_GetMetrics(details::c_str(sysfsName), &metrics); res < 0) {
468         errno = -res;
469         return {};
470     }
471     return metrics;
472 }
473 
getLastReadError(const Control & control)474 inline std::optional<LastReadError> getLastReadError(const Control& control) {
475     LastReadError lastReadError;
476     if (const auto res = IncFs_GetLastReadError(control, &lastReadError); res < 0) {
477         errno = -res;
478         return {};
479     }
480     return lastReadError;
481 }
482 
483 } // namespace android::incfs
484 
485 inline bool operator==(const IncFsFileId& l, const IncFsFileId& r) {
486     return memcmp(&l, &r, sizeof(l)) == 0;
487 }
488