blob: db654134d24f34140f814e4e3e9d75bc516fa663 [file] [log] [blame]
Songchun Fan59eee562019-10-24 16:46:06 -07001/*
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 */
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070016
17#define LOG_TAG "incfs"
18
Songchun Fan59eee562019-10-24 16:46:06 -070019#include "incfs.h"
20
Songchun Fan66fff272020-05-08 14:19:38 -070021#include <IncrementalProperties.sysprop.h>
Songchun Fan59eee562019-10-24 16:46:06 -070022#include <android-base/file.h>
23#include <android-base/logging.h>
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070024#include <android-base/no_destructor.h>
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080025#include <android-base/parsebool.h>
Songchun Fan59eee562019-10-24 16:46:06 -070026#include <android-base/stringprintf.h>
27#include <android-base/strings.h>
28#include <android-base/unique_fd.h>
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080029#include <dirent.h>
Songchun Fan59eee562019-10-24 16:46:06 -070030#include <errno.h>
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070031#include <fcntl.h>
Songchun Fan59eee562019-10-24 16:46:06 -070032#include <libgen.h>
Alex Buynytskyyebe71e52020-02-10 07:41:22 -080033#include <openssl/sha.h>
Songchun Fan0fdee212020-02-12 17:16:03 -080034#include <selinux/android.h>
35#include <selinux/selinux.h>
Songchun Fan59eee562019-10-24 16:46:06 -070036#include <sys/mount.h>
37#include <sys/poll.h>
38#include <sys/stat.h>
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080039#include <sys/syscall.h>
40#include <sys/types.h>
Songchun Fan59eee562019-10-24 16:46:06 -070041#include <sys/vfs.h>
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080042#include <sys/xattr.h>
Songchun Fan59eee562019-10-24 16:46:06 -070043#include <unistd.h>
44
45#include <chrono>
46#include <fstream>
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080047#include <iterator>
48#include <mutex>
Songchun Fan59eee562019-10-24 16:46:06 -070049#include <optional>
50#include <string_view>
51
52#include "MountRegistry.h"
53#include "path.h"
54
Songchun Fan59eee562019-10-24 16:46:06 -070055using namespace std::literals;
Luca Stefanie91f4fb2020-07-15 18:41:01 +020056
57using android::base::StringPrintf;
58using android::base::unique_fd;
Songchun Fan59eee562019-10-24 16:46:06 -070059
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070060struct IncFsControl final {
Songchun Fana3f30572020-03-02 15:36:33 -080061 IncFsFd cmd;
62 IncFsFd pendingReads;
63 IncFsFd logs;
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070064 constexpr IncFsControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs)
Songchun Fana3f30572020-03-02 15:36:33 -080065 : cmd(cmd), pendingReads(pendingReads), logs(logs) {}
66};
67
Luca Stefani742dfca2020-07-15 18:32:32 +020068static android::incfs::MountRegistry& registry() {
Luca Stefanie91f4fb2020-07-15 18:41:01 +020069 static android::base::NoDestructor<android::incfs::MountRegistry> instance{};
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -070070 return *instance;
Songchun Fan59eee562019-10-24 16:46:06 -070071}
72
Luca Stefanie91f4fb2020-07-15 18:41:01 +020073static unique_fd openRaw(std::string_view file) {
74 auto fd = unique_fd(::open(android::incfs::details::c_str(file), O_RDONLY | O_CLOEXEC));
Songchun Fan59eee562019-10-24 16:46:06 -070075 if (fd < 0) {
Luca Stefanie91f4fb2020-07-15 18:41:01 +020076 return unique_fd{-errno};
Songchun Fan59eee562019-10-24 16:46:06 -070077 }
78 return fd;
79}
80
Luca Stefanie91f4fb2020-07-15 18:41:01 +020081static unique_fd openRaw(std::string_view dir, std::string_view name) {
Luca Stefani742dfca2020-07-15 18:32:32 +020082 return openRaw(android::incfs::path::join(dir, name));
Songchun Fan59eee562019-10-24 16:46:06 -070083}
84
Alex Buynytskyyd228b4e2020-03-31 11:58:34 -070085static std::string rootForCmd(int fd) {
Luca Stefani742dfca2020-07-15 18:32:32 +020086 auto cmdFile = android::incfs::path::fromFd(fd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080087 if (cmdFile.empty()) {
88 LOG(INFO) << __func__ << "(): name empty for " << fd;
Songchun Fan59eee562019-10-24 16:46:06 -070089 return {};
90 }
Luca Stefani742dfca2020-07-15 18:32:32 +020091 auto res = android::incfs::path::dirName(cmdFile);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -080092 if (res.empty()) {
93 LOG(INFO) << __func__ << "(): dirname empty for " << cmdFile;
Songchun Fan59eee562019-10-24 16:46:06 -070094 return {};
95 }
Luca Stefani742dfca2020-07-15 18:32:32 +020096 if (!android::incfs::path::endsWith(cmdFile, INCFS_PENDING_READS_FILENAME)) {
Alex Buynytskyyd228b4e2020-03-31 11:58:34 -070097 LOG(INFO) << __func__ << "(): invalid file name " << cmdFile;
98 return {};
99 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800100 if (cmdFile.data() == res.data() || cmdFile.starts_with(res)) {
101 cmdFile.resize(res.size());
102 return cmdFile;
103 }
104 return std::string(res);
Songchun Fan59eee562019-10-24 16:46:06 -0700105}
106
Luca Stefani742dfca2020-07-15 18:32:32 +0200107static android::incfs::Features readIncFsFeatures() {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800108 static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
Luca Stefani742dfca2020-07-15 18:32:32 +0200109 const auto dir = android::incfs::path::openDir(kSysfsFeaturesDir);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800110 if (!dir) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200111 return android::incfs::Features::none;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800112 }
113
Luca Stefani742dfca2020-07-15 18:32:32 +0200114 int res = android::incfs::Features::none;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800115 while (auto entry = ::readdir(dir.get())) {
116 if (entry->d_type != DT_REG) {
117 continue;
118 }
119 if (entry->d_name == "corefs"sv) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200120 res |= android::incfs::Features::core;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800121 }
122 }
123
Luca Stefani742dfca2020-07-15 18:32:32 +0200124 return android::incfs::Features(res);
Songchun Fan59eee562019-10-24 16:46:06 -0700125}
126
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800127IncFsFeatures IncFs_Features() {
128 return IncFsFeatures(readIncFsFeatures());
129}
130
131static bool isFsAvailable() {
132 static const char kProcFilesystems[] = "/proc/filesystems";
Songchun Fan59eee562019-10-24 16:46:06 -0700133 std::string filesystems;
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200134 if (!android::base::ReadFileToString(kProcFilesystems, &filesystems)) {
Songchun Fan59eee562019-10-24 16:46:06 -0700135 return false;
136 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800137 return filesystems.find("\t" INCFS_NAME "\n") != std::string::npos;
Songchun Fan59eee562019-10-24 16:46:06 -0700138}
139
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700140static std::string_view incFsPropertyValue() {
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200141 static const android::base::NoDestructor<std::string> kValue{
Luca Stefani9dcdc372020-07-15 18:10:41 +0200142 android::sysprop::IncrementalProperties::enable().value_or("")};
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700143 return *kValue;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800144}
145
146static std::pair<bool, std::string_view> parseProperty(std::string_view property) {
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200147 auto boolVal = android::base::ParseBool(property);
148 if (boolVal == android::base::ParseBoolResult::kTrue) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800149 return {isFsAvailable(), {}};
Songchun Fan59eee562019-10-24 16:46:06 -0700150 }
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200151 if (boolVal == android::base::ParseBoolResult::kFalse) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800152 return {false, {}};
153 }
154
155 // Don't load the module at once, but instead only check if it is loadable.
156 static const auto kModulePrefix = "module:"sv;
157 if (property.starts_with(kModulePrefix)) {
158 const auto modulePath = property.substr(kModulePrefix.size());
Luca Stefani742dfca2020-07-15 18:32:32 +0200159 return {::access(android::incfs::details::c_str(modulePath), R_OK | X_OK), modulePath};
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800160 }
161 return {false, {}};
162}
163
164namespace {
165
166class IncFsInit {
167public:
168 IncFsInit() {
169 auto [featureEnabled, moduleName] = parseProperty(incFsPropertyValue());
170 featureEnabled_ = featureEnabled;
171 moduleName_ = moduleName;
172 loaded_ = featureEnabled_ && isFsAvailable();
173 }
174
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700175 constexpr ~IncFsInit() = default;
176
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800177 bool enabled() const { return featureEnabled_; }
178 bool enabledAndReady() const {
179 if (!featureEnabled_) {
180 return false;
181 }
182 if (moduleName_.empty()) {
183 return true;
184 }
185 if (loaded_) {
186 return true;
187 }
188 std::call_once(loadedFlag_, [this] {
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200189 const unique_fd fd(
Luca Stefani742dfca2020-07-15 18:32:32 +0200190 TEMP_FAILURE_RETRY(::open(android::incfs::details::c_str(moduleName_),
191 O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800192 if (fd < 0) {
193 PLOG(ERROR) << "could not open IncFs kernel module \"" << moduleName_ << '"';
194 return;
195 }
196
197 const auto rc = syscall(__NR_finit_module, fd.get(), "", 0);
198 if (rc < 0) {
199 PLOG(ERROR) << "finit_module for IncFs \"" << moduleName_ << "\" failed";
200 return;
201 }
202 if (!isFsAvailable()) {
203 LOG(ERROR) << "loaded IncFs kernel module \"" << moduleName_
204 << "\" but incremental-fs is still not available";
205 }
206 loaded_ = true;
207 LOG(INFO) << "successfully loaded IncFs kernel module \"" << moduleName_ << '"';
208 });
209 return loaded_;
210 }
211
212private:
213 bool featureEnabled_;
214 std::string_view moduleName_;
215 mutable std::once_flag loadedFlag_;
216 mutable bool loaded_;
217};
218
219} // namespace
220
221static IncFsInit& init() {
222 static IncFsInit initer;
223 return initer;
224}
225
226bool IncFs_IsEnabled() {
227 return init().enabled();
Songchun Fan59eee562019-10-24 16:46:06 -0700228}
229
230bool isIncFsPath(const char* path) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800231 struct statfs fs = {};
232 if (::statfs(path, &fs) != 0) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700233 PLOG(WARNING) << __func__ << "(): could not statfs " << path;
Songchun Fan59eee562019-10-24 16:46:06 -0700234 return false;
235 }
236
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800237 return fs.f_type == (decltype(fs.f_type))INCFS_MAGIC_NUMBER;
Songchun Fan59eee562019-10-24 16:46:06 -0700238}
239
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800240static int isDir(const char* path) {
241 struct stat st;
242 if (::stat(path, &st) != 0) {
243 return -errno;
Songchun Fan59eee562019-10-24 16:46:06 -0700244 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800245 if (!S_ISDIR(st.st_mode)) {
246 return -ENOTDIR;
247 }
248 return 0;
249}
Songchun Fan59eee562019-10-24 16:46:06 -0700250
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800251static bool isAbsolute(const char* path) {
252 return path && path[0] == '/';
253}
254
255static int isValidMountTarget(const char* path) {
256 if (!isAbsolute(path)) {
257 return -EINVAL;
258 }
259 if (isIncFsPath(path)) {
Songchun Fan59eee562019-10-24 16:46:06 -0700260 LOG(ERROR) << "[incfs] mounting over existing incfs mount is not allowed";
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800261 return -EINVAL;
262 }
263 if (const auto err = isDir(path); err != 0) {
264 return err;
265 }
Luca Stefani742dfca2020-07-15 18:32:32 +0200266 if (const auto err = android::incfs::path::isEmptyDir(path); err != 0) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800267 return err;
268 }
269 return 0;
270}
271
272static int rmDirContent(const char* path) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200273 auto dir = android::incfs::path::openDir(path);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800274 if (!dir) {
275 return -EINVAL;
276 }
277 while (auto entry = ::readdir(dir.get())) {
278 if (entry->d_name == "."sv || entry->d_name == ".."sv) {
279 continue;
280 }
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200281 auto fullPath = StringPrintf("%s/%s", path, entry->d_name);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800282 if (entry->d_type == DT_DIR) {
283 if (const auto err = rmDirContent(fullPath.c_str()); err != 0) {
284 return err;
285 }
286 if (const auto err = ::rmdir(fullPath.c_str()); err != 0) {
287 return err;
288 }
289 } else {
290 if (const auto err = ::unlink(fullPath.c_str()); err != 0) {
291 return err;
292 }
293 }
294 }
295 return 0;
296}
297
298static std::string makeMountOptionsString(IncFsMountOptions options) {
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200299 return StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1",
300 unsigned(options.defaultReadTimeoutMs),
301 unsigned(options.readLogBufferPages < 0
302 ? INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES
303 : options.readLogBufferPages));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800304}
305
Songchun Fana3f30572020-03-02 15:36:33 -0800306static IncFsControl* makeControl(const char* root) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700307 auto cmd = openRaw(root, INCFS_PENDING_READS_FILENAME);
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -0700308 if (!cmd.ok()) {
Songchun Fana3f30572020-03-02 15:36:33 -0800309 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800310 }
Luca Stefanie91f4fb2020-07-15 18:41:01 +0200311 unique_fd pendingReads(fcntl(cmd.get(), F_DUPFD_CLOEXEC, cmd.get()));
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -0700312 if (!pendingReads.ok()) {
Songchun Fana3f30572020-03-02 15:36:33 -0800313 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800314 }
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700315 auto logs = openRaw(root, INCFS_LOG_FILENAME);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800316 // logs may be absent, that's fine
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -0700317 auto control = IncFs_CreateControl(cmd.get(), pendingReads.get(), logs.get());
318 if (control) {
319 (void)cmd.release();
320 (void)pendingReads.release();
321 (void)logs.release();
322 }
323 return control;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800324}
325
326static std::string makeCommandPath(std::string_view root, std::string_view item) {
327 auto [itemRoot, subpath] = registry().rootAndSubpathFor(item);
328 if (itemRoot != root) {
329 return {};
330 }
331 // TODO: add "/.cmd/" if we decide to use a separate control tree.
Luca Stefani742dfca2020-07-15 18:32:32 +0200332 return android::incfs::path::join(itemRoot, subpath);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800333}
334
335static void toString(IncFsFileId id, char* out) {
336 // Make sure this function matches the one in the kernel (e.g. same case for a-f digits).
337 static constexpr char kHexChar[] = "0123456789abcdef";
338
339 for (auto item = std::begin(id.data); item != std::end(id.data); ++item, out += 2) {
340 out[0] = kHexChar[(*item & 0xf0) >> 4];
341 out[1] = kHexChar[(*item & 0x0f)];
342 }
343}
344
345static std::string toStringImpl(IncFsFileId id) {
346 std::string res(kIncFsFileIdStringLength, '\0');
347 toString(id, res.data());
348 return res;
349}
350
351static IncFsFileId toFileIdImpl(std::string_view str) {
352 if (str.size() != kIncFsFileIdStringLength) {
353 return kIncFsInvalidFileId;
Songchun Fan59eee562019-10-24 16:46:06 -0700354 }
355
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800356 IncFsFileId res;
357 auto out = (char*)&res;
358 for (auto it = str.begin(); it != str.end(); it += 2, ++out) {
359 static const auto fromChar = [](char src) -> char {
360 if (src >= '0' && src <= '9') {
361 return src - '0';
362 }
363 if (src >= 'a' && src <= 'f') {
364 return src - 'a' + 10;
365 }
366 return -1;
367 };
368
369 const char c[2] = {fromChar(it[0]), fromChar(it[1])};
370 if (c[0] == -1 || c[1] == -1) {
371 errno = EINVAL;
372 return kIncFsInvalidFileId;
373 }
374 *out = (c[0] << 4) | c[1];
Songchun Fan59eee562019-10-24 16:46:06 -0700375 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800376 return res;
377}
378
379int IncFs_FileIdToString(IncFsFileId id, char* out) {
380 if (!out) {
381 return -EINVAL;
382 }
383 toString(id, out);
384 return 0;
385}
386
387IncFsFileId IncFs_FileIdFromString(const char* in) {
388 return toFileIdImpl({in, kIncFsFileIdStringLength});
389}
390
Alex Buynytskyyebe71e52020-02-10 07:41:22 -0800391IncFsFileId IncFs_FileIdFromMetadata(IncFsSpan metadata) {
392 IncFsFileId id = {};
393 if (size_t(metadata.size) <= sizeof(id)) {
394 memcpy(&id, metadata.data, metadata.size);
395 } else {
396 uint8_t buffer[SHA_DIGEST_LENGTH];
397 static_assert(sizeof(buffer) >= sizeof(id));
398
399 SHA_CTX ctx;
400 SHA1_Init(&ctx);
401 SHA1_Update(&ctx, metadata.data, metadata.size);
402 SHA1_Final(buffer, &ctx);
403 memcpy(&id, buffer, sizeof(id));
404 }
405 return id;
406}
407
Songchun Fan4a18a602020-07-13 15:43:58 -0700408static bool restoreconControlFiles(std::string_view targetDir) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200409 const std::string controlFilePaths[] =
410 {android::incfs::path::join(targetDir, INCFS_PENDING_READS_FILENAME),
411 android::incfs::path::join(targetDir, INCFS_LOG_FILENAME)};
Songchun Fan4a18a602020-07-13 15:43:58 -0700412 for (size_t i = 0; i < std::size(controlFilePaths); i++) {
413 if (const auto err = selinux_android_restorecon(controlFilePaths[i].c_str(),
414 SELINUX_ANDROID_RESTORECON_FORCE);
415 err != 0) {
416 PLOG(ERROR) << "[incfs] Failed to restorecon: " << controlFilePaths[i]
417 << " error code: " << err;
418 errno = -err;
419 return false;
420 }
421 }
422 return true;
423}
424
Songchun Fana3f30572020-03-02 15:36:33 -0800425IncFsControl* IncFs_Mount(const char* backingPath, const char* targetDir,
426 IncFsMountOptions options) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800427 if (!init().enabledAndReady()) {
428 LOG(WARNING) << "[incfs] Feature is not enabled";
Songchun Fana3f30572020-03-02 15:36:33 -0800429 errno = ENOTSUP;
430 return nullptr;
Songchun Fan59eee562019-10-24 16:46:06 -0700431 }
432
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800433 if (auto err = isValidMountTarget(targetDir); err != 0) {
Songchun Fana3f30572020-03-02 15:36:33 -0800434 errno = -err;
435 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800436 }
437 if (!isAbsolute(backingPath)) {
Songchun Fana3f30572020-03-02 15:36:33 -0800438 errno = EINVAL;
439 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800440 }
441
Luca Stefani742dfca2020-07-15 18:32:32 +0200442 if (options.flags & android::incfs::createOnly) {
443 if (const auto err = android::incfs::path::isEmptyDir(backingPath); err != 0) {
Songchun Fana3f30572020-03-02 15:36:33 -0800444 errno = -err;
445 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800446 }
447 } else if (options.flags & android::incfs::truncate) {
448 if (const auto err = rmDirContent(backingPath); err != 0) {
Songchun Fana3f30572020-03-02 15:36:33 -0800449 errno = -err;
450 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800451 }
452 }
453
454 const auto opts = makeMountOptionsString(options);
455 if (::mount(backingPath, targetDir, INCFS_NAME, MS_NOSUID | MS_NODEV | MS_NOATIME,
456 opts.c_str())) {
Songchun Fana3f30572020-03-02 15:36:33 -0800457 PLOG(ERROR) << "[incfs] Failed to mount IncFS filesystem: " << targetDir
458 << " errno: " << errno;
459 return nullptr;
Songchun Fan59eee562019-10-24 16:46:06 -0700460 }
461
Songchun Fan4a18a602020-07-13 15:43:58 -0700462 if (!restoreconControlFiles(targetDir)) {
Songchun Fana3f30572020-03-02 15:36:33 -0800463 return nullptr;
Songchun Fan0fdee212020-02-12 17:16:03 -0800464 }
465
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800466 auto control = makeControl(targetDir);
Songchun Fana3f30572020-03-02 15:36:33 -0800467 if (control == nullptr) {
468 return nullptr;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800469 }
Songchun Fana3f30572020-03-02 15:36:33 -0800470 return control;
Songchun Fan59eee562019-10-24 16:46:06 -0700471}
472
Songchun Fana3f30572020-03-02 15:36:33 -0800473IncFsControl* IncFs_Open(const char* dir) {
Songchun Fan59eee562019-10-24 16:46:06 -0700474 auto root = registry().rootFor(dir);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800475 if (root.empty()) {
Songchun Fana3f30572020-03-02 15:36:33 -0800476 errno = EINVAL;
477 return nullptr;
Songchun Fan59eee562019-10-24 16:46:06 -0700478 }
Luca Stefani742dfca2020-07-15 18:32:32 +0200479 return makeControl(android::incfs::details::c_str(root));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800480}
481
Songchun Fana3f30572020-03-02 15:36:33 -0800482IncFsFd IncFs_GetControlFd(const IncFsControl* control, IncFsFdType type) {
483 if (!control) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700484 return -EINVAL;
Songchun Fana3f30572020-03-02 15:36:33 -0800485 }
486 switch (type) {
487 case CMD:
488 return control->cmd;
489 case PENDING_READS:
490 return control->pendingReads;
491 case LOGS:
492 return control->logs;
493 default:
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700494 return -EINVAL;
Songchun Fana3f30572020-03-02 15:36:33 -0800495 }
496}
497
Yurii Zubrytskyi3c846362020-04-22 23:19:37 -0700498IncFsSize IncFs_ReleaseControlFds(IncFsControl* control, IncFsFd out[], IncFsSize outSize) {
499 if (!control || !out) {
500 return -EINVAL;
501 }
502 if (outSize < IncFsFdType::FDS_COUNT) {
503 return -ERANGE;
504 }
505 out[CMD] = std::exchange(control->cmd, -1);
506 out[PENDING_READS] = std::exchange(control->pendingReads, -1);
507 out[LOGS] = std::exchange(control->logs, -1);
508 return IncFsFdType::FDS_COUNT;
509}
510
Songchun Fana3f30572020-03-02 15:36:33 -0800511IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) {
512 return new IncFsControl(cmd, pendingReads, logs);
513}
514
515void IncFs_DeleteControl(IncFsControl* control) {
516 if (control) {
Yurii Zubrytskyi3c846362020-04-22 23:19:37 -0700517 if (control->cmd >= 0) {
518 close(control->cmd);
519 }
520 if (control->pendingReads >= 0) {
521 close(control->pendingReads);
522 }
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700523 if (control->logs >= 0) {
524 close(control->logs);
Songchun Fana3f30572020-03-02 15:36:33 -0800525 }
526 delete control;
527 }
528}
529
530IncFsErrorCode IncFs_SetOptions(const IncFsControl* control, IncFsMountOptions options) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700531 if (!control) {
532 return -EINVAL;
533 }
534 auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800535 if (root.empty()) {
536 return -EINVAL;
537 }
538 auto opts = makeMountOptionsString(options);
539 if (::mount(nullptr, root.c_str(), nullptr, MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_NOATIME,
540 opts.c_str()) != 0) {
541 const auto error = errno;
542 PLOG(ERROR) << "[incfs] Failed to remount IncFS filesystem: " << root;
543 return -error;
Songchun Fan59eee562019-10-24 16:46:06 -0700544 }
545 return 0;
546}
547
Songchun Fana3f30572020-03-02 15:36:33 -0800548IncFsErrorCode IncFs_Root(const IncFsControl* control, char buffer[], size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700549 if (!control) {
550 return -EINVAL;
551 }
552 std::string result = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800553 if (*bufferSize <= result.size()) {
554 *bufferSize = result.size() + 1;
555 return -EOVERFLOW;
556 }
557 result.copy(buffer, result.size());
558 buffer[result.size()] = '\0';
559 *bufferSize = result.size();
560 return 0;
561}
562
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -0700563template <class T>
564std::optional<T> read(IncFsSpan& data) {
565 if (data.size < (int32_t)sizeof(T)) {
566 return {};
567 }
568 T res;
569 memcpy(&res, data.data, sizeof(res));
570 data.data += sizeof(res);
571 data.size -= sizeof(res);
572 return res;
573}
574
575static IncFsErrorCode validateSignatureFormat(IncFsSpan signature) {
576 if (signature.data == nullptr && signature.size == 0) {
577 return 0; // it's fine to have unverified files too
578 }
579 if ((signature.data == nullptr) != (signature.size == 0)) {
580 return -EINVAL;
581 }
582
583 // These structs are here purely for checking the minimum size. Maybe will use them for
584 // parsing later.
585 struct __attribute__((packed)) Hashing {
586 int32_t size;
587 int32_t algorithm;
588 int8_t log2_blocksize;
589 int32_t salt_size;
590 int32_t raw_root_hash_size;
591 };
592 struct __attribute__((packed)) Signing {
593 int32_t size;
594 int32_t apk_digest_size;
595 int32_t certificate_size;
596 int32_t addl_data_size;
597 int32_t public_key_size;
598 int32_t algorithm;
599 int32_t signature_size;
600 };
601 struct __attribute__((packed)) MinSignature {
602 int32_t version;
603 Hashing hashing_info;
604 Signing signing_info;
605 };
606
607 if (signature.size < (int32_t)sizeof(MinSignature)) {
608 return -ERANGE;
609 }
610 if (signature.size > INCFS_MAX_SIGNATURE_SIZE) {
611 return -ERANGE;
612 }
613
614 auto version = read<int32_t>(signature);
615 if (version.value_or(-1) != INCFS_SIGNATURE_VERSION) {
616 return -EINVAL;
617 }
618 auto hashSize = read<int32_t>(signature);
619 if (!hashSize || signature.size < *hashSize) {
620 return -EINVAL;
621 }
622 auto hashAlgo = read<int32_t>(signature);
623 if (hashAlgo.value_or(-1) != INCFS_HASH_TREE_SHA256) {
624 return -EINVAL;
625 }
626 auto logBlockSize = read<int8_t>(signature);
627 if (logBlockSize.value_or(-1) != 12 /* 2^12 == 4096 */) {
628 return -EINVAL;
629 }
630 auto saltSize = read<int32_t>(signature);
631 if (saltSize.value_or(-1) != 0) {
632 return -EINVAL;
633 }
634 auto rootHashSize = read<int32_t>(signature);
635 if (rootHashSize.value_or(-1) != INCFS_MAX_HASH_SIZE) {
636 return -EINVAL;
637 }
638 if (signature.size < *rootHashSize) {
639 return -EINVAL;
640 }
641 signature.data += *rootHashSize;
642 signature.size -= *rootHashSize;
643 auto signingSize = read<int32_t>(signature);
644 // everything remaining has to be in the signing info
645 if (signingSize.value_or(-1) != signature.size) {
646 return -EINVAL;
647 }
648
649 // TODO: validate the signature part too.
650 return 0;
651}
652
Songchun Fana3f30572020-03-02 15:36:33 -0800653IncFsErrorCode IncFs_MakeFile(const IncFsControl* control, const char* path, int32_t mode,
654 IncFsFileId id, IncFsNewFileParams params) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700655 if (!control) {
656 return -EINVAL;
657 }
658
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800659 auto [root, subpath] = registry().rootAndSubpathFor(path);
660 if (root.empty()) {
Alex Buynytskyyebe71e52020-02-10 07:41:22 -0800661 PLOG(WARNING) << "[incfs] makeFile failed for path " << path << ", root is empty.";
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800662 return -EINVAL;
663 }
Alex Buynytskyyebe71e52020-02-10 07:41:22 -0800664 if (params.size < 0) {
665 LOG(WARNING) << "[incfs] makeFile failed for path " << path
666 << ", size is invalid: " << params.size;
667 return -ERANGE;
668 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800669
Luca Stefani742dfca2020-07-15 18:32:32 +0200670 const auto [subdir, name] = android::incfs::path::splitDirBase(subpath);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800671 incfs_new_file_args args = {
672 .size = (uint64_t)params.size,
673 .mode = (uint16_t)mode,
674 .directory_path = (uint64_t)subdir.data(),
675 .file_name = (uint64_t)name.data(),
676 .file_attr = (uint64_t)params.metadata.data,
677 .file_attr_len = (uint32_t)params.metadata.size,
678 };
679 static_assert(sizeof(args.file_id.bytes) == sizeof(id.data));
680 memcpy(args.file_id.bytes, id.data, sizeof(args.file_id.bytes));
681
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -0700682 if (auto err = validateSignatureFormat(params.signature)) {
683 return err;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800684 }
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -0700685 args.signature_info = (uint64_t)(uintptr_t)params.signature.data;
686 args.signature_size = (uint64_t)params.signature.size;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800687
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700688 if (::ioctl(control->cmd, INCFS_IOC_CREATE_FILE, &args)) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800689 PLOG(WARNING) << "[incfs] makeFile failed for " << root << " / " << subdir << " / " << name
690 << " of " << params.size << " bytes";
691 return -errno;
692 }
Luca Stefani742dfca2020-07-15 18:32:32 +0200693 if (::chmod(android::incfs::path::join(root, subpath).c_str(), mode)) {
Yurii Zubrytskyi3a259cf2020-05-02 01:20:42 -0700694 PLOG(WARNING) << "[incfs] couldn't change file mode to 0" << std::oct << mode;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800695 }
696
697 return 0;
698}
699
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700700static IncFsErrorCode makeDir(const char* commandPath, int32_t mode, bool allowExisting) {
701 if (!::mkdir(commandPath, mode)) {
702 if (::chmod(commandPath, mode)) {
Yurii Zubrytskyi3a259cf2020-05-02 01:20:42 -0700703 PLOG(WARNING) << "[incfs] couldn't change directory mode to 0" << std::oct << mode;
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700704 }
705 return 0;
706 }
707 // don't touch the existing dir's mode - mkdir(1) works that way.
708 return (allowExisting && errno == EEXIST) ? 0 : -errno;
709}
710
711static IncFsErrorCode makeDirs(std::string_view commandPath, std::string_view path,
712 std::string_view root, int32_t mode) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200713 auto commandCPath = android::incfs::details::c_str(commandPath);
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700714 const auto mkdirRes = makeDir(commandCPath, mode, true);
715 if (!mkdirRes) {
716 return 0;
717 }
718 if (mkdirRes != -ENOENT) {
719 LOG(ERROR) << __func__ << "(): mkdir failed for " << path << " - " << mkdirRes;
720 return mkdirRes;
721 }
722
Luca Stefani742dfca2020-07-15 18:32:32 +0200723 const auto parent = android::incfs::path::dirName(commandPath);
724 if (!android::incfs::path::startsWith(parent, root)) {
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700725 // went too far, already out of the root mount
726 return -EINVAL;
727 }
728
Luca Stefani742dfca2020-07-15 18:32:32 +0200729 if (auto parentMkdirRes = makeDirs(parent, android::incfs::path::dirName(path), root, mode)) {
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700730 return parentMkdirRes;
731 }
732 return makeDir(commandCPath, mode, true);
733}
734
Songchun Fana3f30572020-03-02 15:36:33 -0800735IncFsErrorCode IncFs_MakeDir(const IncFsControl* control, const char* path, int32_t mode) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700736 if (!control) {
737 return -EINVAL;
738 }
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700739 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800740 if (root.empty()) {
741 LOG(ERROR) << __func__ << "(): root is empty for " << path;
742 return -EINVAL;
743 }
744 auto commandPath = makeCommandPath(root, path);
745 if (commandPath.empty()) {
746 LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
747 return -EINVAL;
748 }
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700749 if (auto res = makeDir(commandPath.c_str(), mode, false)) {
750 LOG(ERROR) << __func__ << "(): mkdir failed for " << commandPath << " - " << res;
751 return res;
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800752 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800753 return 0;
754}
755
Yurii Zubrytskyi9b0276b2020-04-22 13:56:55 -0700756IncFsErrorCode IncFs_MakeDirs(const IncFsControl* control, const char* path, int32_t mode) {
757 if (!control) {
758 return -EINVAL;
759 }
760 const auto root = rootForCmd(control->cmd);
761 if (root.empty()) {
762 LOG(ERROR) << __func__ << "(): root is empty for " << path;
763 return -EINVAL;
764 }
765 auto commandPath = makeCommandPath(root, path);
766 if (commandPath.empty()) {
767 LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
768 return -EINVAL;
769 }
770 return makeDirs(commandPath, path, root, mode);
771}
772
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800773static IncFsErrorCode getMetadata(const char* path, char buffer[], size_t* bufferSize) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200774 const auto res = ::getxattr(path, android::incfs::kMetadataAttrName, buffer, *bufferSize);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800775 if (res < 0) {
776 if (errno == ERANGE) {
Luca Stefani742dfca2020-07-15 18:32:32 +0200777 auto neededSize = ::getxattr(path, android::incfs::kMetadataAttrName, buffer, 0);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800778 if (neededSize >= 0) {
779 *bufferSize = neededSize;
780 return 0;
781 }
782 }
783 return -errno;
784 }
785 *bufferSize = res;
786 return 0;
787}
788
Songchun Fana3f30572020-03-02 15:36:33 -0800789IncFsErrorCode IncFs_GetMetadataById(const IncFsControl* control, IncFsFileId fileId, char buffer[],
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800790 size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700791 if (!control) {
792 return -EINVAL;
793 }
794
795 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800796 if (root.empty()) {
797 return -EINVAL;
798 }
Luca Stefani742dfca2020-07-15 18:32:32 +0200799 auto name = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(fileId));
800 return getMetadata(android::incfs::details::c_str(name), buffer, bufferSize);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800801}
802
Songchun Fana3f30572020-03-02 15:36:33 -0800803IncFsErrorCode IncFs_GetMetadataByPath(const IncFsControl* control, const char* path, char buffer[],
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800804 size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700805 if (!control) {
806 return -EINVAL;
807 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800808 const auto pathRoot = registry().rootFor(path);
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700809 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800810 if (root.empty() || root != pathRoot) {
811 return -EINVAL;
812 }
813
814 return getMetadata(path, buffer, bufferSize);
815}
816
Songchun Fana3f30572020-03-02 15:36:33 -0800817IncFsFileId IncFs_GetId(const IncFsControl* control, const char* path) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700818 if (!control) {
819 return kIncFsInvalidFileId;
820 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800821 const auto pathRoot = registry().rootFor(path);
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700822 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800823 if (root.empty() || root != pathRoot) {
824 errno = EINVAL;
825 return kIncFsInvalidFileId;
826 }
827 char buffer[kIncFsFileIdStringLength];
Luca Stefani742dfca2020-07-15 18:32:32 +0200828 const auto res = ::getxattr(path, android::incfs::kIdAttrName, buffer, sizeof(buffer));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800829 if (res != sizeof(buffer)) {
830 return kIncFsInvalidFileId;
831 }
832 return toFileIdImpl({buffer, std::size(buffer)});
833}
834
835static IncFsErrorCode getSignature(int fd, char buffer[], size_t* bufferSize) {
836 incfs_get_file_sig_args args = {
837 .file_signature = (uint64_t)buffer,
838 .file_signature_buf_size = (uint32_t)*bufferSize,
839 };
840
841 auto res = ::ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args);
842 if (res < 0) {
843 if (errno == E2BIG) {
844 *bufferSize = INCFS_MAX_SIGNATURE_SIZE;
845 }
846 return -errno;
847 }
848 *bufferSize = args.file_signature_len_out;
849 return 0;
850}
851
Songchun Fana3f30572020-03-02 15:36:33 -0800852IncFsErrorCode IncFs_GetSignatureById(const IncFsControl* control, IncFsFileId fileId,
853 char buffer[], size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700854 if (!control) {
855 return -EINVAL;
856 }
857
858 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800859 if (root.empty()) {
860 return -EINVAL;
861 }
Luca Stefani742dfca2020-07-15 18:32:32 +0200862 auto file = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(fileId));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800863 auto fd = openRaw(file);
864 if (fd < 0) {
865 return fd.get();
866 }
867 return getSignature(fd, buffer, bufferSize);
868}
869
Songchun Fana3f30572020-03-02 15:36:33 -0800870IncFsErrorCode IncFs_GetSignatureByPath(const IncFsControl* control, const char* path,
871 char buffer[], size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700872 if (!control) {
873 return -EINVAL;
874 }
875
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800876 const auto pathRoot = registry().rootFor(path);
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700877 const auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800878 if (root.empty() || root != pathRoot) {
879 return -EINVAL;
880 }
Alex Buynytskyy06825d42020-02-07 16:03:05 -0800881 return IncFs_UnsafeGetSignatureByPath(path, buffer, bufferSize);
882}
883
884IncFsErrorCode IncFs_UnsafeGetSignatureByPath(const char* path, char buffer[], size_t* bufferSize) {
Alex Buynytskyy4c57f822020-02-12 15:27:47 -0800885 if (!isIncFsPath(path)) {
886 return -EINVAL;
887 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800888 auto fd = openRaw(path);
889 if (fd < 0) {
890 return fd.get();
891 }
892 return getSignature(fd, buffer, bufferSize);
893}
894
Songchun Fana3f30572020-03-02 15:36:33 -0800895IncFsErrorCode IncFs_Link(const IncFsControl* control, const char* fromPath,
896 const char* wherePath) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700897 if (!control) {
898 return -EINVAL;
899 }
900
901 auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800902 if (root.empty()) {
903 return -EINVAL;
904 }
905 auto cmdFrom = makeCommandPath(root, fromPath);
906 if (cmdFrom.empty()) {
907 return -EINVAL;
908 }
909 auto cmdWhere = makeCommandPath(root, wherePath);
910 if (cmdWhere.empty()) {
911 return -EINVAL;
912 }
913 if (::link(cmdFrom.c_str(), cmdWhere.c_str())) {
914 return -errno;
915 }
916 return 0;
917}
918
Songchun Fana3f30572020-03-02 15:36:33 -0800919IncFsErrorCode IncFs_Unlink(const IncFsControl* control, const char* path) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700920 if (!control) {
921 return -EINVAL;
922 }
923
924 auto root = rootForCmd(control->cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800925 if (root.empty()) {
926 return -EINVAL;
927 }
928 auto cmdPath = makeCommandPath(root, path);
929 if (cmdPath.empty()) {
930 return -EINVAL;
931 }
932 if (::unlink(cmdPath.c_str())) {
933 if (errno == EISDIR) {
934 if (!::rmdir(cmdPath.c_str())) {
935 return 0;
936 }
937 }
938 return -errno;
939 }
940 return 0;
941}
942
943static int waitForReads(int fd, int32_t timeoutMs, incfs_pending_read_info pendingReadsBuffer[],
944 size_t* pendingReadsBufferSize) {
Luca Stefani118b5912020-07-15 18:16:27 +0200945 auto hrTimeout = std::chrono::steady_clock::duration(std::chrono::milliseconds(timeoutMs));
Songchun Fan59eee562019-10-24 16:46:06 -0700946
947 while (hrTimeout > hrTimeout.zero() || (!pendingReadsBuffer && hrTimeout == hrTimeout.zero())) {
Luca Stefani118b5912020-07-15 18:16:27 +0200948 const auto startTs = std::chrono::steady_clock::now();
Songchun Fan59eee562019-10-24 16:46:06 -0700949
950 pollfd pfd = {fd, POLLIN, 0};
Luca Stefani118b5912020-07-15 18:16:27 +0200951 const auto res =
952 ::poll(&pfd, 1, duration_cast<std::chrono::milliseconds>(hrTimeout).count());
Songchun Fan59eee562019-10-24 16:46:06 -0700953 if (res > 0) {
954 break;
955 }
956 if (res == 0) {
957 if (pendingReadsBufferSize) {
958 *pendingReadsBufferSize = 0;
959 }
960 return -ETIMEDOUT;
961 }
962 const auto error = errno;
963 if (error != EINTR) {
964 PLOG(ERROR) << "poll() failed";
965 return -error;
966 }
Luca Stefani118b5912020-07-15 18:16:27 +0200967 hrTimeout -= std::chrono::steady_clock::now() - startTs;
Songchun Fan59eee562019-10-24 16:46:06 -0700968 }
969 if (!pendingReadsBuffer) {
970 return hrTimeout < hrTimeout.zero() ? -ETIMEDOUT : 0;
971 }
972
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800973 auto res =
974 ::read(fd, pendingReadsBuffer, *pendingReadsBufferSize * sizeof(*pendingReadsBuffer));
Songchun Fan59eee562019-10-24 16:46:06 -0700975 if (res < 0) {
976 const auto error = errno;
977 PLOG(ERROR) << "read() failed";
978 return -error;
979 }
980 if (res == 0) {
981 *pendingReadsBufferSize = 0;
982 return -ETIMEDOUT;
983 }
984 if ((res % sizeof(*pendingReadsBuffer)) != 0) {
985 PLOG(ERROR) << "read() returned half of a struct??";
986 return -EFAULT;
987 }
988 *pendingReadsBufferSize = res / sizeof(*pendingReadsBuffer);
989 return 0;
990}
991
Songchun Fana3f30572020-03-02 15:36:33 -0800992IncFsErrorCode IncFs_WaitForPendingReads(const IncFsControl* control, int32_t timeoutMs,
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800993 IncFsReadInfo buffer[], size_t* bufferSize) {
Alex Buynytskyy3f198872020-05-07 14:17:47 -0700994 if (!control || control->pendingReads < 0) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -0700995 return -EINVAL;
996 }
997
Yurii Zubrytskyid6688852019-11-26 17:39:08 -0800998 std::vector<incfs_pending_read_info> pendingReads;
999 pendingReads.resize(*bufferSize);
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001000 if (const auto res =
1001 waitForReads(control->pendingReads, timeoutMs, pendingReads.data(), bufferSize)) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001002 return res;
1003 }
1004 for (size_t i = 0; i != *bufferSize; ++i) {
1005 buffer[i] = IncFsReadInfo{
1006 .bootClockTsUs = pendingReads[i].timestamp_us,
1007 .block = (IncFsBlockIndex)pendingReads[i].block_index,
1008 .serialNo = pendingReads[i].serial_number,
1009 };
1010 memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
1011 }
1012 return 0;
1013}
1014
Songchun Fana3f30572020-03-02 15:36:33 -08001015IncFsErrorCode IncFs_WaitForPageReads(const IncFsControl* control, int32_t timeoutMs,
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001016 IncFsReadInfo buffer[], size_t* bufferSize) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001017 if (!control) {
1018 return -EINVAL;
1019 }
1020
1021 auto logsFd = control->logs;
Songchun Fana3f30572020-03-02 15:36:33 -08001022 if (logsFd < 0) {
Songchun Fan59eee562019-10-24 16:46:06 -07001023 return -EINVAL;
1024 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001025 std::vector<incfs_pending_read_info> pendingReads;
1026 pendingReads.resize(*bufferSize);
Songchun Fana3f30572020-03-02 15:36:33 -08001027 if (const auto res = waitForReads(logsFd, timeoutMs, pendingReads.data(), bufferSize)) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001028 return res;
1029 }
1030 for (size_t i = 0; i != *bufferSize; ++i) {
1031 buffer[i] = IncFsReadInfo{
1032 .bootClockTsUs = pendingReads[i].timestamp_us,
1033 .block = (IncFsBlockIndex)pendingReads[i].block_index,
1034 .serialNo = pendingReads[i].serial_number,
1035 };
1036 memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
1037 }
1038 return 0;
1039}
1040
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001041static IncFsFd openForSpecialOps(int cmd, const char* path) {
Luca Stefanie91f4fb2020-07-15 18:41:01 +02001042 unique_fd fd(::open(path, O_RDONLY | O_CLOEXEC));
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001043 if (fd < 0) {
1044 return -errno;
1045 }
Yurii Zubrytskyid00cd1b2020-03-19 10:47:32 -07001046 struct incfs_permit_fill args = {.file_descriptor = (uint32_t)fd.get()};
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001047 auto err = ::ioctl(cmd, INCFS_IOC_PERMIT_FILL, &args);
1048 if (err < 0) {
1049 return -errno;
1050 }
1051 return fd.release();
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001052}
1053
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001054IncFsFd IncFs_OpenForSpecialOpsByPath(const IncFsControl* control, const char* path) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001055 if (!control) {
1056 return -EINVAL;
1057 }
1058
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001059 const auto pathRoot = registry().rootFor(path);
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001060 const auto cmd = control->cmd;
Alex Buynytskyyd228b4e2020-03-31 11:58:34 -07001061 const auto root = rootForCmd(cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001062 if (root.empty() || root != pathRoot) {
1063 return -EINVAL;
1064 }
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001065 return openForSpecialOps(cmd, makeCommandPath(root, path).c_str());
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001066}
1067
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001068IncFsFd IncFs_OpenForSpecialOpsById(const IncFsControl* control, IncFsFileId id) {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001069 if (!control) {
1070 return -EINVAL;
1071 }
1072
1073 const auto cmd = control->cmd;
Alex Buynytskyyd228b4e2020-03-31 11:58:34 -07001074 const auto root = rootForCmd(cmd);
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001075 if (root.empty()) {
1076 return -EINVAL;
1077 }
Luca Stefani742dfca2020-07-15 18:32:32 +02001078 auto name = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(id));
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001079 return openForSpecialOps(cmd, makeCommandPath(root, name).c_str());
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001080}
1081
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001082static int writeBlocks(int fd, const incfs_fill_block blocks[], int blocksCount) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001083 if (fd < 0 || blocksCount == 0) {
Songchun Fan59eee562019-10-24 16:46:06 -07001084 return 0;
1085 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001086 if (blocksCount < 0) {
1087 return -EINVAL;
1088 }
Songchun Fan59eee562019-10-24 16:46:06 -07001089
1090 auto ptr = blocks;
1091 const auto end = blocks + blocksCount;
1092 do {
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001093 struct incfs_fill_blocks args = {.count = uint64_t(end - ptr),
1094 .fill_blocks = (uint64_t)(uintptr_t)ptr};
1095 const auto written = ::ioctl(fd, INCFS_IOC_FILL_BLOCKS, &args);
Songchun Fan59eee562019-10-24 16:46:06 -07001096 if (written < 0) {
1097 if (errno == EINTR) {
1098 continue;
1099 }
1100 const auto error = errno;
1101 PLOG(WARNING) << "writing IncFS blocks failed";
1102 if (ptr == blocks) {
1103 return -error;
1104 }
1105 // something has been written, return a success here and let the
1106 // next call handle the error.
1107 break;
1108 }
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001109 ptr += written;
Songchun Fan59eee562019-10-24 16:46:06 -07001110 } while (ptr < end);
1111 return ptr - blocks;
1112}
1113
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001114IncFsErrorCode IncFs_WriteBlocks(const IncFsDataBlock blocks[], size_t blocksCount) {
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001115 incfs_fill_block incfsBlocks[128];
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001116 int writtenCount = 0;
1117 int incfsBlocksUsed = 0;
1118 int lastBlockFd = -1;
1119 for (size_t i = 0; i < blocksCount; ++i) {
1120 if (lastBlockFd != blocks[i].fileFd || incfsBlocksUsed == std::size(incfsBlocks)) {
1121 auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1122 if (count > 0) {
1123 writtenCount += count;
1124 }
1125 if (count != incfsBlocksUsed) {
1126 return writtenCount ? writtenCount : count;
1127 }
1128 lastBlockFd = blocks[i].fileFd;
1129 incfsBlocksUsed = 0;
1130 }
Yurii Zubrytskyi62efffc2020-03-11 10:45:54 -07001131 incfsBlocks[incfsBlocksUsed] = incfs_fill_block{
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001132 .block_index = (uint32_t)blocks[i].pageIndex,
1133 .data_len = blocks[i].dataSize,
1134 .data = (uint64_t)blocks[i].data,
1135 .compression = (uint8_t)blocks[i].compression,
1136 .flags = uint8_t(blocks[i].kind == INCFS_BLOCK_KIND_HASH ? INCFS_BLOCK_FLAGS_HASH
1137 : 0),
1138 };
1139 ++incfsBlocksUsed;
Songchun Fan59eee562019-10-24 16:46:06 -07001140 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001141 auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1142 if (count > 0) {
1143 writtenCount += count;
Songchun Fan59eee562019-10-24 16:46:06 -07001144 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001145 return writtenCount ? writtenCount : count;
Songchun Fan59eee562019-10-24 16:46:06 -07001146}
1147
Songchun Fan59eee562019-10-24 16:46:06 -07001148IncFsErrorCode IncFs_BindMount(const char* sourceDir, const char* targetDir) {
Luca Stefani742dfca2020-07-15 18:32:32 +02001149 if (!android::incfs::enabled()) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001150 return -ENOTSUP;
Songchun Fan59eee562019-10-24 16:46:06 -07001151 }
Songchun Fan59eee562019-10-24 16:46:06 -07001152
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001153 auto [sourceRoot, subpath] = registry().rootAndSubpathFor(sourceDir);
1154 if (sourceRoot.empty()) {
Songchun Fan59eee562019-10-24 16:46:06 -07001155 return -EINVAL;
1156 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001157 if (subpath.empty()) {
1158 LOG(WARNING) << "[incfs] Binding the root mount '" << sourceRoot << "' is not allowed";
1159 return -EINVAL;
1160 }
1161
1162 if (auto err = isValidMountTarget(targetDir); err != 0) {
1163 return err;
1164 }
1165
1166 if (::mount(sourceDir, targetDir, nullptr, MS_BIND, nullptr)) {
1167 PLOG(ERROR) << "[incfs] Failed to bind mount '" << sourceDir << "' to '" << targetDir
1168 << '\'';
1169 return -errno;
1170 }
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001171 return 0;
Songchun Fan59eee562019-10-24 16:46:06 -07001172}
1173
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001174IncFsErrorCode IncFs_Unmount(const char* dir) {
Luca Stefani742dfca2020-07-15 18:32:32 +02001175 if (!android::incfs::enabled()) {
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001176 return -ENOTSUP;
1177 }
1178
Yurii Zubrytskyid6688852019-11-26 17:39:08 -08001179 errno = 0;
1180 if (::umount2(dir, MNT_FORCE) == 0 || errno == EINVAL || errno == ENOENT) {
1181 // EINVAL - not a mount point, ENOENT - doesn't exist at all
1182 return -errno;
1183 }
1184 PLOG(WARNING) << __func__ << ": umount(force) failed, detaching '" << dir << '\'';
1185 errno = 0;
1186 if (!::umount2(dir, MNT_DETACH)) {
1187 return 0;
1188 }
1189 PLOG(WARNING) << __func__ << ": umount(detach) returned non-zero for '" << dir << '\'';
1190 return 0;
1191}
1192
1193bool IncFs_IsIncFsPath(const char* path) {
1194 return isIncFsPath(path);
Songchun Fan59eee562019-10-24 16:46:06 -07001195}
Yurii Zubrytskyi59e99372020-03-28 21:32:12 -07001196
1197IncFsErrorCode IncFs_GetFilledRanges(int fd, IncFsSpan outBuffer, IncFsFilledRanges* filledRanges) {
1198 return IncFs_GetFilledRangesStartingFrom(fd, 0, outBuffer, filledRanges);
1199}
1200
1201IncFsErrorCode IncFs_GetFilledRangesStartingFrom(int fd, int startBlockIndex, IncFsSpan outBuffer,
1202 IncFsFilledRanges* filledRanges) {
1203 if (fd < 0) {
1204 return -EBADF;
1205 }
1206 if (startBlockIndex < 0) {
1207 return -EINVAL;
1208 }
1209 if (!outBuffer.data && outBuffer.size > 0) {
1210 return -EINVAL;
1211 }
1212 if (!filledRanges) {
1213 return -EINVAL;
1214 }
1215 // Use this to optimize the incfs call and have the same buffer for both the incfs and the
1216 // public structs.
1217 static_assert(sizeof(IncFsBlockRange) == sizeof(incfs_filled_range));
1218
1219 *filledRanges = {};
1220
1221 auto outStart = (IncFsBlockRange*)outBuffer.data;
1222 auto outEnd = outStart + outBuffer.size / sizeof(*outStart);
1223
1224 auto outPtr = outStart;
1225 int error = 0;
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001226 int dataBlocks;
Yurii Zubrytskyi59e99372020-03-28 21:32:12 -07001227 incfs_get_filled_blocks_args args = {};
1228 for (;;) {
1229 auto start = args.index_out ? args.index_out : startBlockIndex;
1230 args = incfs_get_filled_blocks_args{
1231 .range_buffer = (uint64_t)(uintptr_t)outPtr,
1232 .range_buffer_size = uint32_t((outEnd - outPtr) * sizeof(*outPtr)),
1233 .start_index = start,
1234 };
1235 errno = 0;
1236 auto res = ::ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &args);
1237 error = errno;
1238 if (res && error != EINTR && error != ERANGE) {
1239 return -error;
1240 }
1241
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001242 dataBlocks = args.data_blocks_out;
Yurii Zubrytskyi59e99372020-03-28 21:32:12 -07001243 outPtr += args.range_buffer_size_out / sizeof(incfs_filled_range);
1244 if (!res || error == ERANGE) {
1245 break;
1246 }
1247 // in case of EINTR we want to continue calling the function
1248 }
1249
1250 if (outPtr > outEnd) {
1251 outPtr = outEnd;
1252 error = ERANGE;
1253 }
1254
1255 filledRanges->endIndex = args.index_out;
1256 auto hashStartPtr = outPtr;
1257 if (outPtr != outStart) {
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001258 // figure out the ranges for data block and hash blocks in the output
Yurii Zubrytskyi59e99372020-03-28 21:32:12 -07001259 for (; hashStartPtr != outStart; --hashStartPtr) {
1260 if ((hashStartPtr - 1)->begin < dataBlocks) {
1261 break;
1262 }
1263 }
1264 auto lastDataPtr = hashStartPtr - 1;
1265 // here we go, this is the first block that's before or at the hashes
1266 if (lastDataPtr->end <= dataBlocks) {
1267 ; // we're good, the boundary is between the ranges - |hashStartPtr| is correct
1268 } else {
1269 // the hard part: split the |lastDataPtr| range into the data and the hash pieces
1270 if (outPtr == outEnd) {
1271 // the buffer turned out to be too small, even though it actually wasn't
1272 error = ERANGE;
1273 if (hashStartPtr == outEnd) {
1274 // this is even worse: there's no room to put even a single hash block into.
1275 filledRanges->endIndex = lastDataPtr->end = dataBlocks;
1276 } else {
1277 std::copy_backward(lastDataPtr, outPtr - 1, outPtr);
1278 lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1279 filledRanges->endIndex = (outPtr - 1)->end;
1280 }
1281 } else {
1282 std::copy_backward(lastDataPtr, outPtr, outPtr + 1);
1283 lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1284 ++outPtr;
1285 }
1286 }
1287 // now fix the indices of all hash blocks - no one should know they're simply past the
1288 // regular data blocks in the file!
1289 for (auto ptr = hashStartPtr; ptr != outPtr; ++ptr) {
1290 ptr->begin -= dataBlocks;
1291 ptr->end -= dataBlocks;
1292 }
1293 }
1294
1295 filledRanges->dataRanges = outStart;
1296 filledRanges->dataRangesCount = hashStartPtr - outStart;
1297 filledRanges->hashRanges = hashStartPtr;
1298 filledRanges->hashRangesCount = outPtr - hashStartPtr;
1299
1300 return -error;
1301}
1302
1303IncFsErrorCode IncFs_IsFullyLoaded(int fd) {
1304 char buffer[2 * sizeof(IncFsBlockRange)];
1305 IncFsFilledRanges ranges;
1306 auto res = IncFs_GetFilledRanges(fd, IncFsSpan{.data = buffer, .size = std::size(buffer)},
1307 &ranges);
1308 if (res == -ERANGE) {
1309 // need room for more than two ranges - definitely not fully loaded
1310 return -ENODATA;
1311 }
1312 if (res != 0) {
1313 return res;
1314 }
1315 // empty file
1316 if (ranges.endIndex == 0) {
1317 return 0;
1318 }
1319 // file with no hash tree
1320 if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 0) {
1321 return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == ranges.endIndex)
1322 ? 0
1323 : -ENODATA;
1324 }
1325 // file with a hash tree
1326 if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 1) {
1327 // calculate the expected data size from the size of the hash range and |endIndex|, which is
1328 // the total number of blocks in the file, both data and hash blocks together.
1329 if (ranges.hashRanges[0].begin != 0) {
1330 return -ENODATA;
1331 }
1332 const auto expectedDataBlocks =
1333 ranges.endIndex - (ranges.hashRanges[0].end - ranges.hashRanges[0].begin);
1334 return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == expectedDataBlocks)
1335 ? 0
1336 : -ENODATA;
1337 }
1338 return -ENODATA;
Yurii Zubrytskyi4ad09772020-04-01 12:14:38 -07001339}
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001340
Luca Stefani742dfca2020-07-15 18:32:32 +02001341android::incfs::MountRegistry& android::incfs::defaultMountRegistry() {
Yurii Zubrytskyi003751d2020-04-16 12:45:39 -07001342 return registry();
1343}