]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
erofs: support dot-omitted directories
authorGao Xiang <hsiangkao@linux.alibaba.com>
Mon, 10 Mar 2025 09:54:55 +0000 (17:54 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Sun, 16 Mar 2025 17:25:32 +0000 (01:25 +0800)
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 <hsiangkao@linux.alibaba.com>
Acked-by: Chao Yu <chao@kernel.org>
Link: https://lore.kernel.org/r/20250310095459.2620647-6-hsiangkao@linux.alibaba.com
fs/erofs/dir.c
fs/erofs/erofs_fs.h
fs/erofs/inode.c
fs/erofs/internal.h

index fa3c2d380cc9bd602eae90712249b66b1f314922..2fae209d0274031d69ab15538fd7308c9ec6047b 100644 (file)
@@ -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;
 }
 
index 8330ca3b18d336e4633d157d1f59aa8d9fe635a7..791124b3f57c9065a279b5f490fb8261111d5da7 100644 (file)
@@ -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' */
index 6de281312ea7ebee3b088c192eb1a2d5ec4dd4f9..a0ae0b4f7b012a3481f39cd52a5418e79fb262e0 100644 (file)
@@ -136,8 +136,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);
index 07515a6f2534a8fc8cef4a2a393d51432a31b3b4..91d0b400459cf9c8e2470e1212e492761514708d 100644 (file)
@@ -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;