[PATCH] erofs-utils: introduce fuse framework
Li Guifu
blucerlee at gmail.com
Sun Aug 18 23:59:23 AEST 2019
It builds a erofsfuse in the fuse directory.
Using erofsfuse <images> <mount dir> to achive it.
It only uspport read a regular file which is inline or not.
Compressed file need another patch to finish it
Signed-off-by: Li Guifu <blucerlee at gmail.com>
---
Makefile.am | 2 +-
README | 28 ++++-
configure.ac | 3 +-
fuse/Makefile.am | 14 +++
fuse/dentry.c | 129 ++++++++++++++++++++++
fuse/dentry.h | 42 ++++++++
fuse/disk_io.c | 72 +++++++++++++
fuse/disk_io.h | 21 ++++
fuse/getattr.c | 64 +++++++++++
fuse/getattr.h | 15 +++
fuse/init.c | 98 +++++++++++++++++
fuse/init.h | 22 ++++
fuse/logging.c | 81 ++++++++++++++
fuse/logging.h | 55 ++++++++++
fuse/main.c | 170 +++++++++++++++++++++++++++++
fuse/namei.c | 225 +++++++++++++++++++++++++++++++++++++++
fuse/namei.h | 22 ++++
fuse/open.c | 22 ++++
fuse/open.h | 15 +++
fuse/read.c | 114 ++++++++++++++++++++
fuse/read.h | 16 +++
fuse/readir.c | 121 +++++++++++++++++++++
fuse/readir.h | 17 +++
include/erofs/defs.h | 3 +
include/erofs/internal.h | 37 +++++++
25 files changed, 1405 insertions(+), 3 deletions(-)
create mode 100644 fuse/Makefile.am
create mode 100644 fuse/dentry.c
create mode 100644 fuse/dentry.h
create mode 100644 fuse/disk_io.c
create mode 100644 fuse/disk_io.h
create mode 100644 fuse/getattr.c
create mode 100644 fuse/getattr.h
create mode 100644 fuse/init.c
create mode 100644 fuse/init.h
create mode 100644 fuse/logging.c
create mode 100644 fuse/logging.h
create mode 100644 fuse/main.c
create mode 100644 fuse/namei.c
create mode 100644 fuse/namei.h
create mode 100644 fuse/open.c
create mode 100644 fuse/open.h
create mode 100644 fuse/read.c
create mode 100644 fuse/read.h
create mode 100644 fuse/readir.c
create mode 100644 fuse/readir.h
diff --git a/Makefile.am b/Makefile.am
index d94ab73..7e17994 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,4 +3,4 @@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS=lib mkfs
+SUBDIRS=lib mkfs fuse
diff --git a/README b/README
index 64c1e54..5765fff 100644
--- a/README
+++ b/README
@@ -2,7 +2,8 @@ erofs-utils
===========
erofs-utils includes user-space tools for erofs filesystem images.
-Currently only mkfs.erofs is available.
+One is mkfs.erofs to create a image, the other is erofsfuse which
+is used to mount a image on a directory
mkfs.erofs
----------
@@ -80,6 +81,31 @@ It may still be useful since new erofs-utils has not been widely used in
commercial products. However, if that happens, please report bug to us
as well.
+erofs-utils: erofsfuse
+----------------------
+erofsfuse mount a erofs filesystem image created by mkfs.erofs to a directory, and then
+It can be listed, read files and so on.
+
+Dependencies
+~~~~~~~~~~~~
+FUSE library version: 2.9.7
+fusermount version: 2.9.7
+using FUSE kernel interface version 7.19
+
+How to installed fuse
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+sudo apt-get update
+sudo apt-get install -y fuse libfuse-dev
+
+How to build erofsfuse
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ $ ./autogen.sh
+ $ ./configure
+ $ make
+
+erofsfuse binary will be generated under fuse folder.
+
Contribution
------------
diff --git a/configure.ac b/configure.ac
index 6f4eacc..a8eb598 100644
--- a/configure.ac
+++ b/configure.ac
@@ -169,6 +169,7 @@ AM_CONDITIONAL([ENABLE_LZ4HC], [test "x${have_lz4hc}" = "xyes"])
AC_CONFIG_FILES([Makefile
lib/Makefile
- mkfs/Makefile])
+ mkfs/Makefile
+ fuse/Makefile])
AC_OUTPUT
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
new file mode 100644
index 0000000..fffd67a
--- /dev/null
+++ b/fuse/Makefile.am
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Makefile.am
+
+AUTOMAKE_OPTIONS = foreign
+bin_PROGRAMS = erofsfuse
+erofsfuse_SOURCES = main.c dentry.c getattr.c logging.c namei.c read.c disk_io.c init.c open.c readir.c
+erofsfuse_CFLAGS = -Wall -Werror -Wextra \
+ -I$(top_srcdir)/include \
+ $(shell pkg-config fuse --cflags) \
+ -DFUSE_USE_VERSION=26 \
+ -std=gnu99
+LDFLAGS += $(shell pkg-config fuse --libs)
+erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la -ldl
+
diff --git a/fuse/dentry.c b/fuse/dentry.c
new file mode 100644
index 0000000..0569ff5
--- /dev/null
+++ b/fuse/dentry.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\dentry.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "dentry.h"
+#include "erofs/internal.h"
+#include "logging.h"
+
+#define DCACHE_ENTRY_CALLOC() calloc(1, sizeof(struct dcache_entry))
+#define DCACHE_ENTRY_LIFE 8
+
+struct dcache_entry root_entry;
+
+int dcache_init_root(uint32_t nid)
+{
+ if (root_entry.nid)
+ return -1;
+
+ /* Root entry doesn't need most of the fields. Namely, it only uses the
+ * nid field and the subdirs pointer.
+ */
+ logi("Initializing root_entry dcache entry");
+ root_entry.nid = nid;
+ root_entry.subdirs = NULL;
+ root_entry.siblings = NULL;
+
+ return 0;
+}
+
+/* Inserts a node as a subdirs of a given parent. The parent is updated to
+ * point the newly inserted subdirs as the first subdirs. We return the new
+ * entry so that further entries can be inserted.
+ *
+ * [0] [0]
+ * / ==> \
+ * / ==> \
+ * .->[1]->[2]-. .->[1]->[3]->[2]-.
+ * `-----------´ `----------------´
+ */
+struct dcache_entry *dcache_insert(struct dcache_entry *parent,
+ const char *name, int namelen, uint32_t nid)
+{
+ struct dcache_entry *new_entry;
+
+ logd("Inserting %s,%d to dcache", name, namelen);
+
+ /* TODO: Deal with names that exceed the allocated size */
+ if (namelen + 1 > DCACHE_ENTRY_NAME_LEN)
+ return NULL;
+
+ if (parent == NULL)
+ parent = &root_entry;
+
+ new_entry = DCACHE_ENTRY_CALLOC();
+ if (!new_entry)
+ return NULL;
+
+ strncpy(new_entry->name, name, namelen);
+ new_entry->name[namelen] = 0;
+ new_entry->nid = nid;
+
+ if (!parent->subdirs) {
+ new_entry->siblings = new_entry;
+ parent->subdirs = new_entry;
+ } else {
+ new_entry->siblings = parent->subdirs->siblings;
+ parent->subdirs->siblings = new_entry;
+ parent->subdirs = new_entry;
+ }
+
+ return new_entry;
+}
+
+/* Lookup a cache entry for a given file name. Return value is a struct pointer
+ * that can be used to both obtain the nid number and insert further child
+ * entries.
+ * TODO: Prune entries by using the LRU counter
+ */
+struct dcache_entry *dcache_lookup(struct dcache_entry *parent,
+ const char *name, int namelen)
+{
+ struct dcache_entry *iter;
+
+ if (parent == NULL)
+ parent = &root_entry;
+
+ if (!parent->subdirs)
+ return NULL;
+
+ /* Iterate the list of siblings to see if there is any match */
+ iter = parent->subdirs;
+
+ do {
+ if (strncmp(iter->name, name, namelen) == 0 &&
+ iter->name[namelen] == 0) {
+ parent->subdirs = iter;
+
+ return iter;
+ }
+
+ iter = iter->siblings;
+ } while (iter != parent->subdirs);
+
+ return NULL;
+}
+
+struct dcache_entry *dcache_try_insert(struct dcache_entry *parent,
+ const char *name, int namelen, uint32_t nid)
+{
+ struct dcache_entry *d = dcache_lookup(parent, name, namelen);
+
+ if (d)
+ return d;
+
+ return dcache_insert(parent, name, namelen, nid);
+
+}
+erofs_nid_t dcache_get_nid(struct dcache_entry *entry)
+{
+ return entry ? entry->nid : root_entry.nid;
+}
+
+struct dcache_entry *dcache_root(void)
+{
+ return &root_entry;
+}
+
diff --git a/fuse/dentry.h b/fuse/dentry.h
new file mode 100644
index 0000000..ee2144d
--- /dev/null
+++ b/fuse/dentry.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\dentry.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef _EROFS_DENTRY_H
+#define _EROFS_DENTRY_H
+
+#include <stdint.h>
+#include "erofs/internal.h"
+
+#ifdef __64BITS
+#define DCACHE_ENTRY_NAME_LEN 40
+#else
+#define DCACHE_ENTRY_NAME_LEN 48
+#endif
+
+/* This struct declares a node of a k-tree. Every node has a pointer to one of
+ * the subdirs and a pointer (in a circular list fashion) to its siblings.
+ */
+
+struct dcache_entry {
+ struct dcache_entry *subdirs;
+ struct dcache_entry *siblings;
+ uint32_t nid;
+ uint16_t lru_count;
+ uint8_t user_count;
+ char name[DCACHE_ENTRY_NAME_LEN];
+};
+
+struct dcache_entry *dcache_insert(struct dcache_entry *parent,
+ const char *name, int namelen, uint32_t n);
+struct dcache_entry *dcache_lookup(struct dcache_entry *parent,
+ const char *name, int namelen);
+struct dcache_entry *dcache_try_insert(struct dcache_entry *parent,
+ const char *name, int namelen,
+ uint32_t nid);
+
+erofs_nid_t dcache_get_nid(struct dcache_entry *entry);
+int dcache_init_root(uint32_t n);
+#endif
diff --git a/fuse/disk_io.c b/fuse/disk_io.c
new file mode 100644
index 0000000..72d351b
--- /dev/null
+++ b/fuse/disk_io.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\disk_io.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#define _XOPEN_SOURCE 500
+#include "disk_io.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "logging.h"
+
+#ifdef __FreeBSD__
+#include <string.h>
+#endif
+
+static const char *erofs_devname;
+static int erofs_devfd = -1;
+static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
+
+int dev_open(const char *path)
+{
+ int fd = open(path, O_RDONLY);
+
+ if (fd < 0)
+ return -errno;
+
+ erofs_devfd = fd;
+ erofs_devname = path;
+
+ return 0;
+}
+
+static inline int pread_wrapper(int fd, void *buf, size_t count, off_t offset)
+{
+ return pread(fd, buf, count, offset);
+}
+
+int dev_read(void *buf, size_t count, off_t offset)
+{
+ ssize_t pread_ret;
+ int lerrno;
+
+ ASSERT(erofs_devfd >= 0);
+
+ pthread_mutex_lock(&read_lock);
+ pread_ret = pread_wrapper(erofs_devfd, buf, count, offset);
+ lerrno = errno;
+ logd("Disk Read: offset[0x%jx] count[%zd] pread_ret=%zd %s",
+ offset, count, pread_ret, strerror(lerrno));
+ pthread_mutex_unlock(&read_lock);
+ if (count == 0)
+ logw("Read operation with 0 size");
+
+ ASSERT((size_t)pread_ret == count);
+
+ return pread_ret;
+}
+
+void dev_close(void)
+{
+ if (erofs_devfd >= 0) {
+ close(erofs_devfd);
+ erofs_devfd = -1;
+ }
+}
diff --git a/fuse/disk_io.h b/fuse/disk_io.h
new file mode 100644
index 0000000..6b4bd3c
--- /dev/null
+++ b/fuse/disk_io.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\disk_io.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __DISK_IO_H
+#define __DISK_IO_H
+
+#include "erofs/defs.h"
+#include "erofs/internal.h"
+
+int dev_open(const char *path);
+void dev_close(void);
+int dev_read(void *buf, size_t count, off_t offset);
+
+static inline int dev_read_blk(void *buf, uint32_t nr)
+{
+ return dev_read(buf, EROFS_BLKSIZ, blknr_to_addr(nr));
+}
+#endif
diff --git a/fuse/getattr.c b/fuse/getattr.c
new file mode 100644
index 0000000..6c91424
--- /dev/null
+++ b/fuse/getattr.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\getattr.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "getattr.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "erofs/defs.h"
+#include "erofs/internal.h"
+#include "erofs_fs.h"
+
+#include "logging.h"
+#include "namei.h"
+
+extern struct erofs_super_block super;
+
+/* GNU's definitions of the attributes
+ * (http://www.gnu.org/software/libc/manual/html_node/Attribute-Meanings.html):
+ * st_uid: The user ID of the file’s owner.
+ * st_gid: The group ID of the file.
+ * st_atime: This is the last access time for the file.
+ * st_mtime: This is the time of the last modification to the contents of the
+ * file.
+ * st_mode: Specifies the mode of the file. This includes file type information
+ * (see Testing File Type) and the file permission bits (see Permission
+ * Bits).
+ * st_nlink: The number of hard links to the file.This count keeps track of how
+ * many directories have entries for this file. If the count is ever
+ * decremented to zero, then the file itself is discarded as soon as
+ * no process still holds it open. Symbolic links are not counted in
+ * the total.
+ * st_size: This specifies the size of a regular file in bytes. For files that
+ * are really devices this field isn’t usually meaningful.For symbolic
+ * links this specifies the length of the file name the link refers to.
+ */
+int erofs_getattr(const char *path, struct stat *stbuf)
+{
+ struct erofs_vnode v;
+ int ret;
+
+ logd("getattr(%s)", path);
+ memset(&v, 0, sizeof(v));
+ ret = erofs_iget_by_path(path, &v);
+ if (ret)
+ return -ENOENT;
+
+ stbuf->st_mode = le16_to_cpu(v.i_mode);
+ stbuf->st_nlink = le16_to_cpu(v.i_nlink);
+ stbuf->st_size = le32_to_cpu(v.i_size);
+ stbuf->st_blocks = stbuf->st_size / EROFS_BLKSIZ;
+ stbuf->st_uid = le16_to_cpu(v.i_uid);
+ stbuf->st_gid = le16_to_cpu(v.i_gid);
+ stbuf->st_atime = super.build_time;
+ stbuf->st_mtime = super.build_time;
+ stbuf->st_ctime = super.build_time;
+
+ return 0;
+}
diff --git a/fuse/getattr.h b/fuse/getattr.h
new file mode 100644
index 0000000..dbcff7c
--- /dev/null
+++ b/fuse/getattr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\getattr.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __EROFS_GETATTR_H
+#define __EROFS_GETATTR_H
+
+#include <fuse.h>
+#include <fuse_opt.h>
+
+int erofs_getattr(const char *path, struct stat *st);
+
+#endif
diff --git a/fuse/init.c b/fuse/init.c
new file mode 100644
index 0000000..885705f
--- /dev/null
+++ b/fuse/init.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\init.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "init.h"
+#include <string.h>
+#include <asm-generic/errno-base.h>
+
+#include "namei.h"
+#include "disk_io.h"
+#include "logging.h"
+
+#define STR(_X) (#_X)
+#define SUPER_MEM(_X) (super._X)
+
+
+struct erofs_super_block super;
+static struct erofs_super_block *sbk = &super;
+
+int erofs_init_super(void)
+{
+ int ret;
+ char buf[EROFS_BLKSIZ];
+ struct erofs_super_block *sb;
+
+ memset(buf, 0, sizeof(buf));
+ ret = dev_read_blk(buf, 0);
+ if (ret != EROFS_BLKSIZ) {
+ logi("Failed to read super block ret=%d", ret);
+ return -EINVAL;
+ }
+
+ sb = (struct erofs_super_block *) (buf + BOOT_SECTOR_SIZE);
+ sbk->magic = le32_to_cpu(sb->magic);
+ if (sbk->magic != EROFS_SUPER_MAGIC_V1) {
+ logi("EROFS magic[0x%X] NOT matched to [0x%X] ",
+ super.magic, EROFS_SUPER_MAGIC_V1);
+ return -EINVAL;
+ }
+
+ sbk->checksum = le32_to_cpu(sb->checksum);
+ sbk->features = le32_to_cpu(sb->features);
+ sbk->blkszbits = sb->blkszbits;
+ ASSERT(sbk->blkszbits != 32);
+
+ sbk->inos = le64_to_cpu(sb->inos);
+ sbk->build_time = le64_to_cpu(sb->build_time);
+ sbk->build_time_nsec = le32_to_cpu(sb->build_time_nsec);
+ sbk->blocks = le32_to_cpu(sb->blocks);
+ sbk->meta_blkaddr = le32_to_cpu(sb->meta_blkaddr);
+ sbk->xattr_blkaddr = le32_to_cpu(sb->xattr_blkaddr);
+ memcpy(sbk->uuid, sb->uuid, 16);
+ memcpy(sbk->volume_name, sb->volume_name, 16);
+ sbk->root_nid = le16_to_cpu(sb->root_nid);
+
+ logp("%-15s:0x%X", STR(magic), SUPER_MEM(magic));
+ logp("%-15s:0x%X", STR(features), SUPER_MEM(features));
+ logp("%-15s:%u", STR(blkszbits), SUPER_MEM(blkszbits));
+ logp("%-15s:%u", STR(root_nid), SUPER_MEM(root_nid));
+ logp("%-15s:%ul", STR(inos), SUPER_MEM(inos));
+ logp("%-15s:%d", STR(meta_blkaddr), SUPER_MEM(meta_blkaddr));
+ logp("%-15s:%d", STR(xattr_blkaddr), SUPER_MEM(xattr_blkaddr));
+
+ return 0;
+}
+
+erofs_nid_t erofs_get_root_nid(void)
+{
+ return sbk->root_nid;
+}
+
+erofs_nid_t addr2nid(erofs_off_t addr)
+{
+ erofs_nid_t offset = (erofs_nid_t)sbk->meta_blkaddr * EROFS_BLKSIZ;
+
+ ASSERT(IS_SLOT_ALIGN(addr));
+ return (addr - offset) >> EROFS_ISLOTBITS;
+}
+
+erofs_off_t nid2addr(erofs_nid_t nid)
+{
+ erofs_off_t offset = (erofs_off_t)sbk->meta_blkaddr * EROFS_BLKSIZ;
+
+ return (nid << EROFS_ISLOTBITS) + offset;
+}
+
+void *erofs_init(struct fuse_conn_info *info)
+{
+ logi("Using FUSE protocol %d.%d", info->proto_major, info->proto_minor);
+
+ if (inode_init(erofs_get_root_nid()) != 0) {
+ loge("inode initialization failed")
+ ABORT();
+ }
+ return NULL;
+}
diff --git a/fuse/init.h b/fuse/init.h
new file mode 100644
index 0000000..d7a97b5
--- /dev/null
+++ b/fuse/init.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\init.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __EROFS_INIT_H
+#define __EROFS_INIT_H
+
+#include <fuse.h>
+#include <fuse_opt.h>
+#include "erofs/internal.h"
+
+#define BOOT_SECTOR_SIZE 0x400
+
+int erofs_init_super(void);
+erofs_nid_t erofs_get_root_nid(void);
+erofs_off_t nid2addr(erofs_nid_t nid);
+erofs_nid_t addr2nid(erofs_off_t addr);
+void *erofs_init(struct fuse_conn_info *info);
+
+#endif
diff --git a/fuse/logging.c b/fuse/logging.c
new file mode 100644
index 0000000..2d1f1c7
--- /dev/null
+++ b/fuse/logging.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\logging.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "logging.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <pthread.h>
+
+static int loglevel = DEFAULT_LOG_LEVEL;
+static FILE *logfile;
+static const char * const loglevel_str[] = {
+ [LOG_EMERG] = "[emerg]",
+ [LOG_ALERT] = "[alert]",
+ [LOG_DUMP] = "[dump] ",
+ [LOG_ERR] = "[err] ",
+ [LOG_WARNING] = "[warn] ",
+ [LOG_NOTICE] = "[notic]",
+ [LOG_INFO] = "[info] ",
+ [LOG_DEBUG] = "[debug]",
+};
+
+void __LOG(int level, const char *func, int line, const char *format, ...)
+{
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+ va_list ap;
+ FILE *fp = logfile ? logfile : stdout;
+
+ if (!fp)
+ return;
+ if (level < 0 || level > loglevel)
+ return;
+
+ /* We have a lock here so different threads don interleave the log
+ * output
+ */
+ pthread_mutex_lock(&lock);
+ va_start(ap, format);
+ fprintf(fp, "%s", loglevel_str[level]);
+ if (func)
+ fprintf(fp, "%s", func);
+ if (line >= 0)
+ fprintf(fp, "(%d):", line);
+ vfprintf(fp, format, ap);
+ fprintf(fp, "\n");
+ va_end(ap);
+ fflush(fp);
+ pthread_mutex_unlock(&lock);
+}
+
+inline void logging_setlevel(int new_level)
+{
+ loglevel = new_level;
+}
+
+int logging_open(const char *path)
+{
+ if (path == NULL)
+ return 0;
+
+ logfile = fopen(path, "w");
+ if (logfile == NULL) {
+ perror("open");
+ return -1;
+ }
+
+ return 0;
+}
+
+void logging_close(void)
+{
+ if (logfile) {
+ fflush(logfile);
+ fclose(logfile);
+ logfile = NULL;
+ }
+}
+
diff --git a/fuse/logging.h b/fuse/logging.h
new file mode 100644
index 0000000..7aa2eda
--- /dev/null
+++ b/fuse/logging.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\logging.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __LOGGING_H
+#define __LOGGING_H
+
+#include <assert.h>
+#include <stdlib.h>
+
+#define LOG_EMERG 0
+#define LOG_ALERT 1
+#define LOG_DUMP 2
+#define LOG_ERR 3
+#define LOG_WARNING 4
+#define LOG_NOTICE 5
+#define LOG_INFO 6
+#define LOG_DEBUG 7
+
+#define logem(...) __LOG(LOG_EMERG, __func__, __LINE__, ##__VA_ARGS__)
+#define loga(...) __LOG(LOG_ALERT, __func__, __LINE__, ##__VA_ARGS__)
+#define loge(...) __LOG(LOG_ERR, __func__, __LINE__, ##__VA_ARGS__)
+#define logw(...) __LOG(LOG_WARNING, __func__, __LINE__, ##__VA_ARGS__)
+#define logn(...) __LOG(LOG_NOTICE, __func__, __LINE__, ##__VA_ARGS__)
+#define logi(...) __LOG(LOG_INFO, __func__, __LINE__, ##__VA_ARGS__)
+#define logp(...) __LOG(LOG_DUMP, "", -1, ##__VA_ARGS__)
+#define logd(...) __LOG(LOG_DEBUG, __func__, __LINE__, ##__VA_ARGS__)
+
+#define DEFAULT_LOG_FILE "fuse.log"
+
+#ifdef _DEBUG
+#define DEFAULT_LOG_LEVEL LOG_DEBUG
+
+#define ASSERT(assertion) ({ \
+ if (!(assertion)) { \
+ logw("ASSERT FAIL: " #assertion); \
+ assert(assertion); \
+ } \
+})
+#define ABORT(_X) abort(_X)
+#else
+#define DEFAULT_LOG_LEVEL LOG_ERR
+#define ASSERT(assertion)
+#define ABORT(_X)
+#endif
+
+void __LOG(int level, const char *func, int line, const char *format, ...);
+void logging_setlevel(int new_level);
+int logging_open(const char *path);
+void logging_close(void);
+
+#endif
+
diff --git a/fuse/main.c b/fuse/main.c
new file mode 100644
index 0000000..fa9795e
--- /dev/null
+++ b/fuse/main.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\main.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <stddef.h>
+
+#include "logging.h"
+#include "init.h"
+#include "read.h"
+#include "getattr.h"
+#include "open.h"
+#include "readir.h"
+#include "disk_io.h"
+
+enum {
+ EROFS_OPT_HELP,
+ EROFS_OPT_VER,
+};
+
+struct options {
+ const char *disk;
+ const char *mount;
+ const char *logfile;
+ unsigned int debug_lvl;
+};
+static struct options cfg;
+
+#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
+
+static const struct fuse_opt option_spec[] = {
+ OPTION("--log=%s", logfile),
+ OPTION("--dbg=%u", debug_lvl),
+ FUSE_OPT_KEY("-h", EROFS_OPT_HELP),
+ FUSE_OPT_KEY("-v", EROFS_OPT_VER),
+ FUSE_OPT_END
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "\terofsfuse [options] <image> <mountpoint>\n");
+ fprintf(stderr, "\t --log=<file> output log file\n");
+ fprintf(stderr, "\t --dbg=<level> log debug level 0 ~ 7\n");
+ fprintf(stderr, "\t -h show help\n");
+ fprintf(stderr, "\t -v show version\n");
+ exit(1);
+}
+
+static void dump_cfg(void)
+{
+ fprintf(stderr, "\tdisk :%s\n", cfg.disk);
+ fprintf(stderr, "\tmount:%s\n", cfg.mount);
+ fprintf(stderr, "\tdebug_lvl:%u\n", cfg.debug_lvl);
+ fprintf(stderr, "\tlogfile :%s\n", cfg.logfile);
+}
+
+static int optional_opt_func(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ UNUSED(data);
+ UNUSED(outargs);
+
+ switch (key) {
+ case FUSE_OPT_KEY_OPT:
+ return 1;
+
+ case FUSE_OPT_KEY_NONOPT:
+ if (!cfg.disk) {
+ cfg.disk = strdup(arg);
+ return 0;
+ } else if (!cfg.mount)
+ cfg.mount = strdup(arg);
+
+ return 1;
+ case EROFS_OPT_HELP:
+ usage();
+ break;
+
+ case EROFS_OPT_VER:
+ fprintf(stderr, "EROFS FUSE VERSION v 1.0.0\n");
+ exit(0);
+ }
+
+ return 1;
+}
+
+static void signal_handle_sigsegv(int signal)
+{
+ void *array[10];
+ size_t nptrs;
+ char **strings;
+ size_t i;
+
+ UNUSED(signal);
+ logd("========================================");
+ logd("Segmentation Fault. Starting backtrace:");
+ nptrs = backtrace(array, 10);
+ strings = backtrace_symbols(array, nptrs);
+ if (strings) {
+ for (i = 0; i < nptrs; i++)
+ logd("%s", strings[i]);
+ free(strings);
+ }
+ logd("========================================");
+
+ abort();
+}
+
+static struct fuse_operations erofs_ops = {
+ .getattr = erofs_getattr,
+ .readdir = erofs_readdir,
+ .open = erofs_open,
+ .read = erofs_read,
+ .init = erofs_init,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+
+ if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
+ fprintf(stderr, "Failed to initialize signals\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Parse options */
+ if (fuse_opt_parse(&args, &cfg, option_spec, optional_opt_func) < 0)
+ return 1;
+
+ dump_cfg();
+
+ if (logging_open(cfg.logfile) < 0) {
+ fprintf(stderr, "Failed to initialize logging\n");
+ goto exit;
+ }
+
+ logging_setlevel(cfg.debug_lvl);
+
+ if (dev_open(cfg.disk) < 0) {
+ fprintf(stderr, "Failed to open disk:%s\n", cfg.disk);
+ goto exit_log;
+ }
+
+ if (erofs_init_super()) {
+ fprintf(stderr, "Failed to read erofs super block\n");
+ goto exit_dev;
+ }
+
+ logi("fuse start");
+
+ ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
+
+ logi("fuse done ret=%d", ret);
+
+exit_dev:
+ dev_close();
+exit_log:
+ logging_close();
+exit:
+ fuse_opt_free_args(&args);
+ return ret;
+}
+
diff --git a/fuse/namei.c b/fuse/namei.c
new file mode 100644
index 0000000..ab497e8
--- /dev/null
+++ b/fuse/namei.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\inode.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "namei.h"
+#include <linux/kdev_t.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "erofs/defs.h"
+#include "logging.h"
+#include "disk_io.h"
+#include "dentry.h"
+#include "init.h"
+
+#define IS_PATH_SEPARATOR(__c) ((__c) == '/')
+#define MINORBITS 20
+#define MINORMASK ((1U << MINORBITS) - 1)
+#define DT_UNKNOWN 0
+
+static const char *skip_trailing_backslash(const char *path)
+{
+ while (IS_PATH_SEPARATOR(*path))
+ path++;
+ return path;
+}
+
+static uint8_t get_path_token_len(const char *path)
+{
+ uint8_t len = 0;
+
+ while (path[len] != '/' && path[len])
+ len++;
+ return len;
+}
+
+int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *vi)
+{
+ int ret;
+ char buf[EROFS_BLKSIZ];
+ struct erofs_inode_v1 *v1;
+ const erofs_off_t addr = nid2addr(nid);
+ const size_t size = EROFS_BLKSIZ - erofs_blkoff(addr);
+
+ ret = dev_read(buf, size, addr);
+ if (ret != (int)size)
+ return -EIO;
+
+ v1 = (struct erofs_inode_v1 *)buf;
+ vi->data_mapping_mode = __inode_data_mapping(le16_to_cpu(v1->i_advise));
+ vi->inode_isize = sizeof(struct erofs_inode_v1);
+ vi->xattr_isize = ondisk_xattr_ibody_size(v1->i_xattr_icount);
+ vi->i_size = le32_to_cpu(v1->i_size);
+ vi->i_mode = le16_to_cpu(v1->i_mode);
+ vi->i_uid = le16_to_cpu(v1->i_uid);
+ vi->i_gid = le16_to_cpu(v1->i_gid);
+ vi->i_nlink = le16_to_cpu(v1->i_nlink);
+ vi->nid = nid;
+
+ switch (vi->i_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ /* fixme: add special devices support
+ * vi->i_rdev = new_decode_dev(le32_to_cpu(v1->i_u.rdev));
+ */
+ break;
+ case S_IFIFO:
+ case S_IFSOCK:
+ /*fixme: vi->i_rdev = 0; */
+ break;
+ case S_IFREG:
+ case S_IFLNK:
+ case S_IFDIR:
+ vi->raw_blkaddr = le32_to_cpu(v1->i_u.raw_blkaddr);
+ break;
+ default:
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* dirent + name string */
+struct dcache_entry *list_name(const char *buf, struct dcache_entry *parent,
+ const char *name, unsigned int len)
+{
+ struct dcache_entry *entry = NULL;
+ struct erofs_dirent *ds, *de;
+
+ ds = (struct erofs_dirent *)buf;
+ de = (struct erofs_dirent *)(buf + le16_to_cpu(ds->nameoff));
+
+ while (ds < de) {
+ erofs_nid_t nid = le64_to_cpu(ds->nid);
+ uint16_t nameoff = le16_to_cpu(ds->nameoff);
+ char *d_name = (char *)(buf + nameoff);
+ uint16_t name_len = (ds + 1 >= de) ? (uint16_t)strlen(d_name) :
+ le16_to_cpu(ds[1].nameoff) - nameoff;
+
+ #if defined(EROFS_DEBUG_ENTRY)
+ {
+ char debug[EROFS_BLKSIZ];
+
+ memcpy(debug, d_name, name_len);
+ debug[name_len] = '\0';
+ logi("list entry: %s nid=%u", debug, nid);
+ }
+ #endif
+
+ entry = dcache_try_insert(parent, d_name, name_len, nid);
+ if (len == name_len && !memcmp(name, d_name, name_len))
+ return entry;
+
+ entry = NULL;
+ ++ds;
+ }
+
+ return entry;
+}
+
+struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
+ unsigned int name_len)
+{
+ int ret;
+ char buf[EROFS_BLKSIZ];
+ struct dcache_entry *entry = NULL;
+ struct erofs_vnode v;
+ uint32_t nr_cnt, dir_nr, dirsize, blkno;
+
+ ret = erofs_iget_by_nid(parent->nid, &v);
+ if (ret)
+ return NULL;
+
+ /* to check whether dirent is in the inline dirs */
+ blkno = v.raw_blkaddr;
+ dirsize = v.i_size;
+ dir_nr = erofs_blknr(dirsize);
+
+ nr_cnt = 0;
+ while (nr_cnt < dir_nr) {
+ if (dev_read_blk(buf, blkno + nr_cnt) != EROFS_BLKSIZ)
+ return NULL;
+
+ entry = list_name(buf, parent, name, name_len);
+ if (entry)
+ goto next;
+
+ ++nr_cnt;
+ }
+
+ if (v.data_mapping_mode == EROFS_INODE_FLAT_INLINE) {
+ uint32_t dir_off = erofs_blkoff(dirsize);
+ off_t dir_addr = nid2addr(dcache_get_nid(parent))
+ + sizeof(struct erofs_inode_v1);
+
+ memset(buf, 0, sizeof(buf));
+ ret = dev_read(buf, dir_off, dir_addr);
+ if (ret < 0 && (uint32_t)ret != dir_off)
+ return NULL;
+
+ entry = list_name(buf, parent, name, name_len);
+ }
+next:
+ return entry;
+}
+
+extern struct dcache_entry root_entry;
+int walk_path(const char *_path, erofs_nid_t *out_nid)
+{
+ struct dcache_entry *next, *ret;
+ const char *path = _path;
+
+ ret = next = &root_entry;
+ for (;;) {
+ uint8_t path_len;
+
+ path = skip_trailing_backslash(path);
+ path_len = get_path_token_len(path);
+ ret = next;
+ if (path_len == 0)
+ break;
+
+ next = dcache_lookup(ret, path, path_len);
+ if (!next) {
+ next = disk_lookup(ret, path, path_len);
+ if (!next)
+ return -ENOENT;
+ }
+
+ path += path_len;
+ }
+
+ if (!ret)
+ return -ENOENT;
+ logd("find path = %s nid=%u", _path, ret->nid);
+
+ *out_nid = ret->nid;
+ return 0;
+
+}
+
+int erofs_iget_by_path(const char *path, struct erofs_vnode *v)
+{
+ int ret;
+ erofs_nid_t nid;
+
+ ret = walk_path(path, &nid);
+ if (ret)
+ return ret;
+
+ return erofs_iget_by_nid(nid, v);
+}
+
+int inode_init(erofs_nid_t root)
+{
+ dcache_init_root(root);
+
+ return 0;
+}
+
diff --git a/fuse/namei.h b/fuse/namei.h
new file mode 100644
index 0000000..80e84d7
--- /dev/null
+++ b/fuse/namei.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\inode.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __INODE_H
+#define __INODE_H
+
+#include "erofs/internal.h"
+#include "erofs_fs.h"
+
+int inode_init(erofs_nid_t root);
+struct dcache_entry *get_cached_dentry(struct dcache_entry **parent,
+ const char **path);
+int erofs_iget_by_path(const char *path, struct erofs_vnode *v);
+int erofs_iget_by_nid(erofs_nid_t nid, struct erofs_vnode *v);
+struct dcache_entry *disk_lookup(struct dcache_entry *parent, const char *name,
+ unsigned int name_len);
+int walk_path(const char *path, erofs_nid_t *out_nid);
+
+#endif
diff --git a/fuse/open.c b/fuse/open.c
new file mode 100644
index 0000000..9d2edca
--- /dev/null
+++ b/fuse/open.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\open.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "open.h"
+#include <asm-generic/errno-base.h>
+#include <fuse.h>
+#include <fuse_opt.h>
+#include "logging.h"
+
+int erofs_open(const char *path, struct fuse_file_info *fi)
+{
+ logi("open path=%s", path);
+
+ if ((fi->flags & O_ACCMODE) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
diff --git a/fuse/open.h b/fuse/open.h
new file mode 100644
index 0000000..d02c1f7
--- /dev/null
+++ b/fuse/open.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\open.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __EROFS_OPEN_H
+#define __EROFS_OPEN_H
+
+#include <fuse.h>
+#include <fuse_opt.h>
+
+int erofs_open(const char *path, struct fuse_file_info *fi);
+
+#endif
diff --git a/fuse/read.c b/fuse/read.c
new file mode 100644
index 0000000..b2bfbd3
--- /dev/null
+++ b/fuse/read.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\read.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "read.h"
+#include <errno.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "erofs/defs.h"
+#include "erofs/internal.h"
+#include "logging.h"
+#include "namei.h"
+#include "disk_io.h"
+#include "init.h"
+
+size_t erofs_read_data(struct erofs_vnode *vnode, char *buffer,
+ size_t size, off_t offset)
+{
+ int ret;
+ size_t sum, rdsz = 0;
+ uint32_t addr = blknr_to_addr(vnode->raw_blkaddr) + offset;
+
+ sum = (offset + size) > vnode->i_size ?
+ (size_t)(vnode->i_size - offset) : size;
+ while (rdsz < sum) {
+ size_t count = min(EROFS_BLKSIZ, (uint32_t)(sum - rdsz));
+
+ ret = dev_read(buffer + rdsz, count, addr + rdsz);
+ if (ret < 0 || (size_t)ret != count)
+ return -EIO;
+ rdsz += count;
+ }
+
+ logi("nid:%u size=%zd offset=%llu realsize=%zd done",
+ vnode->nid, size, (long long)offset, rdsz);
+ return rdsz;
+
+}
+
+size_t erofs_read_data_inline(struct erofs_vnode *vnode, char *buffer,
+ size_t size, off_t offset)
+{
+ int ret;
+ size_t sum, suminline, rdsz = 0;
+ uint32_t addr = blknr_to_addr(vnode->raw_blkaddr) + offset;
+ uint32_t szblk = vnode->i_size - erofs_blkoff(vnode->i_size);
+
+ sum = (offset + size) > szblk ? (size_t)(szblk - offset) : size;
+ suminline = size - sum;
+
+ while (rdsz < sum) {
+ size_t count = min(EROFS_BLKSIZ, (uint32_t)(sum - rdsz));
+
+ ret = dev_read(buffer + rdsz, count, addr + rdsz);
+ if (ret < 0 || (uint32_t)ret != count)
+ return -EIO;
+ rdsz += count;
+ }
+
+ if (!suminline)
+ goto finished;
+
+ addr = nid2addr(vnode->nid) + sizeof(struct erofs_inode_v1)
+ + vnode->xattr_isize;
+ ret = dev_read(buffer + rdsz, suminline, addr);
+ if (ret < 0 || (size_t)ret != suminline)
+ return -EIO;
+ rdsz += suminline;
+
+finished:
+ logi("nid:%u size=%zd suminline=%u offset=%llu realsize=%zd done",
+ vnode->nid, size, suminline, (long long)offset, rdsz);
+ return rdsz;
+
+}
+
+
+int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int ret;
+ erofs_nid_t nid;
+ struct erofs_vnode v;
+
+ UNUSED(fi);
+ logi("path:%s size=%zd offset=%llu", path, size, (long long)offset);
+
+ ret = walk_path(path, &nid);
+ if (ret)
+ return ret;
+
+ ret = erofs_iget_by_nid(nid, &v);
+ if (ret)
+ return ret;
+
+ logi("path:%s nid=%llu mode=%u", path, nid, v.data_mapping_mode);
+ switch (v.data_mapping_mode) {
+ case EROFS_INODE_FLAT_PLAIN:
+ return erofs_read_data(&v, buffer, size, offset);
+
+ case EROFS_INODE_FLAT_INLINE:
+ return erofs_read_data_inline(&v, buffer, size, offset);
+
+ case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
+ case EROFS_INODE_FLAT_COMPRESSION:
+ /* Fixme: */
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/fuse/read.h b/fuse/read.h
new file mode 100644
index 0000000..f2fcebe
--- /dev/null
+++ b/fuse/read.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\read.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __EROFS_READ_H
+#define __EROFS_READ_H
+
+#include <fuse.h>
+#include <fuse_opt.h>
+
+int erofs_read(const char *path, char *buffer, size_t size, off_t offset,
+ struct fuse_file_info *fi);
+
+#endif
diff --git a/fuse/readir.c b/fuse/readir.c
new file mode 100644
index 0000000..7fc69f4
--- /dev/null
+++ b/fuse/readir.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * erofs-fuse\readir.c
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#include "readir.h"
+#include <errno.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+
+#include "erofs/defs.h"
+#include "erofs/internal.h"
+#include "erofs_fs.h"
+#include "namei.h"
+#include "disk_io.h"
+#include "logging.h"
+#include "init.h"
+
+erofs_nid_t split_entry(char *entry, off_t ofs, char *end, char *name)
+{
+ struct erofs_dirent *de = (struct erofs_dirent *)(entry + ofs);
+ uint16_t nameoff = le16_to_cpu(de->nameoff);
+ const char *de_name = entry + nameoff;
+ uint32_t de_namelen;
+
+ if ((void *)(de + 1) >= (void *)end)
+ de_namelen = strlen(de_name);
+ else
+ de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
+
+ memcpy(name, de_name, de_namelen);
+ name[de_namelen] = '\0';
+ return le64_to_cpu(de->nid);
+}
+
+int fill_dir(char *entrybuf, fuse_fill_dir_t filler, void *buf)
+{
+ uint32_t ofs;
+ uint16_t nameoff;
+ char *end;
+ char name[EROFS_BLKSIZ];
+
+ nameoff = le16_to_cpu(((struct erofs_dirent *)entrybuf)->nameoff);
+ end = entrybuf + nameoff;
+ ofs = 0;
+ while (ofs < nameoff) {
+ (void)split_entry(entrybuf, ofs, end, name);
+ filler(buf, name, NULL, 0);
+ ofs += sizeof(struct erofs_dirent);
+ }
+
+ return 0;
+}
+
+/** Function to add an entry in a readdir() operation
+ *
+ * @param buf the buffer passed to the readdir() operation
+ * @param name the file name of the directory entry
+ * @param stat file attributes, can be NULL
+ * @param off offset of the next entry or zero
+ * @return 1 if buffer is full, zero otherwise
+ */
+int erofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int ret;
+ erofs_nid_t nid;
+ struct erofs_vnode v;
+ char dirsbuf[EROFS_BLKSIZ];
+ uint32_t dir_nr, dir_off, nr_cnt;
+
+ logd("readdir:%s offset=%llu", path, (long long)offset);
+ UNUSED(fi);
+
+ ret = walk_path(path, &nid);
+ if (ret)
+ return ret;
+
+ logd("path=%s nid = %u", path, nid);
+ ret = erofs_iget_by_nid(nid, &v);
+ if (ret)
+ return ret;
+
+ if (!S_ISDIR(v.i_mode))
+ return -ENOTDIR;
+
+ if (!v.i_size)
+ return 0;
+
+ dir_nr = erofs_blknr(v.i_size);
+ dir_off = erofs_blkoff(v.i_size);
+ nr_cnt = 0;
+
+ logd("dir_size=%u dir_nr = %u dir_off=%u", v.i_size, dir_nr, dir_off);
+
+ while (nr_cnt < dir_nr) {
+ memset(dirsbuf, 0, sizeof(dirsbuf));
+ ret = dev_read_blk(dirsbuf, v.raw_blkaddr + nr_cnt);
+ if (ret != EROFS_BLKSIZ)
+ return -EIO;
+ fill_dir(dirsbuf, filler, buf);
+ ++nr_cnt;
+ }
+
+ if (v.data_mapping_mode == EROFS_INODE_FLAT_INLINE) {
+ off_t addr;
+
+ addr = nid2addr(nid) + sizeof(struct erofs_inode_v1)
+ + v.xattr_isize;
+
+ memset(dirsbuf, 0, sizeof(dirsbuf));
+ ret = dev_read(dirsbuf, dir_off, addr);
+ if (ret < 0 || (uint32_t)ret != dir_off)
+ return -EIO;
+ fill_dir(dirsbuf, filler, buf);
+ }
+
+ return 0;
+}
+
diff --git a/fuse/readir.h b/fuse/readir.h
new file mode 100644
index 0000000..21ab7a4
--- /dev/null
+++ b/fuse/readir.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * erofs-fuse\readir.h
+ * Created by Li Guifu <blucerlee at gmail.com>
+ */
+
+#ifndef __EROFS_READDIR_H
+#define __EROFS_READDIR_H
+
+#include <fuse.h>
+#include <fuse_opt.h>
+
+int erofs_readdir(const char *path, void *buffer, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi);
+
+
+#endif
diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 0d9910c..19585a2 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -24,6 +24,9 @@
#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
#endif
+#ifndef UNUSED
+#define UNUSED(_X) ((void) _X)
+#endif
/*
* container_of - cast a member of a structure out to the containing structure
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 33a72b5..b8ba88c 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -45,6 +45,8 @@ typedef u32 erofs_blk_t;
#define blknr_to_addr(nr) ((erofs_off_t)(nr) * EROFS_BLKSIZ)
#define BLK_ROUND_UP(addr) DIV_ROUND_UP(addr, EROFS_BLKSIZ)
+#define IS_SLOT_ALIGN(__ADDR) (((__ADDR) % (EROFS_SLOTSIZE)) ? 0 : 1)
+#define IS_BLK_ALIGN(__ADDR) (((__ADDR) % (EROFS_BLKSIZ)) ? 0 : 1)
struct erofs_buffer_head;
@@ -98,11 +100,46 @@ struct erofs_inode {
void *compressmeta;
};
+struct erofs_vnode {
+ uint8_t data_mapping_mode;
+
+ uint32_t i_size;
+ /* inline size in bytes */
+ uint16_t inode_isize;
+ uint16_t xattr_isize;
+
+ uint16_t xattr_shared_count;
+ char *xattr_shared_xattrs;
+
+ erofs_blk_t raw_blkaddr;
+ erofs_nid_t nid;
+ uint32_t i_ino;
+
+ uint16_t i_mode;
+ uint16_t i_uid;
+ uint16_t i_gid;
+ uint16_t i_nlink;
+
+ /* if file is inline read inline data witch inode */
+ char *idata;
+};
+
static inline bool is_inode_layout_compression(struct erofs_inode *inode)
{
return erofs_inode_is_data_compressed(inode->data_mapping_mode);
}
+#define __inode_advise(x, bit, bits) \
+ (((x) >> (bit)) & ((1 << (bits)) - 1))
+
+#define __inode_version(advise) \
+ __inode_advise(advise, EROFS_I_VERSION_BIT, \
+ EROFS_I_VERSION_BITS)
+
+#define __inode_data_mapping(advise) \
+ __inode_advise(advise, EROFS_I_DATA_MAPPING_BIT,\
+ EROFS_I_DATA_MAPPING_BITS)
+
#define IS_ROOT(x) ((x) == (x)->i_parent)
struct erofs_dentry {
--
2.17.1
More information about the Linux-erofs
mailing list