There's no need to record "." dirents in the directory data (while
they could be used for sanity checks, they aren't very useful.)
Omitting "." dirents also improves directory data deduplication.

Use a per-inode (instead of per-sb) flag to indicate if the "." dirent
is omitted or not, ensuring compatibility with incremental builds.  It
also reuses EROFS_I_NLINK_1_BIT, as it has very limited use cases for
directories with `nlink = 1`.

Emit the "." entry as the last virtual dirent in the directory because
it is _much_ less frequently used than the ".." dirent.  It also keeps
`f_pos` meaningful, as it strictly follows the directory data when it's
less than i_size.

Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com>
---
 fs/erofs/dir.c      | 5 +++++
 fs/erofs/erofs_fs.h | 1 +
 fs/erofs/inode.c    | 4 +++-
 fs/erofs/internal.h | 1 +
 4 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
index fa3c2d380cc9..2fae209d0274 100644
--- a/fs/erofs/dir.c
+++ b/fs/erofs/dir.c
@@ -90,6 +90,11 @@ static int erofs_readdir(struct file *f, struct dir_context 
*ctx)
                ofs = 0;
        }
        erofs_put_metabuf(&buf);
+       if (EROFS_I(dir)->dot_omitted && ctx->pos == dir->i_size) {
+               if (!dir_emit_dot(f, ctx))
+                       return 0;
+               ++ctx->pos;
+       }
        return err < 0 ? err : 0;
 }
 
diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
index 8330ca3b18d3..791124b3f57c 100644
--- a/fs/erofs/erofs_fs.h
+++ b/fs/erofs/erofs_fs.h
@@ -116,6 +116,7 @@ static inline bool erofs_inode_is_data_compressed(unsigned 
int datamode)
 #define EROFS_I_VERSION_BIT    0
 #define EROFS_I_DATALAYOUT_BIT 1
 #define EROFS_I_NLINK_1_BIT    4       /* non-directory compact inodes only */
+#define EROFS_I_DOT_OMITTED_BIT        4       /* (directories) omit the `.` 
dirent */
 #define EROFS_I_ALL            ((1 << (EROFS_I_NLINK_1_BIT + 1)) - 1)
 
 /* indicate chunk blkbits, thus 'chunksize = blocksize << chunk blkbits' */
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index 20d58228dfc9..3a5bb73a9397 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -137,8 +137,10 @@ static int erofs_read_inode(struct inode *inode)
                goto err_out;
        }
        switch (inode->i_mode & S_IFMT) {
-       case S_IFREG:
        case S_IFDIR:
+               vi->dot_omitted = (ifmt >> EROFS_I_DOT_OMITTED_BIT) & 1;
+               fallthrough;
+       case S_IFREG:
        case S_IFLNK:
                vi->startblk = le32_to_cpu(copied.i_u.startblk_lo) |
                        ((u64)le16_to_cpu(copied.i_nb.startblk_hi) << 32);
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 07515a6f2534..91d0b400459c 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -245,6 +245,7 @@ struct erofs_inode {
 
        unsigned char datalayout;
        unsigned char inode_isize;
+       bool dot_omitted;
        unsigned int xattr_isize;
 
        unsigned int xattr_name_filter;
-- 
2.43.5


Reply via email to