diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-03 23:09:38 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-11-03 23:09:38 +0000 |
commit | 619f5a888a5591ff0a758be18e71f3912abd5457 (patch) | |
tree | 84acfbe4f954ef0b16158e194902ea9df6fe640c | |
parent | 3955dd6b482159ae0b2a4343c1881430f508a9e0 (diff) | |
parent | 10784ed07990087be79474c595093638c21377cd (diff) | |
download | incremental_delivery-android12L-d2-s2-release.tar.gz |
Snap for 7880955 from 10784ed07990087be79474c595093638c21377cd to sc-d2-releaseandroid-12.1.0_r26android-12.1.0_r25android-12.1.0_r24android-12.1.0_r23android-12.1.0_r18android-12.1.0_r17android-12.1.0_r16android-12.1.0_r15android-12.1.0_r14android-12.1.0_r13android-12.1.0_r12android12L-d2-s8-releaseandroid12L-d2-s7-releaseandroid12L-d2-s6-releaseandroid12L-d2-s5-releaseandroid12L-d2-s4-releaseandroid12L-d2-s3-releaseandroid12L-d2-s2-releaseandroid12L-d2-s1-releaseandroid12L-d2-release
Change-Id: I12dff394d17458439215f8cbf905ffd408909fce
-rw-r--r-- | incfs/MountRegistry.cpp | 45 | ||||
-rw-r--r-- | incfs/incfs.cpp | 103 | ||||
-rw-r--r-- | incfs/include/path.h | 18 | ||||
-rw-r--r-- | incfs/path.cpp | 21 |
4 files changed, 144 insertions, 43 deletions
diff --git a/incfs/MountRegistry.cpp b/incfs/MountRegistry.cpp index b7b53c8..4f16f73 100644 --- a/incfs/MountRegistry.cpp +++ b/incfs/MountRegistry.cpp @@ -302,6 +302,25 @@ static bool forEachLine(base::borrowed_fd fd, Callback&& cb) { return true; } +static std::string fixBackingDir(std::string_view dir, std::string_view mountDir) { + if (!dir.starts_with("/proc/"sv)) { + return std::string(dir); + } + + // HACK: + // Vold uses a secure way of mounting incremental storages, where it passes in + // a virtual symlink in /proc/self/fd/... instead of the original path. Unfortunately, + // this symlink string gets preserved by the system and we can't resolve it later. + // But it's the only place that uses this symlink, and we know exactly how the + // mount and backing directory paths look in that case - so can recover one from + // another. + if (path::endsWith(mountDir, "mount"sv)) { + return path::join(path::dirName(mountDir), "backing_store"sv); + } + // Well, hack didn't work. Still may not return a /proc/ path + return {}; +} + bool MountRegistry::Mounts::loadFrom(base::borrowed_fd fd, std::string_view filesystem) { struct MountInfo { std::string backing; @@ -327,19 +346,21 @@ bool MountRegistry::Mounts::loadFrom(base::borrowed_fd fd, std::string_view file } const auto groupId = items[2]; auto subdir = items[3]; - auto backingDir = items.rbegin()[1]; auto mountPoint = std::string(items[4]); fixProcPath(mountPoint); mountPoint = path::normalize(mountPoint); auto& mount = mountsByGroup[std::string(groupId)]; - if (mount.backing.empty()) { - mount.backing.assign(backingDir); - } else if (mount.backing != backingDir) { - LOG(WARNING) << "[incfs] root '" << *mount.roots.begin() - << "' mounted in multiple places with different backing dirs, '" - << mount.backing << "' vs new '" << backingDir - << "'; updating to the new one"; - mount.backing.assign(backingDir); + auto backingDir = fixBackingDir(items.rbegin()[1], mountPoint); + if (!backingDir.empty()) { + if (mount.backing.empty()) { + mount.backing = std::move(backingDir); + } else if (mount.backing != backingDir) { + LOG(WARNING) << "[incfs] root '" << *mount.roots.begin() + << "' mounted in multiple places with different backing dirs, '" + << mount.backing << "' vs new '" << backingDir + << "'; updating to the new one"; + mount.backing = std::move(backingDir); + } } if (subdir == "/"sv) { mount.roots.emplace(mountPoint); @@ -368,6 +389,12 @@ bool MountRegistry::Mounts::loadFrom(base::borrowed_fd fd, std::string_view file << mount.bindPoints.size() << " bind(s), ignoring"; continue; } + if (mount.backing.empty()) { + LOG(WARNING) << "[incfs] mount '" << *mount.roots.begin() + << "' has no backing dir, but " << mount.bindPoints.size() + << " bind(s), ignoring"; + continue; + } Root& root = roots[index]; auto& binds = root.binds; diff --git a/incfs/incfs.cpp b/incfs/incfs.cpp index 842e381..e13cd81 100644 --- a/incfs/incfs.cpp +++ b/incfs/incfs.cpp @@ -81,8 +81,13 @@ static ab::unique_fd openRaw(std::string_view file) { return fd; } -static ab::unique_fd openRaw(std::string_view dir, std::string_view name) { - return openRaw(path::join(dir, name)); +static ab::unique_fd openAt(int fd, std::string_view name, int flags = 0) { + auto res = ab::unique_fd( + ::openat(fd, details::c_str(name), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | flags)); + if (res < 0) { + return ab::unique_fd{-errno}; + } + return res; } static std::string indexPath(std::string_view root, IncFsFileId fileId) { @@ -334,32 +339,47 @@ static int isValidMountTarget(const char* path) { return 0; } -static int rmDirContent(const char* path) { - auto dir = path::openDir(path); +static int rmDirContent(int dirFd) { + auto dir = path::openDir(dirFd); if (!dir) { - return -EINVAL; + return -errno; } while (auto entry = ::readdir(dir.get())) { if (entry->d_name == "."sv || entry->d_name == ".."sv) { continue; } - auto fullPath = ab::StringPrintf("%s/%s", path, entry->d_name); if (entry->d_type == DT_DIR) { - if (const auto err = rmDirContent(fullPath.c_str()); err != 0) { - return err; + auto fd = openAt(dirFd, entry->d_name, O_DIRECTORY); + if (!fd.ok()) { + return -errno; } - if (const auto err = ::rmdir(fullPath.c_str()); err != 0) { + if (const auto err = rmDirContent(fd.get())) { return err; } + if (::unlinkat(fd.get(), entry->d_name, AT_REMOVEDIR)) { + return -errno; + } } else { - if (const auto err = ::unlink(fullPath.c_str()); err != 0) { - return err; + auto fd = openAt(dirFd, entry->d_name); + if (!fd.ok()) { + return -errno; + } + if (::unlinkat(fd.get(), entry->d_name, 0)) { + return -errno; } } } return 0; } +static int rmDirContent(const char* path) { + auto fd = openAt(-1, path, O_DIRECTORY); + if (!fd.ok()) { + return -errno; + } + return rmDirContent(fd.get()); +} + static std::string makeMountOptionsString(IncFsMountOptions options) { auto opts = ab::StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1,", unsigned(options.defaultReadTimeoutMs), @@ -375,8 +395,8 @@ static std::string makeMountOptionsString(IncFsMountOptions options) { return opts; } -static IncFsControl* makeControl(const char* root) { - auto cmd = openRaw(root, INCFS_PENDING_READS_FILENAME); +static IncFsControl* makeControl(int fd) { + auto cmd = openAt(fd, INCFS_PENDING_READS_FILENAME); if (!cmd.ok()) { return nullptr; } @@ -384,13 +404,13 @@ static IncFsControl* makeControl(const char* root) { if (!pendingReads.ok()) { return nullptr; } - auto logs = openRaw(root, INCFS_LOG_FILENAME); + auto logs = openAt(fd, INCFS_LOG_FILENAME); if (!logs.ok()) { return nullptr; } ab::unique_fd blocksWritten; if (features() & Features::v2) { - blocksWritten = openRaw(root, INCFS_BLOCKS_WRITTEN_FILENAME); + blocksWritten = openAt(fd, INCFS_BLOCKS_WRITTEN_FILENAME); if (!blocksWritten.ok()) { return nullptr; } @@ -549,12 +569,30 @@ IncFsControl* IncFs_Mount(const char* backingPath, const char* targetDir, return nullptr; } - if (!restoreconControlFiles(targetDir)) { + // in case when the path is given in a form of a /proc/.../fd/ link, we need to update + // it here: old fd refers to the original empty directory, not to the mount + std::string updatedTargetDir; + if (path::dirName(targetDir) == path::procfsFdDir) { + updatedTargetDir = path::readlink(targetDir); + } else { + updatedTargetDir = targetDir; + } + + auto rootFd = ab::unique_fd(::open(updatedTargetDir.c_str(), O_PATH | O_CLOEXEC | O_DIRECTORY)); + if (updatedTargetDir != targetDir) { + // ensure that the new directory is still the same after reopening + if (path::fromFd(rootFd) != updatedTargetDir) { + errno = EINVAL; + return nullptr; + } + } + + if (!restoreconControlFiles(path::procfsForFd(rootFd))) { (void)IncFs_Unmount(targetDir); return nullptr; } - auto control = makeControl(targetDir); + auto control = makeControl(rootFd); if (control == nullptr) { (void)IncFs_Unmount(targetDir); return nullptr; @@ -568,7 +606,8 @@ IncFsControl* IncFs_Open(const char* dir) { errno = EINVAL; return nullptr; } - return makeControl(details::c_str(root)); + auto rootFd = ab::unique_fd(::open(details::c_str(root), O_PATH | O_CLOEXEC | O_DIRECTORY)); + return makeControl(rootFd); } IncFsFd IncFs_GetControlFd(const IncFsControl* control, IncFsFdType type) { @@ -789,7 +828,7 @@ IncFsErrorCode IncFs_MakeFile(const IncFsControl* control, const char* path, int << " of " << params.size << " bytes"; return -errno; } - if (::chmod(path::join(root, subpath).c_str(), mode)) { + if (::chmod(path::join(root, subdir, name).c_str(), mode)) { PLOG(WARNING) << "[incfs] couldn't change file mode to 0" << std::oct << mode; } @@ -1325,13 +1364,21 @@ IncFsErrorCode IncFs_BindMount(const char* sourceDir, const char* targetDir) { return -ENOTSUP; } - auto [sourceRoot, subpath] = registry().rootAndSubpathFor(sourceDir); - if (sourceRoot.empty()) { - return -EINVAL; - } - if (subpath.empty()) { - LOG(WARNING) << "[incfs] Binding the root mount '" << sourceRoot << "' is not allowed"; - return -EINVAL; + if (path::dirName(sourceDir) == path::procfsFdDir) { + // can't find such path in the mount registry, but still can verify the filesystem + // via the stat() call + if (!isIncFsPathImpl(sourceDir)) { + return -EINVAL; + } + } else { + auto [sourceRoot, subpath] = registry().rootAndSubpathFor(sourceDir); + if (sourceRoot.empty()) { + return -EINVAL; + } + if (subpath.empty()) { + LOG(WARNING) << "[incfs] Binding the root mount '" << sourceRoot << "' is not allowed"; + return -EINVAL; + } } if (auto err = isValidMountTarget(targetDir); err != 0) { @@ -1350,6 +1397,10 @@ IncFsErrorCode IncFs_Unmount(const char* dir) { if (!enabled()) { return -ENOTSUP; } + if (!isIncFsPathImpl(dir)) { + LOG(WARNING) << __func__ << ": umount() called on non-incfs directory '" << dir << '\''; + return -EINVAL; + } errno = 0; if (::umount2(dir, MNT_FORCE) == 0 || errno == EINVAL || errno == ENOENT) { diff --git a/incfs/include/path.h b/incfs/include/path.h index 325eb1d..62f8b54 100644 --- a/incfs/include/path.h +++ b/incfs/include/path.h @@ -29,7 +29,11 @@ namespace details { void appendNextPath(std::string& res, std::string_view c); } +constexpr auto procfsFdDir = std::string_view("/proc/self/fd"); + std::string fromFd(int fd); +std::string procfsForFd(int fd); +std::string readlink(std::string_view path); bool isAbsolute(std::string_view path); std::string normalize(std::string_view path); @@ -94,11 +98,17 @@ int isEmptyDir(std::string_view dir); bool startsWith(std::string_view path, std::string_view prefix); bool endsWith(std::string_view path, std::string_view prefix); +struct PathDirCloser { + void operator()(DIR* d) const { ::closedir(d); } +}; + inline auto openDir(const char* path) { - struct closer { - void operator()(DIR* d) const { ::closedir(d); } - }; - auto dir = std::unique_ptr<DIR, closer>(::opendir(path)); + auto dir = std::unique_ptr<DIR, PathDirCloser>(::opendir(path)); + return dir; +} + +inline auto openDir(int dirFd) { + auto dir = std::unique_ptr<DIR, PathDirCloser>(::fdopendir(dirFd)); return dir; } diff --git a/incfs/path.cpp b/incfs/path.cpp index f6f87ae..51442db 100644 --- a/incfs/path.cpp +++ b/incfs/path.cpp @@ -114,12 +114,25 @@ std::string normalize(std::string_view path) { return result; } +static constexpr char fdNameFormat[] = "/proc/self/fd/%d"; + +std::string procfsForFd(int fd) { + char fdNameBuffer[std::size(fdNameFormat) + 11 + 1]; // max int length + '\0' + snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd); + return fdNameBuffer; +} + std::string fromFd(int fd) { - static constexpr auto kDeletedSuffix = " (deleted)"sv; - static constexpr char fdNameFormat[] = "/proc/self/fd/%d"; char fdNameBuffer[std::size(fdNameFormat) + 11 + 1]; // max int length + '\0' snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd); + return readlink(fdNameBuffer); +} + +std::string readlink(std::string_view path) { + static constexpr auto kDeletedSuffix = " (deleted)"sv; + + auto cPath = c_str(path); std::string res; // We used to call lstat() here to preallocate the buffer to the exact required size; turns out // that call is significantly more expensive than anything else, so doing a couple extra @@ -127,9 +140,9 @@ std::string fromFd(int fd) { auto bufSize = 256; for (;;) { res.resize(bufSize - 1, '\0'); - auto size = ::readlink(fdNameBuffer, &res[0], res.size()); + auto size = ::readlink(cPath, &res[0], res.size()); if (size < 0) { - PLOG(ERROR) << "readlink failed for " << fdNameBuffer; + PLOG(ERROR) << "readlink failed for " << path; return {}; } if (size >= ssize_t(res.size())) { |