Commit | Line | Data |
---|---|---|
29b24f6c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3aa8ec71 | 2 | /* |
3aa8ec71 | 3 | * Copyright (C) 2017-2018 HUAWEI, Inc. |
592e7cd0 | 4 | * https://www.huawei.com/ |
fe5de585 | 5 | * Copyright (C) 2022, Alibaba Cloud |
3aa8ec71 GX |
6 | */ |
7 | #include "internal.h" | |
8 | ||
a6b9b1d5 | 9 | static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx, |
ecce9212 | 10 | void *dentry_blk, struct erofs_dirent *de, |
5b5c96c6 | 11 | unsigned int nameoff0, unsigned int maxsize) |
3aa8ec71 | 12 | { |
5b5c96c6 | 13 | const struct erofs_dirent *end = dentry_blk + nameoff0; |
3aa8ec71 | 14 | |
3aa8ec71 | 15 | while (de < end) { |
5b5c96c6 HL |
16 | unsigned char d_type = fs_ftype_to_dtype(de->file_type); |
17 | unsigned int nameoff = le16_to_cpu(de->nameoff); | |
18 | const char *de_name = (char *)dentry_blk + nameoff; | |
33bac912 | 19 | unsigned int de_namelen; |
3aa8ec71 | 20 | |
33bac912 GX |
21 | /* the last dirent in the block? */ |
22 | if (de + 1 >= end) | |
23 | de_namelen = strnlen(de_name, maxsize - nameoff); | |
24 | else | |
25 | de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; | |
3aa8ec71 | 26 | |
8b987bca | 27 | /* a corrupted entry is found */ |
8d8a09b0 GX |
28 | if (nameoff + de_namelen > maxsize || |
29 | de_namelen > EROFS_NAME_LEN) { | |
4f761fa2 GX |
30 | erofs_err(dir->i_sb, "bogus dirent @ nid %llu", |
31 | EROFS_I(dir)->nid); | |
8b987bca | 32 | DBG_BUGON(1); |
a6b9b1d5 | 33 | return -EFSCORRUPTED; |
8b987bca | 34 | } |
3aa8ec71 | 35 | |
3aa8ec71 | 36 | if (!dir_emit(ctx, de_name, de_namelen, |
019ec6c1 | 37 | le64_to_cpu(de->nid), d_type)) |
3aa8ec71 GX |
38 | return 1; |
39 | ++de; | |
ecce9212 | 40 | ctx->pos += sizeof(struct erofs_dirent); |
3aa8ec71 | 41 | } |
3aa8ec71 GX |
42 | return 0; |
43 | } | |
44 | ||
45 | static int erofs_readdir(struct file *f, struct dir_context *ctx) | |
46 | { | |
47 | struct inode *dir = file_inode(f); | |
fe5de585 | 48 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
3acea5fc JX |
49 | struct super_block *sb = dir->i_sb; |
50 | unsigned long bsz = sb->s_blocksize; | |
3acea5fc | 51 | unsigned int ofs = erofs_blkoff(sb, ctx->pos); |
3aa8ec71 GX |
52 | int err = 0; |
53 | bool initial = true; | |
54 | ||
958b9f85 | 55 | buf.mapping = dir->i_mapping; |
5b5c96c6 HL |
56 | while (ctx->pos < dir->i_size) { |
57 | erofs_off_t dbstart = ctx->pos - ofs; | |
3aa8ec71 | 58 | struct erofs_dirent *de; |
7dd68b14 | 59 | unsigned int nameoff, maxsize; |
3aa8ec71 | 60 | |
706e50e4 | 61 | de = erofs_bread(&buf, dbstart, true); |
fe5de585 | 62 | if (IS_ERR(de)) { |
2e1473d5 | 63 | erofs_err(sb, "failed to readdir of logical block %llu of nid %llu", |
5b5c96c6 | 64 | erofs_blknr(sb, dbstart), EROFS_I(dir)->nid); |
fe5de585 | 65 | err = PTR_ERR(de); |
acb383f1 GX |
66 | break; |
67 | } | |
3aa8ec71 | 68 | |
3aa8ec71 | 69 | nameoff = le16_to_cpu(de->nameoff); |
3acea5fc JX |
70 | if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) { |
71 | erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu", | |
4f761fa2 | 72 | nameoff, EROFS_I(dir)->nid); |
a6b9b1d5 | 73 | err = -EFSCORRUPTED; |
ecce9212 | 74 | break; |
3aa8ec71 GX |
75 | } |
76 | ||
5b5c96c6 | 77 | maxsize = min_t(unsigned int, dir->i_size - dbstart, bsz); |
3aa8ec71 | 78 | /* search dirents at the arbitrary position */ |
8d8a09b0 | 79 | if (initial) { |
3aa8ec71 | 80 | initial = false; |
3aa8ec71 | 81 | ofs = roundup(ofs, sizeof(struct erofs_dirent)); |
5b5c96c6 | 82 | ctx->pos = dbstart + ofs; |
3aa8ec71 GX |
83 | } |
84 | ||
ecce9212 | 85 | err = erofs_fill_dentries(dir, ctx, de, (void *)de + ofs, |
a6b9b1d5 | 86 | nameoff, maxsize); |
8d8a09b0 | 87 | if (err) |
3aa8ec71 | 88 | break; |
5b5c96c6 | 89 | ctx->pos = dbstart + maxsize; |
3aa8ec71 GX |
90 | ofs = 0; |
91 | } | |
fe5de585 | 92 | erofs_put_metabuf(&buf); |
e9dfe33a GX |
93 | if (EROFS_I(dir)->dot_omitted && ctx->pos == dir->i_size) { |
94 | if (!dir_emit_dot(f, ctx)) | |
95 | return 0; | |
96 | ++ctx->pos; | |
97 | } | |
3aa8ec71 GX |
98 | return err < 0 ? err : 0; |
99 | } | |
100 | ||
101 | const struct file_operations erofs_dir_fops = { | |
102 | .llseek = generic_file_llseek, | |
103 | .read = generic_read_dir, | |
bee15682 | 104 | .iterate_shared = erofs_readdir, |
3aa8ec71 | 105 | }; |