diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp index 71b39a929..164fc9145 100644 --- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp +++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp @@ -220,7 +220,7 @@ static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, } static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz, - uint64_t file_size) { + uint64_t file_size, std::function on_progress) { // 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. // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks @@ -245,12 +245,22 @@ static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blo return false; } + int permille = -1; for (; offset < file_size; offset += blocksz) { if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) { PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset << " in file " << file_path; return false; } + // Don't invoke the callback every iteration - wait until a significant + // chunk (here, 1/1000th) of the data has been processed. + int new_permille = (static_cast(offset) * 1000) / file_size; + if (new_permille != permille) { + if (on_progress && !on_progress(offset, file_size)) { + return false; + } + permille = new_permille; + } } if (lseek64(file_fd, 0, SEEK_SET) < 0) { @@ -264,6 +274,10 @@ static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blo return false; } + // Send one last progress notification. + if (on_progress && !on_progress(file_size, file_size)) { + return false; + } return true; } @@ -412,7 +426,8 @@ static bool ReadFiemap(int file_fd, const std::string& file_path, return last_extent_seen; } -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 progress) { // if 'create' is false, open an existing file and do not truncate. int open_flags = O_RDWR | O_CLOEXEC; if (create) { @@ -474,7 +489,7 @@ FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_s } if (create) { - if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) { + if (!AllocateFile(file_fd, abs_path, blocksz, file_size, std::move(progress))) { LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size << " bytes"; cleanup(abs_path, create); diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp index 6dff0e8b4..51015378c 100644 --- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp +++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp @@ -83,6 +83,25 @@ TEST_F(FiemapWriterTest, CheckFilePath) { EXPECT_EQ(access(testfile.c_str(), F_OK), 0); } +TEST_F(FiemapWriterTest, CheckProgress) { + std::vector expected{ + 0, + 4096, + }; + size_t invocations = 0; + auto callback = [&](uint64_t done, uint64_t total) -> bool { + EXPECT_LT(invocations, expected.size()); + EXPECT_EQ(done, expected[invocations]); + EXPECT_EQ(total, 4096); + invocations++; + return true; + }; + + auto ptr = FiemapWriter::Open(testfile, 4096, true, std::move(callback)); + EXPECT_NE(ptr, nullptr); + EXPECT_EQ(invocations, 2); +} + TEST_F(FiemapWriterTest, CheckBlockDevicePath) { FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096); EXPECT_EQ(fptr->size(), 4096); diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h index ae61344b6..ab78f9361 100644 --- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h +++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -36,9 +37,13 @@ class FiemapWriter final { public: // Factory method for FiemapWriter. // The method returns FiemapUniquePtr that contains all the data necessary to be able to write - // to the given file directly using raw block i/o. + // to the given file directly using raw block i/o. The optional progress callback will be + // invoked, if create is true, while the file is being initialized. It receives the bytes + // written and the number of total bytes. If the callback returns false, the operation will + // fail. static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size, - bool create = true); + bool create = true, + std::function progress = {}); // Syncs block device writes. bool Flush() const;