All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gao Xiang <gaoxiang25@huawei.com>
To: Li Guifu <blucerlee@gmail.com>
Cc: linux-erofs@lists.ozlabs.org
Subject: Re: [PATCH] erofs-utils: introduce fuse framework
Date: Tue, 20 Aug 2019 17:27:04 +0800	[thread overview]
Message-ID: <20190820092632.GI159846@architecture4> (raw)
In-Reply-To: <20190818135923.27444-1-blucerlee@gmail.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8\"", Size: 43774 bytes --]

Hi Guifu,

On Sun, Aug 18, 2019 at 06:59:23AM -0700, Li Guifu wrote:
> 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@gmail.com>

OK, let's play with it later, I will look through all
the code below and integrate it into erofs-utils framework.

Thanks,
Gao Xiang

> ---
>  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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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
> 

      reply	other threads:[~2019-08-20  9:28 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-18 13:59 [PATCH] erofs-utils: introduce fuse framework Li Guifu
2019-08-20  9:27 ` Gao Xiang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190820092632.GI159846@architecture4 \
    --to=gaoxiang25@huawei.com \
    --cc=blucerlee@gmail.com \
    --cc=linux-erofs@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.