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