2019-12-16 19:54:07 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "utility.h"
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <android-base/file.h>
|
|
|
|
#include <android-base/logging.h>
|
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
#include <android-base/strings.h>
|
|
|
|
#include <libfiemap/fiemap_writer.h>
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
namespace fiemap {
|
|
|
|
|
|
|
|
using namespace std::string_literals;
|
|
|
|
using android::base::unique_fd;
|
|
|
|
|
|
|
|
static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
|
|
|
|
|
2019-12-27 22:33:58 +00:00
|
|
|
FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
|
2019-12-16 19:54:07 +00:00
|
|
|
// Create the smallest file possible (one block).
|
2019-12-27 22:33:58 +00:00
|
|
|
FiemapUniquePtr writer;
|
|
|
|
auto status = FiemapWriter::Open(file_path, 1, &writer);
|
|
|
|
if (!status.is_ok()) {
|
|
|
|
return status;
|
2019-12-16 19:54:07 +00:00
|
|
|
}
|
|
|
|
|
2019-12-27 22:33:58 +00:00
|
|
|
*result = 0;
|
2019-12-16 19:54:07 +00:00
|
|
|
switch (writer->fs_type()) {
|
|
|
|
case EXT4_SUPER_MAGIC:
|
|
|
|
// The minimum is 16GiB, so just report that. If we wanted we could parse the
|
|
|
|
// superblock and figure out if 64-bit support is enabled.
|
2019-12-27 22:33:58 +00:00
|
|
|
*result = 17179869184ULL;
|
2019-12-16 19:54:07 +00:00
|
|
|
break;
|
|
|
|
case F2FS_SUPER_MAGIC:
|
|
|
|
// Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
|
|
|
|
// 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
|
2019-12-27 22:33:58 +00:00
|
|
|
*result = 4329690886144ULL;
|
2019-12-16 19:54:07 +00:00
|
|
|
break;
|
|
|
|
case MSDOS_SUPER_MAGIC:
|
|
|
|
// 4GB-1, which we want aligned to the block size.
|
2019-12-27 22:33:58 +00:00
|
|
|
*result = 4294967295;
|
|
|
|
*result -= (*result % writer->block_size());
|
2019-12-16 19:54:07 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close and delete the temporary file.
|
|
|
|
writer = nullptr;
|
|
|
|
unlink(file_path.c_str());
|
|
|
|
|
2019-12-27 22:33:58 +00:00
|
|
|
return FiemapStatus::Ok();
|
2019-12-16 19:54:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Given a SplitFiemap, this returns a device path that will work during first-
|
|
|
|
// stage init (i.e., its path can be found by InitRequiredDevices).
|
|
|
|
std::string GetDevicePathForFile(SplitFiemap* file) {
|
|
|
|
auto bdev_path = file->bdev_path();
|
|
|
|
|
|
|
|
struct stat userdata, given;
|
|
|
|
if (!stat(bdev_path.c_str(), &given) && !stat(kUserdataDevice, &userdata)) {
|
|
|
|
if (S_ISBLK(given.st_mode) && S_ISBLK(userdata.st_mode) &&
|
|
|
|
given.st_rdev == userdata.st_rdev) {
|
|
|
|
return kUserdataDevice;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bdev_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string JoinPaths(const std::string& dir, const std::string& file) {
|
|
|
|
if (android::base::EndsWith(dir, "/")) {
|
|
|
|
return dir + file;
|
|
|
|
}
|
|
|
|
return dir + "/" + file;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool F2fsPinBeforeAllocate(int file_fd, bool* supported) {
|
|
|
|
struct stat st;
|
|
|
|
if (fstat(file_fd, &st) < 0) {
|
|
|
|
PLOG(ERROR) << "stat failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::string bdev;
|
|
|
|
if (!BlockDeviceToName(major(st.st_dev), minor(st.st_dev), &bdev)) {
|
|
|
|
LOG(ERROR) << "Failed to get block device name for " << major(st.st_dev) << ":"
|
|
|
|
<< minor(st.st_dev);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string contents;
|
|
|
|
std::string feature_file = "/sys/fs/f2fs/" + bdev + "/features";
|
|
|
|
if (!android::base::ReadFileToString(feature_file, &contents)) {
|
|
|
|
PLOG(ERROR) << "read failed: " << feature_file;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
contents = android::base::Trim(contents);
|
|
|
|
|
|
|
|
auto features = android::base::Split(contents, ", ");
|
|
|
|
auto iter = std::find(features.begin(), features.end(), "pin_file"s);
|
|
|
|
*supported = (iter != features.end());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
|
|
|
|
// The symlinks in /sys/dev/block point to the block device node under /sys/device/..
|
|
|
|
// The directory name in the target corresponds to the name of the block device. We use
|
|
|
|
// that to extract the block device name.
|
|
|
|
// e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
|
|
|
|
// follows.
|
|
|
|
// 1:0 -> ../../devices/virtual/block/ram0
|
|
|
|
std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
|
|
|
|
std::string sysfs_bdev;
|
|
|
|
|
|
|
|
if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
|
|
|
|
PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*bdev_name = ::android::base::Basename(sysfs_bdev);
|
2020-07-22 23:35:09 +00:00
|
|
|
// Check that the symlink doesn't point to itself.
|
2019-12-16 19:54:07 +00:00
|
|
|
if (sysfs_bdev == *bdev_name) {
|
|
|
|
LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FilesystemHasReliablePinning(const std::string& file, bool* supported) {
|
|
|
|
struct statfs64 sfs;
|
|
|
|
if (statfs64(file.c_str(), &sfs)) {
|
|
|
|
PLOG(ERROR) << "statfs failed: " << file;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (sfs.f_type != F2FS_SUPER_MAGIC) {
|
|
|
|
*supported = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
|
|
|
|
if (fd < 0) {
|
|
|
|
PLOG(ERROR) << "open failed: " << file;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return F2fsPinBeforeAllocate(fd, supported);
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:52:12 +00:00
|
|
|
bool IsSubdir(const std::string& child, const std::string& parent) {
|
|
|
|
// Precondition: both are absolute paths.
|
|
|
|
CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
|
|
|
|
CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
|
|
|
|
|
|
|
|
// Remove extraneous "/" at the end.
|
|
|
|
std::string_view child_sv = child;
|
|
|
|
while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
|
|
|
|
;
|
|
|
|
|
|
|
|
std::string_view parent_sv = parent;
|
|
|
|
while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
|
|
|
|
;
|
|
|
|
|
|
|
|
// IsSubdir(anything, "/") => true
|
|
|
|
if (parent_sv == "/") return true;
|
|
|
|
|
|
|
|
// IsSubdir("/foo", "/foo") => true
|
|
|
|
if (parent_sv == child_sv) return true;
|
|
|
|
|
|
|
|
// IsSubdir("/foo/bar", "/foo") => true
|
|
|
|
// IsSubdir("/foo-bar", "/foo") => false
|
|
|
|
return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
|
|
|
|
}
|
|
|
|
|
2019-12-16 19:54:07 +00:00
|
|
|
} // namespace fiemap
|
|
|
|
} // namespace android
|