[PATCH v2 2/2] Add API to iterate over inodes in EROFS
Kelvin Zhang
zhangkelvin at google.com
Thu Dec 9 12:21:18 AEDT 2021
Change-Id: Ia35708080a72ee204eaaddfc670d3cb8023a078c
Signed-off-by: Kelvin Zhang <zhangkelvin at google.com>
---
include/erofs/iterate.h | 57 +++++++++++++
lib/Makefile.am | 2 +-
lib/iterate.c | 173 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 231 insertions(+), 1 deletion(-)
create mode 100644 include/erofs/iterate.h
create mode 100644 lib/iterate.c
diff --git a/include/erofs/iterate.h b/include/erofs/iterate.h
new file mode 100644
index 0000000..96171a7
--- /dev/null
+++ b/include/erofs/iterate.h
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2021 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.
+//
+
+#ifndef ITERATE_ITERATE
+#define ITERATE_ITERATE
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#include "erofs/io.h"
+#include "erofs/print.h"
+
+
+struct erofs_inode_info {
+ uint64_t inode_id;
+ const char* name;
+ bool is_reg_file;
+ u64 compressed_size;
+ u64 uncompressed_size;
+ struct erofs_inode* inode;
+ void* arg;
+};
+// Callback function for iterating over inodes of EROFS
+
+typedef bool (*ErofsIterCallback)(struct erofs_inode_info);
+
+int erofs_iterate_dir(const struct erofs_sb_info* sbi,
+ erofs_nid_t nid,
+ erofs_nid_t parent_nid,
+ ErofsIterCallback cb,
+ void* arg);
+int erofs_iterate_root_dir(const struct erofs_sb_info* sbi,
+ ErofsIterCallback cbg,
+ void* arg);
+int erofs_get_occupied_size(struct erofs_inode* inode, erofs_off_t* size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ITERATE_ITERATE
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 67ba798..20c0e4f 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -27,7 +27,7 @@ noinst_HEADERS = $(top_srcdir)/include/erofs_fs.h \
noinst_HEADERS += compressor.h
liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \
namei.c data.c compress.c compressor.c zmap.c decompress.c \
- compress_hints.c hashmap.c sha256.c blobchunk.c
+ compress_hints.c hashmap.c sha256.c blobchunk.c iterate.c
liberofs_la_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
if ENABLE_LZ4
liberofs_la_CFLAGS += ${LZ4_CFLAGS}
diff --git a/lib/iterate.c b/lib/iterate.c
new file mode 100644
index 0000000..1a10ec1
--- /dev/null
+++ b/lib/iterate.c
@@ -0,0 +1,173 @@
+//
+// Copyright (C) 2021 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 "erofs/internal.h"
+#include "erofs_fs.h"
+#include "erofs/print.h"
+#include "erofs/iterate.h"
+
+static int erofs_read_dirent(const struct erofs_sb_info* sbi,
+ const struct erofs_dirent* de,
+ erofs_nid_t nid,
+ erofs_nid_t parent_nid,
+ const char* dname,
+ ErofsIterCallback cb,
+ void* arg) {
+ int err;
+ erofs_off_t occupied_size = 0;
+ struct erofs_inode inode = {.nid = de->nid};
+ err = erofs_read_inode_from_disk(&inode);
+ if (err) {
+ erofs_err("read file inode from disk failed!");
+ return err;
+ }
+ err = erofs_get_occupied_size(&inode, &occupied_size);
+ if (err) {
+ erofs_err("get file size failed\n");
+ return err;
+ }
+ char buf[PATH_MAX + 1];
+ erofs_get_inode_name(sbi, de->nid, buf, PATH_MAX + 1);
+ struct erofs_inode_info info = {
+ .inode_id = de->nid,
+ .name = buf,
+ .is_reg_file = de->file_type == EROFS_FT_REG_FILE,
+ .compressed_size = occupied_size,
+ .uncompressed_size = inode.i_size,
+ .inode = &inode,
+ .arg = arg};
+ cb(info);
+ if ((de->file_type == EROFS_FT_DIR) && de->nid != nid &&
+ de->nid != parent_nid) {
+ err = erofs_iterate_dir(sbi, de->nid, nid, cb, arg);
+ if (err) {
+ erofs_err("parse dir nid %u error occurred\n",
+ (unsigned int)(de->nid));
+ return err;
+ }
+ }
+ return 0;
+}
+
+static inline int erofs_checkdirent(const struct erofs_dirent* de,
+ const struct erofs_dirent* last_de,
+ u32 maxsize,
+ const char* dname) {
+ int dname_len;
+ unsigned int nameoff = le16_to_cpu(de->nameoff);
+ if (nameoff < sizeof(struct erofs_dirent) || nameoff >= PAGE_SIZE) {
+ erofs_err("invalid de[0].nameoff %u @ nid %llu", nameoff, de->nid | 0ULL);
+ return -EFSCORRUPTED;
+ }
+ dname_len = (de + 1 >= last_de) ? strnlen(dname, maxsize - nameoff)
+ : le16_to_cpu(de[1].nameoff) - nameoff;
+ /* a corrupted entry is found */
+ if (nameoff + dname_len > maxsize || dname_len > EROFS_NAME_LEN) {
+ erofs_err("bogus dirent @ nid %llu", le64_to_cpu(de->nid) | 0ULL);
+ DBG_BUGON(1);
+ return -EFSCORRUPTED;
+ }
+ if (de->file_type >= EROFS_FT_MAX) {
+ erofs_err("invalid file type %u", (unsigned int)(de->nid));
+ return -EFSCORRUPTED;
+ }
+ return dname_len;
+}
+
+int erofs_iterate_dir(const struct erofs_sb_info* sbi,
+ erofs_nid_t nid,
+ erofs_nid_t parent_nid,
+ ErofsIterCallback cb,
+ void* arg) {
+ int err;
+ erofs_off_t offset;
+ char buf[EROFS_BLKSIZ];
+ struct erofs_inode vi = {.nid = nid};
+ err = erofs_read_inode_from_disk(&vi);
+ if (err)
+ return err;
+ struct erofs_inode_info inode_info = {
+ .inode_id = nid,
+ .name = buf,
+ .is_reg_file = false,
+ .compressed_size = vi.i_size,
+ .uncompressed_size = vi.i_size,
+ .inode = &vi,
+ .arg = arg,
+ };
+ err = erofs_get_inode_name(sbi, nid, buf, EROFS_BLKSIZ);
+ cb(inode_info);
+ if (err) {
+ return err;
+ }
+ offset = 0;
+ while (offset < vi.i_size) {
+ erofs_off_t maxsize = min_t(erofs_off_t, vi.i_size - offset, EROFS_BLKSIZ);
+ const struct erofs_dirent* de = (const struct erofs_dirent*)(buf);
+ struct erofs_dirent* end;
+ unsigned int nameoff;
+ err = erofs_pread(&vi, buf, maxsize, offset);
+ if (err)
+ return err;
+ nameoff = le16_to_cpu(de->nameoff);
+ end = (struct erofs_dirent*)(buf + nameoff);
+ while (de < end) {
+ const char* dname;
+ int ret;
+ /* skip "." and ".." dentry */
+ if (de->nid == nid || de->nid == parent_nid) {
+ de++;
+ continue;
+ }
+ dname = (char*)buf + nameoff;
+ ret = erofs_checkdirent(de, end, maxsize, dname);
+ if (ret < 0)
+ return ret;
+ ret = erofs_read_dirent(sbi, de, nid, parent_nid, dname, cb, arg);
+ if (ret < 0)
+ return ret;
+ ++de;
+ }
+ offset += maxsize;
+ }
+ return 0;
+}
+
+int erofs_get_occupied_size(struct erofs_inode* inode, erofs_off_t* size) {
+ *size = 0;
+ switch (inode->datalayout) {
+ case EROFS_INODE_FLAT_INLINE:
+ case EROFS_INODE_FLAT_PLAIN:
+ 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;
+ break;
+ default:
+ erofs_err("unknown datalayout");
+ return -1;
+ }
+ return 0;
+}
+
+int erofs_iterate_root_dir(const struct erofs_sb_info* sbi,
+ ErofsIterCallback cb,
+ void* arg) {
+ return erofs_iterate_dir(sbi, sbi->root_nid, sbi->root_nid, cb, arg);
+}
+
--
2.34.1.173.g76aa8bc2d0-goog
More information about the Linux-erofs
mailing list