summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-03 23:09:38 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-03 23:09:38 +0000
commit619f5a888a5591ff0a758be18e71f3912abd5457 (patch)
tree84acfbe4f954ef0b16158e194902ea9df6fe640c
parent3955dd6b482159ae0b2a4343c1881430f508a9e0 (diff)
parent10784ed07990087be79474c595093638c21377cd (diff)
downloadincremental_delivery-android12L-d2-s2-release.tar.gz
Change-Id: I12dff394d17458439215f8cbf905ffd408909fce
-rw-r--r--incfs/MountRegistry.cpp45
-rw-r--r--incfs/incfs.cpp103
-rw-r--r--incfs/include/path.h18
-rw-r--r--incfs/path.cpp21
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())) {