Fix erofs-utils 1.7 compatibility

Test: th
Change-Id: I273fb8937e584fadf8fe7205be75c049a474bdba
diff --git a/common/utils.h b/common/utils.h
index 6c6337f..0c8c13f 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -642,9 +642,10 @@
   }
 } deferrer;
 
-#define TOKENPASTE(x, y) x##y
-#define DEFER                                                    \
-  auto TOKENPASTE(_deferred_lambda_call, __COUNTER__) = deferrer \
-                                                        << [&]() mutable
+#define TOKENPASTE1(x, y) x##y
+#define TOKENPASTE2(x, y) TOKENPASTE1(x, y)
+#define DEFER                                                     \
+  auto TOKENPASTE2(_deferred_lambda_call, __COUNTER__) = deferrer \
+                                                         << [&]() mutable
 
 #endif  // UPDATE_ENGINE_COMMON_UTILS_H_
diff --git a/lz4diff/lz4diff_compress_unittest.cc b/lz4diff/lz4diff_compress_unittest.cc
index b4b56d2..d05c6be 100644
--- a/lz4diff/lz4diff_compress_unittest.cc
+++ b/lz4diff/lz4diff_compress_unittest.cc
@@ -47,21 +47,20 @@
 static void ExtractErofsImage(const char* erofs_image,
                               const char* inode_path,
                               Blob* output) {
-  // EROFS has plenty of global variable usage. Protect calls to EROFS APIs with
-  // global mutex.
-  // TODO(b/202784930) Replace erofs-utils with a cleaner and more C++ friendly
-  // library. (Or turn erofs-utils into one)
-  static std::mutex mutex;
-  std::lock_guard lock(mutex);
-  auto err = dev_open_ro(erofs_image);
+  struct erofs_sb_info sbi {};
+  auto err = dev_open_ro(&sbi, erofs_image);
   ASSERT_EQ(err, 0);
-  DEFER { dev_close(); };
+  DEFER {
+    dev_close(&sbi);
+  };
 
-  err = erofs_read_superblock();
+  err = erofs_read_superblock(&sbi);
   ASSERT_EQ(err, 0);
-  struct erofs_inode inode;
+  struct erofs_inode inode {
+    .sbi = &sbi
+  };
   err = erofs_ilookup(inode_path, &inode);
-  ASSERT_EQ(err, 0);
+  ASSERT_EQ(err, 0) << strerror(-err);
   output->resize(inode.i_size);
   err = erofs_pread(&inode,
                     reinterpret_cast<char*>(output->data()),
diff --git a/payload_generator/erofs_filesystem.cc b/payload_generator/erofs_filesystem.cc
index 508c9a1..32a5fc5 100644
--- a/payload_generator/erofs_filesystem.cc
+++ b/payload_generator/erofs_filesystem.cc
@@ -28,6 +28,7 @@
 #include <erofs/dir.h>
 #include <erofs/io.h>
 #include <erofs_fs.h>
+#include <erofs/internal.h>
 
 #include "erofs_iterate.h"
 #include "lz4diff/lz4diff.pb.h"
@@ -43,6 +44,7 @@
 namespace {
 
 static constexpr int GetOccupiedSize(const struct erofs_inode* inode,
+                                     size_t block_size,
                                      erofs_off_t* size) {
   *size = 0;
   switch (inode->datalayout) {
@@ -51,9 +53,9 @@
     case EROFS_INODE_CHUNK_BASED:
       *size = inode->i_size;
       break;
-    case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
-    case EROFS_INODE_FLAT_COMPRESSION:
-      *size = inode->u.i_blocks * EROFS_BLKSIZ;
+    case EROFS_INODE_COMPRESSED_FULL:
+    case EROFS_INODE_COMPRESSED_COMPACT:
+      *size = inode->u.i_blocks * block_size;
       break;
     default:
       LOG(ERROR) << "unknown datalayout " << inode->datalayout;
@@ -166,48 +168,40 @@
 
 }  // namespace
 
-static_assert(kBlockSize == EROFS_BLKSIZ);
-
 std::unique_ptr<ErofsFilesystem> ErofsFilesystem::CreateFromFile(
     const std::string& filename, const CompressionAlgorithm& algo) {
   if (!IsErofsImage(filename.c_str())) {
     return {};
   }
-  // erofs-utils makes heavy use of global variables. Hence its functions aren't
-  // thread safe. For example, it stores a global int holding file descriptors
-  // to the opened EROFS image. It doesn't even support opening more than 1
-  // imaeg at a time.
-  // TODO(b/202784930) Replace erofs-utils with a cleaner and more C++ friendly
-  // library. (Or turn erofs-utils into one)
-  static std::mutex m;
-  std::lock_guard g{m};
+  struct erofs_sb_info sbi {};
 
-  if (const auto err = dev_open_ro(filename.c_str()); err) {
+  if (const auto err = dev_open_ro(&sbi, filename.c_str()); err) {
     PLOG(INFO) << "Failed to open " << filename;
     return nullptr;
   }
   DEFER {
-    dev_close();
+    dev_close(&sbi);
   };
 
-  if (const auto err = erofs_read_superblock(); err) {
+  if (const auto err = erofs_read_superblock(&sbi); err) {
     PLOG(INFO) << "Failed to parse " << filename << " as EROFS image";
     return nullptr;
   }
+  const auto block_size = 1UL << sbi.blkszbits;
   struct stat st {};
-  if (const auto err = fstat(erofs_devfd, &st); err) {
+  if (const auto err = fstat(sbi.devfd, &st); err) {
     PLOG(ERROR) << "Failed to stat() " << filename;
     return nullptr;
   }
   const time_t time = sbi.build_time;
   std::vector<File> files;
-  if (!ErofsFilesystem::GetFiles(filename, &files, algo)) {
-    return nullptr;
-  }
+  CHECK(ErofsFilesystem::GetFiles(&sbi, filename, &files, algo))
+      << "Failed to parse EROFS image " << filename;
 
   LOG(INFO) << "Parsed EROFS image of size " << st.st_size << " built in "
             << ctime(&time) << " " << filename
-            << ", number of files: " << files.size();
+            << ", number of files: " << files.size()
+            << ", block size: " << block_size;
   LOG(INFO) << "Using compression algo " << algo << " for " << filename;
   // private ctor doesn't work with make_unique
   return std::unique_ptr<ErofsFilesystem>(
@@ -219,51 +213,59 @@
   return true;
 }
 
-bool ErofsFilesystem::GetFiles(const std::string& filename,
+bool ErofsFilesystem::GetFiles(struct erofs_sb_info* sbi,
+                               const std::string& filename,
                                std::vector<File>* files,
                                const CompressionAlgorithm& algo) {
   size_t unaligned_bytes = 0;
-  erofs_iterate_root_dir(&sbi, [&](struct erofs_iterate_dir_context* p_info) {
-    const auto& info = *p_info;
-    if (info.ctx.de_ftype != EROFS_FT_REG_FILE) {
-      return 0;
-    }
-    struct erofs_inode inode {};
-    inode.nid = info.ctx.de_nid;
-    int err = erofs_read_inode_from_disk(&inode);
-    if (err) {
-      LOG(ERROR) << "Failed to read inode " << inode.nid;
-      return err;
-    }
-    const auto uncompressed_size = inode.i_size;
-    erofs_off_t compressed_size = 0;
-    if (uncompressed_size == 0) {
-      return 0;
-    }
-    err = GetOccupiedSize(&inode, &compressed_size);
-    if (err) {
-      LOG(FATAL) << "Failed to get occupied size for " << filename;
-      return err;
-    }
-    // For EROFS_INODE_FLAT_INLINE , most blocks are stored on aligned
-    // addresses. Except the last block, which is stored right after the
-    // inode. These nodes will have a slight amount of data unaligned, which
-    // is fine.
+  const auto block_size = 1UL << sbi->blkszbits;
+  const auto err = erofs_iterate_root_dir(
+      sbi, [&](struct erofs_iterate_dir_context* p_info) {
+        const auto& info = *p_info;
+        if (info.ctx.de_ftype != EROFS_FT_REG_FILE) {
+          return 0;
+        }
+        struct erofs_inode inode {};
+        inode.nid = info.ctx.de_nid;
+        inode.sbi = sbi;
+        int err = erofs_read_inode_from_disk(&inode);
+        if (err) {
+          LOG(ERROR) << "Failed to read inode " << inode.nid;
+          return err;
+        }
+        const auto uncompressed_size = inode.i_size;
+        erofs_off_t compressed_size = 0;
+        if (uncompressed_size == 0) {
+          return 0;
+        }
+        err = GetOccupiedSize(&inode, block_size, &compressed_size);
+        if (err) {
+          LOG(FATAL) << "Failed to get occupied size for " << filename;
+          return err;
+        }
+        // For EROFS_INODE_FLAT_INLINE , most blocks are stored on aligned
+        // addresses. Except the last block, which is stored right after the
+        // inode. These nodes will have a slight amount of data unaligned, which
+        // is fine.
 
-    File file;
-    file.name = info.path;
-    file.compressed_file_info.zero_padding_enabled =
-        erofs_sb_has_lz4_0padding();
-    file.is_compressed = compressed_size != uncompressed_size;
+        File file;
+        file.name = info.path;
+        file.compressed_file_info.zero_padding_enabled =
+            erofs_sb_has_lz4_0padding(sbi);
+        file.is_compressed = compressed_size != uncompressed_size;
 
-    file.file_stat.st_size = uncompressed_size;
-    file.file_stat.st_ino = inode.nid;
-    FillExtentInfo(&file, filename, &inode, &unaligned_bytes);
-    file.compressed_file_info.algo = algo;
+        file.file_stat.st_size = uncompressed_size;
+        file.file_stat.st_ino = inode.nid;
+        FillExtentInfo(&file, filename, &inode, &unaligned_bytes);
+        file.compressed_file_info.algo = algo;
 
-    files->emplace_back(std::move(file));
-    return 0;
-  });
+        files->emplace_back(std::move(file));
+        return 0;
+      });
+  if (err) {
+    LOG(ERROR) << "EROFS files iteration filed " << strerror(-err);
+    return false;
+  }
 
   for (auto& file : *files) {
     NormalizeExtents(&file.extents);
diff --git a/payload_generator/erofs_filesystem.h b/payload_generator/erofs_filesystem.h
index d0abcb3..b10fc98 100644
--- a/payload_generator/erofs_filesystem.h
+++ b/payload_generator/erofs_filesystem.h
@@ -20,6 +20,8 @@
 #include "update_engine/payload_generator/filesystem_interface.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 
+struct erofs_sb_info;
+
 namespace chromeos_update_engine {
 
 class ErofsFilesystem final : public FilesystemInterface {
@@ -50,7 +52,8 @@
   //    space.
   //  <metadata>: With the rest of ext2 metadata blocks, such as superblocks
   //    and bitmap tables.
-  static bool GetFiles(const std::string& filename,
+  static bool GetFiles(struct erofs_sb_info* sbi,
+                       const std::string& filename,
                        std::vector<File>* files,
                        const CompressionAlgorithm& algo);
 
diff --git a/payload_generator/erofs_iterate.h b/payload_generator/erofs_iterate.h
index ac9d67e..ef37418 100644
--- a/payload_generator/erofs_iterate.h
+++ b/payload_generator/erofs_iterate.h
@@ -13,6 +13,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_EROFS_ITERATE_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_EROFS_ITERATE_H_
 
 #include <string>
 
@@ -36,19 +38,24 @@
 // Callable shold be a functor like
 // std::function<int(struct erofs_inode_info *)>
 template <typename Callable>
-int erofs_iterate_root_dir(const struct erofs_sb_info* sbi, Callable cb) {
-  struct erofs_inode dir {
-    .nid = sbi->root_nid
+int erofs_iterate_root_dir(struct erofs_sb_info* sbi, Callable cb) {
+  CHECK_NE(sbi, nullptr);
+  struct erofs_inode root_dir {
+    .sbi = sbi, .nid = sbi->root_nid
   };
-  int err = erofs_read_inode_from_disk(&dir);
+  int err = erofs_read_inode_from_disk(&root_dir);
   if (err) {
-    LOG(ERROR) << "Failed to read inode " << sbi->root_nid << " from disk";
+    LOG(ERROR) << "Failed to read inode " << sbi->root_nid << " from disk "
+               << strerror(-err);
     return err;
   }
   struct erofs_iterate_dir_context param {
-    .ctx.dir = &dir, .ctx.pnid = sbi->root_nid,
+    .ctx.dir = &root_dir, .ctx.pnid = sbi->root_nid,
     .ctx.cb = [](struct erofs_dir_context* arg) -> int {
       auto ctx = reinterpret_cast<erofs_iterate_dir_context*>(arg);
+      const auto parent_dir = ctx->ctx.dir;
+      const auto sbi = ctx->ctx.dir->sbi;
+      CHECK_NE(sbi, nullptr);
       auto& path = ctx->path;
       const auto len = path.size();
       path.push_back('/');
@@ -58,14 +65,21 @@
       const auto err = (*cb)(ctx);
       if (!err && !ctx->ctx.dot_dotdot && ctx->ctx.de_ftype == EROFS_FT_DIR) {
         // recursively walk into subdirectories
-        erofs_inode dir{.nid = ctx->ctx.de_nid};
+        struct erofs_inode dir {
+          .sbi = sbi, .nid = ctx->ctx.de_nid
+        };
         if (const int err = erofs_read_inode_from_disk(&dir); err) {
+          LOG(FATAL) << "Failed to erofs_read_inode_from_disk("
+                     << ctx->ctx.de_nid << ") " << strerror(-err);
           return err;
         }
         ctx->ctx.dir = &dir;
         if (const auto err = erofs_iterate_dir(&ctx->ctx, false); err) {
+          LOG(FATAL) << "Failed to erofs_iterate_dir(" << ctx->ctx.de_nid
+                     << ") " << strerror(-err);
           return err;
         }
+        ctx->ctx.dir = parent_dir;
       }
       path.resize(len);
       return err;
@@ -74,3 +88,5 @@
   };
   return erofs_iterate_dir(&param.ctx, false);
 }
+
+#endif