Merge "libfiemap: Create/Open returns FiemapStatus"

This commit is contained in:
Yifan Hong 2020-01-08 01:00:43 +00:00 committed by Gerrit Code Review
commit 0ed21b604c
10 changed files with 220 additions and 67 deletions

View file

@ -24,6 +24,7 @@ filegroup {
name: "libfiemap_srcs", name: "libfiemap_srcs",
srcs: [ srcs: [
"fiemap_writer.cpp", "fiemap_writer.cpp",
"fiemap_status.cpp",
"image_manager.cpp", "image_manager.cpp",
"metadata.cpp", "metadata.cpp",
"split_fiemap_writer.cpp", "split_fiemap_writer.cpp",

View file

@ -0,0 +1,42 @@
/*
* 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 <libfiemap/fiemap_status.h>
namespace android::fiemap {
// FiemapStatus -> string
std::string FiemapStatus::string() const {
if (error_code() == ErrorCode::ERROR) {
return "Error";
}
return strerror(-static_cast<int>(error_code()));
}
// -errno -> known ErrorCode
// unknown ErrorCode -> known ErrorCode
FiemapStatus::ErrorCode FiemapStatus::CastErrorCode(int error_code) {
switch (error_code) {
case static_cast<int32_t>(ErrorCode::SUCCESS):
case static_cast<int32_t>(ErrorCode::NO_SPACE):
return static_cast<ErrorCode>(error_code);
case static_cast<int32_t>(ErrorCode::ERROR):
default:
return ErrorCode::ERROR;
}
}
} // namespace android::fiemap

View file

@ -262,9 +262,9 @@ static bool PerformFileChecks(const std::string& file_path, uint64_t* blocksz, u
return true; return true;
} }
static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size, static FiemapStatus FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
const std::string& file_path, const std::string& file_path,
const std::function<bool(uint64_t, uint64_t)>& on_progress) { const std::function<bool(uint64_t, uint64_t)>& on_progress) {
// Even though this is much faster than writing zeroes, it is still slow // Even though this is much faster than writing zeroes, it is still slow
// enough that we need to fire the progress callback periodically. To // enough that we need to fire the progress callback periodically. To
// easily achieve this, we seek in chunks. We use 1000 chunks since // easily achieve this, we seek in chunks. We use 1000 chunks since
@ -280,22 +280,22 @@ static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_si
auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET)); auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
if (rv < 0) { if (rv < 0) {
PLOG(ERROR) << "Failed to lseek " << file_path; PLOG(ERROR) << "Failed to lseek " << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
if (rv != cursor - 1) { if (rv != cursor - 1) {
LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path; LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
return false; return FiemapStatus::Error();
} }
char buffer[] = {0}; char buffer[] = {0};
if (!android::base::WriteFully(file_fd, buffer, 1)) { if (!android::base::WriteFully(file_fd, buffer, 1)) {
PLOG(ERROR) << "Write failed: " << file_path; PLOG(ERROR) << "Write failed: " << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
if (on_progress && !on_progress(cursor, file_size)) { if (on_progress && !on_progress(cursor, file_size)) {
return false; return FiemapStatus::Error();
} }
} }
return true; return FiemapStatus::Ok();
} }
// F2FS-specific ioctl // F2FS-specific ioctl
@ -382,19 +382,19 @@ static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type)
// write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
// blocks are actually written to by the file system and thus getting rid of the holes in the // blocks are actually written to by the file system and thus getting rid of the holes in the
// file. // file.
static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz, static FiemapStatus WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
uint64_t file_size, uint64_t file_size,
const std::function<bool(uint64_t, uint64_t)>& on_progress) { const std::function<bool(uint64_t, uint64_t)>& on_progress) {
auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free); auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
if (buffer == nullptr) { if (buffer == nullptr) {
LOG(ERROR) << "failed to allocate memory for writing file"; LOG(ERROR) << "failed to allocate memory for writing file";
return false; return FiemapStatus::Error();
} }
off64_t offset = lseek64(file_fd, 0, SEEK_SET); off64_t offset = lseek64(file_fd, 0, SEEK_SET);
if (offset < 0) { if (offset < 0) {
PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path; PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
int permille = -1; int permille = -1;
@ -402,7 +402,7 @@ static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocks
if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) { if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
<< " in file " << file_path; << " in file " << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
offset += blocksz; offset += blocksz;
@ -412,7 +412,7 @@ static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocks
int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size; int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) { if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
if (on_progress && !on_progress(offset, file_size)) { if (on_progress && !on_progress(offset, file_size)) {
return false; return FiemapStatus::Error();
} }
permille = new_permille; permille = new_permille;
} }
@ -420,18 +420,18 @@ static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocks
if (lseek64(file_fd, 0, SEEK_SET) < 0) { if (lseek64(file_fd, 0, SEEK_SET) < 0) {
PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path; PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
return true; return FiemapStatus::Ok();
} }
// Reserve space for the file on the file system and write it out to make sure the extents // Reserve space for the file on the file system and write it out to make sure the extents
// don't come back unwritten. Return from this function with the kernel file offset set to 0. // don't come back unwritten. Return from this function with the kernel file offset set to 0.
// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
// aren't moved around. // aren't moved around.
static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz, static FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
uint64_t file_size, unsigned int fs_type, uint64_t file_size, unsigned int fs_type,
std::function<bool(uint64_t, uint64_t)> on_progress) { std::function<bool(uint64_t, uint64_t)> on_progress) {
bool need_explicit_writes = true; bool need_explicit_writes = true;
switch (fs_type) { switch (fs_type) {
case EXT4_SUPER_MAGIC: case EXT4_SUPER_MAGIC:
@ -439,11 +439,11 @@ static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blo
case F2FS_SUPER_MAGIC: { case F2FS_SUPER_MAGIC: {
bool supported; bool supported;
if (!F2fsPinBeforeAllocate(file_fd, &supported)) { if (!F2fsPinBeforeAllocate(file_fd, &supported)) {
return false; return FiemapStatus::Error();
} }
if (supported) { if (supported) {
if (!PinFile(file_fd, file_path, fs_type)) { if (!PinFile(file_fd, file_path, fs_type)) {
return false; return FiemapStatus::Error();
} }
need_explicit_writes = false; need_explicit_writes = false;
} }
@ -455,29 +455,32 @@ static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blo
return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress); return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
default: default:
LOG(ERROR) << "Missing fallocate() support for file system " << fs_type; LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
return false; return FiemapStatus::Error();
} }
if (fallocate(file_fd, 0, 0, file_size)) { if (fallocate(file_fd, 0, 0, file_size)) {
PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size; PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
return false; return FiemapStatus::FromErrno(errno);
} }
if (need_explicit_writes && !WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress)) { if (need_explicit_writes) {
return false; auto status = WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress);
if (!status.is_ok()) {
return status;
}
} }
// flush all writes here .. // flush all writes here ..
if (fsync(file_fd)) { if (fsync(file_fd)) {
PLOG(ERROR) << "Failed to synchronize written file:" << file_path; PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
return false; return FiemapStatus::FromErrno(errno);
} }
// Send one last progress notification. // Send one last progress notification.
if (on_progress && !on_progress(file_size, file_size)) { if (on_progress && !on_progress(file_size, file_size)) {
return false; return FiemapStatus::Error();
} }
return true; return FiemapStatus::Ok();
} }
bool FiemapWriter::HasPinnedExtents(const std::string& file_path) { bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
@ -671,6 +674,18 @@ static bool ReadFibmap(int file_fd, const std::string& file_path,
FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create, FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
std::function<bool(uint64_t, uint64_t)> progress) { std::function<bool(uint64_t, uint64_t)> progress) {
FiemapUniquePtr ret;
if (!Open(file_path, file_size, &ret, create, progress).is_ok()) {
return nullptr;
}
return ret;
}
FiemapStatus FiemapWriter::Open(const std::string& file_path, uint64_t file_size,
FiemapUniquePtr* out, bool create,
std::function<bool(uint64_t, uint64_t)> progress) {
out->reset();
// if 'create' is false, open an existing file and do not truncate. // if 'create' is false, open an existing file and do not truncate.
int open_flags = O_RDWR | O_CLOEXEC; int open_flags = O_RDWR | O_CLOEXEC;
if (create) { if (create) {
@ -683,43 +698,46 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR))); TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
if (file_fd < 0) { if (file_fd < 0) {
PLOG(ERROR) << "Failed to create file at: " << file_path; PLOG(ERROR) << "Failed to create file at: " << file_path;
return nullptr; return FiemapStatus::FromErrno(errno);
} }
std::string abs_path; std::string abs_path;
if (!::android::base::Realpath(file_path, &abs_path)) { if (!::android::base::Realpath(file_path, &abs_path)) {
int saved_errno = errno;
PLOG(ERROR) << "Invalid file path: " << file_path; PLOG(ERROR) << "Invalid file path: " << file_path;
cleanup(file_path, create); cleanup(file_path, create);
return nullptr; return FiemapStatus::FromErrno(saved_errno);
} }
std::string bdev_path; std::string bdev_path;
if (!GetBlockDeviceForFile(abs_path, &bdev_path)) { if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
LOG(ERROR) << "Failed to get block dev path for file: " << file_path; LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
cleanup(abs_path, create); cleanup(abs_path, create);
return nullptr; return FiemapStatus::Error();
} }
::android::base::unique_fd bdev_fd( ::android::base::unique_fd bdev_fd(
TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC))); TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
if (bdev_fd < 0) { if (bdev_fd < 0) {
int saved_errno = errno;
PLOG(ERROR) << "Failed to open block device: " << bdev_path; PLOG(ERROR) << "Failed to open block device: " << bdev_path;
cleanup(file_path, create); cleanup(file_path, create);
return nullptr; return FiemapStatus::FromErrno(saved_errno);
} }
uint64_t bdevsz; uint64_t bdevsz;
if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) { if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
int saved_errno = errno;
LOG(ERROR) << "Failed to get block device size for : " << bdev_path; LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
cleanup(file_path, create); cleanup(file_path, create);
return nullptr; return FiemapStatus::FromErrno(saved_errno);
} }
if (!create) { if (!create) {
file_size = GetFileSize(abs_path); file_size = GetFileSize(abs_path);
if (file_size == 0) { if (file_size == 0) {
LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path; LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
return nullptr; return FiemapStatus::FromErrno(errno);
} }
} }
@ -728,7 +746,7 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) { if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) {
LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path; LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
cleanup(abs_path, create); cleanup(abs_path, create);
return nullptr; return FiemapStatus::Error();
} }
// Align up to the nearest block size. // Align up to the nearest block size.
@ -737,11 +755,13 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
} }
if (create) { if (create) {
if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) { auto status =
AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress));
if (!status.is_ok()) {
LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
<< " bytes"; << " bytes";
cleanup(abs_path, create); cleanup(abs_path, create);
return nullptr; return status;
} }
} }
@ -749,7 +769,7 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
if (!PinFile(file_fd, abs_path, fs_type)) { if (!PinFile(file_fd, abs_path, fs_type)) {
cleanup(abs_path, create); cleanup(abs_path, create);
LOG(ERROR) << "Failed to pin the file in storage"; LOG(ERROR) << "Failed to pin the file in storage";
return nullptr; return FiemapStatus::Error();
} }
// now allocate the FiemapWriter and start setting it up // now allocate the FiemapWriter and start setting it up
@ -760,14 +780,14 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) { if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
LOG(ERROR) << "Failed to read fiemap of file: " << abs_path; LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
cleanup(abs_path, create); cleanup(abs_path, create);
return nullptr; return FiemapStatus::Error();
} }
break; break;
case MSDOS_SUPER_MAGIC: case MSDOS_SUPER_MAGIC:
if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) { if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
LOG(ERROR) << "Failed to read fibmap of file: " << abs_path; LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
cleanup(abs_path, create); cleanup(abs_path, create);
return nullptr; return FiemapStatus::Error();
} }
break; break;
} }
@ -781,7 +801,8 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s
LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device " LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
<< bdev_path; << bdev_path;
return fmap; *out = std::move(fmap);
return FiemapStatus::Ok();
} }
} // namespace fiemap } // namespace fiemap

View file

@ -193,7 +193,9 @@ TEST_F(FiemapWriterTest, FileDeletedOnError) {
} }
TEST_F(FiemapWriterTest, MaxBlockSize) { TEST_F(FiemapWriterTest, MaxBlockSize) {
ASSERT_GT(DetermineMaximumFileSize(testfile), 0); uint64_t max_piece_size = 0;
ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));
ASSERT_GT(max_piece_size, 0);
} }
TEST_F(FiemapWriterTest, FibmapBlockAddressing) { TEST_F(FiemapWriterTest, FibmapBlockAddressing) {

View file

@ -0,0 +1,60 @@
/*
* 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.
*/
#pragma once
#include <errno.h>
#include <stdint.h>
#include <string>
namespace android::fiemap {
// Represent error status of libfiemap classes.
class FiemapStatus {
public:
enum class ErrorCode : int32_t {
SUCCESS = 0,
// Generic non-recoverable failure.
ERROR = INT32_MIN,
// Not enough space
NO_SPACE = -ENOSPC,
};
// Create from a given errno (specified in errno,h)
static FiemapStatus FromErrno(int error_num) { return FiemapStatus(CastErrorCode(-error_num)); }
// Generic error.
static FiemapStatus Error() { return FiemapStatus(ErrorCode::ERROR); }
// Success.
static FiemapStatus Ok() { return FiemapStatus(ErrorCode::SUCCESS); }
ErrorCode error_code() const { return error_code_; }
bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
operator bool() const { return is_ok(); }
// For logging and debugging only.
std::string string() const;
private:
ErrorCode error_code_;
FiemapStatus(ErrorCode code) : error_code_(code) {}
static ErrorCode CastErrorCode(int error);
};
} // namespace android::fiemap

View file

@ -27,6 +27,8 @@
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <libfiemap/fiemap_status.h>
namespace android { namespace android {
namespace fiemap { namespace fiemap {
@ -47,6 +49,9 @@ class FiemapWriter final {
static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size, static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
bool create = true, bool create = true,
std::function<bool(uint64_t, uint64_t)> progress = {}); std::function<bool(uint64_t, uint64_t)> progress = {});
static FiemapStatus Open(const std::string& file_path, uint64_t file_size, FiemapUniquePtr* out,
bool create = true,
std::function<bool(uint64_t, uint64_t)> progress = {});
// Check that a file still has the same extents since it was last opened with FiemapWriter, // Check that a file still has the same extents since it was last opened with FiemapWriter,
// assuming the file was not resized outside of FiemapWriter. Returns false either on error // assuming the file was not resized outside of FiemapWriter. Returns false either on error

View file

@ -25,7 +25,8 @@
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include "fiemap_writer.h" #include <libfiemap/fiemap_status.h>
#include <libfiemap/fiemap_writer.h>
namespace android { namespace android {
namespace fiemap { namespace fiemap {
@ -43,6 +44,9 @@ class SplitFiemap final {
static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size, static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size, uint64_t max_piece_size,
ProgressCallback progress = {}); ProgressCallback progress = {});
static FiemapStatus Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
ProgressCallback progress = {});
// Open an existing split fiemap file. // Open an existing split fiemap file.
static std::unique_ptr<SplitFiemap> Open(const std::string& file_path); static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);

View file

@ -45,16 +45,28 @@ static const size_t kMaxFilePieces = 500;
std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size, std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size, uint64_t max_piece_size,
ProgressCallback progress) { ProgressCallback progress) {
std::unique_ptr<SplitFiemap> ret;
if (!Create(file_path, file_size, max_piece_size, &ret, progress).is_ok()) {
return nullptr;
}
return ret;
}
FiemapStatus SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
ProgressCallback progress) {
out_val->reset();
if (!file_size) { if (!file_size) {
LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path; LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
return nullptr; return FiemapStatus::Error();
} }
if (!max_piece_size) { if (!max_piece_size) {
max_piece_size = DetermineMaximumFileSize(file_path); auto status = DetermineMaximumFileSize(file_path, &max_piece_size);
if (!max_piece_size) { if (!status.is_ok()) {
LOG(ERROR) << "Could not determine maximum file size for " << file_path; LOG(ERROR) << "Could not determine maximum file size for " << file_path;
return nullptr; return status;
} }
} }
@ -75,7 +87,6 @@ std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, u
} }
return true; return true;
}; };
std::unique_ptr<SplitFiemap> out(new SplitFiemap()); std::unique_ptr<SplitFiemap> out(new SplitFiemap());
out->creating_ = true; out->creating_ = true;
out->list_file_ = file_path; out->list_file_ = file_path;
@ -85,14 +96,17 @@ std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, u
while (remaining_bytes) { while (remaining_bytes) {
if (out->files_.size() >= kMaxFilePieces) { if (out->files_.size() >= kMaxFilePieces) {
LOG(ERROR) << "Requested size " << file_size << " created too many split files"; LOG(ERROR) << "Requested size " << file_size << " created too many split files";
return nullptr; out.reset();
return FiemapStatus::Error();
} }
std::string chunk_path = std::string chunk_path =
android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size()); android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
uint64_t chunk_size = std::min(max_piece_size, remaining_bytes); uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress); FiemapUniquePtr writer;
if (!writer) { auto status = FiemapWriter::Open(chunk_path, chunk_size, &writer, true, on_progress);
return nullptr; if (!status.is_ok()) {
out.reset();
return status;
} }
// To make sure the alignment doesn't create too much inconsistency, we // To make sure the alignment doesn't create too much inconsistency, we
@ -110,20 +124,23 @@ std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, u
unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660)); unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
if (fd < 0) { if (fd < 0) {
PLOG(ERROR) << "Failed to open " << file_path; PLOG(ERROR) << "Failed to open " << file_path;
return nullptr; out.reset();
return FiemapStatus::FromErrno(errno);
} }
for (const auto& writer : out->files_) { for (const auto& writer : out->files_) {
std::string line = android::base::Basename(writer->file_path()) + "\n"; std::string line = android::base::Basename(writer->file_path()) + "\n";
if (!android::base::WriteFully(fd, line.data(), line.size())) { if (!android::base::WriteFully(fd, line.data(), line.size())) {
PLOG(ERROR) << "Write failed " << file_path; PLOG(ERROR) << "Write failed " << file_path;
return nullptr; out.reset();
return FiemapStatus::FromErrno(errno);
} }
} }
// Unset this bit, so we don't unlink on destruction. // Unset this bit, so we don't unlink on destruction.
out->creating_ = false; out->creating_ = false;
return out; *out_val = std::move(out);
return FiemapStatus::Ok();
} }
std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) { std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {

View file

@ -37,29 +37,30 @@ using android::base::unique_fd;
static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata"; static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
uint64_t DetermineMaximumFileSize(const std::string& file_path) { FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
// Create the smallest file possible (one block). // Create the smallest file possible (one block).
auto writer = FiemapWriter::Open(file_path, 1); FiemapUniquePtr writer;
if (!writer) { auto status = FiemapWriter::Open(file_path, 1, &writer);
return 0; if (!status.is_ok()) {
return status;
} }
uint64_t result = 0; *result = 0;
switch (writer->fs_type()) { switch (writer->fs_type()) {
case EXT4_SUPER_MAGIC: case EXT4_SUPER_MAGIC:
// The minimum is 16GiB, so just report that. If we wanted we could parse the // 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. // superblock and figure out if 64-bit support is enabled.
result = 17179869184ULL; *result = 17179869184ULL;
break; break;
case F2FS_SUPER_MAGIC: case F2FS_SUPER_MAGIC:
// Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
// 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB. // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
result = 4329690886144ULL; *result = 4329690886144ULL;
break; break;
case MSDOS_SUPER_MAGIC: case MSDOS_SUPER_MAGIC:
// 4GB-1, which we want aligned to the block size. // 4GB-1, which we want aligned to the block size.
result = 4294967295; *result = 4294967295;
result -= (result % writer->block_size()); *result -= (*result % writer->block_size());
break; break;
default: default:
LOG(ERROR) << "Unknown file system type: " << writer->fs_type(); LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
@ -70,7 +71,7 @@ uint64_t DetermineMaximumFileSize(const std::string& file_path) {
writer = nullptr; writer = nullptr;
unlink(file_path.c_str()); unlink(file_path.c_str());
return result; return FiemapStatus::Ok();
} }
// Given a SplitFiemap, this returns a device path that will work during first- // Given a SplitFiemap, this returns a device path that will work during first-

View file

@ -28,7 +28,7 @@ namespace fiemap {
// Given a file that will be created, determine the maximum size its containing // Given a file that will be created, determine the maximum size its containing
// filesystem allows. Note this is a theoretical maximum size; free space is // filesystem allows. Note this is a theoretical maximum size; free space is
// ignored entirely. // ignored entirely.
uint64_t DetermineMaximumFileSize(const std::string& file_path); FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result);
// Given a SplitFiemap, this returns a device path that will work during first- // Given a SplitFiemap, this returns a device path that will work during first-
// stage init (i.e., its path can be found by InitRequiredDevices). // stage init (i.e., its path can be found by InitRequiredDevices).