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