Commit | Line | Data |
---|---|---|
3aa8ec71 GX |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * linux/drivers/staging/erofs/dir.c | |
4 | * | |
5 | * Copyright (C) 2017-2018 HUAWEI, Inc. | |
6 | * http://www.huawei.com/ | |
7 | * Created by Gao Xiang <gaoxiang25@huawei.com> | |
8 | * | |
9 | * This file is subject to the terms and conditions of the GNU General Public | |
10 | * License. See the file COPYING in the main directory of the Linux | |
11 | * distribution for more details. | |
12 | */ | |
13 | #include "internal.h" | |
14 | ||
15 | static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = { | |
16 | [EROFS_FT_UNKNOWN] = DT_UNKNOWN, | |
17 | [EROFS_FT_REG_FILE] = DT_REG, | |
18 | [EROFS_FT_DIR] = DT_DIR, | |
19 | [EROFS_FT_CHRDEV] = DT_CHR, | |
20 | [EROFS_FT_BLKDEV] = DT_BLK, | |
21 | [EROFS_FT_FIFO] = DT_FIFO, | |
22 | [EROFS_FT_SOCK] = DT_SOCK, | |
23 | [EROFS_FT_SYMLINK] = DT_LNK, | |
24 | }; | |
25 | ||
26 | static int erofs_fill_dentries(struct dir_context *ctx, | |
cbebe5d0 BP |
27 | void *dentry_blk, unsigned int *ofs, |
28 | unsigned int nameoff, unsigned int maxsize) | |
3aa8ec71 GX |
29 | { |
30 | struct erofs_dirent *de = dentry_blk; | |
31 | const struct erofs_dirent *end = dentry_blk + nameoff; | |
32 | ||
33 | de = dentry_blk + *ofs; | |
34 | while (de < end) { | |
35 | const char *de_name; | |
36 | int de_namelen; | |
37 | unsigned char d_type; | |
38 | #ifdef CONFIG_EROFS_FS_DEBUG | |
7dd68b14 | 39 | unsigned int dbg_namelen; |
3aa8ec71 GX |
40 | unsigned char dbg_namebuf[EROFS_NAME_LEN]; |
41 | #endif | |
42 | ||
43 | if (unlikely(de->file_type < EROFS_FT_MAX)) | |
44 | d_type = erofs_filetype_table[de->file_type]; | |
45 | else | |
46 | d_type = DT_UNKNOWN; | |
47 | ||
48 | nameoff = le16_to_cpu(de->nameoff); | |
49 | de_name = (char *)dentry_blk + nameoff; | |
50 | ||
51 | de_namelen = unlikely(de + 1 >= end) ? | |
52 | /* last directory entry */ | |
53 | strnlen(de_name, maxsize - nameoff) : | |
54 | le16_to_cpu(de[1].nameoff) - nameoff; | |
55 | ||
8b987bca GX |
56 | /* a corrupted entry is found */ |
57 | if (unlikely(de_namelen < 0)) { | |
58 | DBG_BUGON(1); | |
59 | return -EIO; | |
60 | } | |
3aa8ec71 GX |
61 | |
62 | #ifdef CONFIG_EROFS_FS_DEBUG | |
63 | dbg_namelen = min(EROFS_NAME_LEN - 1, de_namelen); | |
64 | memcpy(dbg_namebuf, de_name, dbg_namelen); | |
65 | dbg_namebuf[dbg_namelen] = '\0'; | |
66 | ||
67 | debugln("%s, found de_name %s de_len %d d_type %d", __func__, | |
68 | dbg_namebuf, de_namelen, d_type); | |
69 | #endif | |
70 | ||
71 | if (!dir_emit(ctx, de_name, de_namelen, | |
019ec6c1 AS |
72 | le64_to_cpu(de->nid), d_type)) |
73 | /* stopped by some reason */ | |
3aa8ec71 GX |
74 | return 1; |
75 | ++de; | |
76 | *ofs += sizeof(struct erofs_dirent); | |
77 | } | |
78 | *ofs = maxsize; | |
79 | return 0; | |
80 | } | |
81 | ||
82 | static int erofs_readdir(struct file *f, struct dir_context *ctx) | |
83 | { | |
84 | struct inode *dir = file_inode(f); | |
85 | struct address_space *mapping = dir->i_mapping; | |
86 | const size_t dirsize = i_size_read(dir); | |
7dd68b14 TW |
87 | unsigned int i = ctx->pos / EROFS_BLKSIZ; |
88 | unsigned int ofs = ctx->pos % EROFS_BLKSIZ; | |
3aa8ec71 GX |
89 | int err = 0; |
90 | bool initial = true; | |
91 | ||
92 | while (ctx->pos < dirsize) { | |
93 | struct page *dentry_page; | |
94 | struct erofs_dirent *de; | |
7dd68b14 | 95 | unsigned int nameoff, maxsize; |
3aa8ec71 GX |
96 | |
97 | dentry_page = read_mapping_page(mapping, i, NULL); | |
98 | if (IS_ERR(dentry_page)) | |
99 | continue; | |
100 | ||
3aa8ec71 GX |
101 | de = (struct erofs_dirent *)kmap(dentry_page); |
102 | ||
103 | nameoff = le16_to_cpu(de->nameoff); | |
104 | ||
105 | if (unlikely(nameoff < sizeof(struct erofs_dirent) || | |
cbebe5d0 | 106 | nameoff >= PAGE_SIZE)) { |
3aa8ec71 | 107 | errln("%s, invalid de[0].nameoff %u", |
cbebe5d0 | 108 | __func__, nameoff); |
3aa8ec71 GX |
109 | |
110 | err = -EIO; | |
111 | goto skip_this; | |
112 | } | |
113 | ||
7dd68b14 TW |
114 | maxsize = min_t(unsigned int, |
115 | dirsize - ctx->pos + ofs, PAGE_SIZE); | |
3aa8ec71 GX |
116 | |
117 | /* search dirents at the arbitrary position */ | |
118 | if (unlikely(initial)) { | |
119 | initial = false; | |
120 | ||
121 | ofs = roundup(ofs, sizeof(struct erofs_dirent)); | |
122 | if (unlikely(ofs >= nameoff)) | |
123 | goto skip_this; | |
124 | } | |
125 | ||
126 | err = erofs_fill_dentries(ctx, de, &ofs, nameoff, maxsize); | |
127 | skip_this: | |
128 | kunmap(dentry_page); | |
129 | ||
3aa8ec71 GX |
130 | put_page(dentry_page); |
131 | ||
132 | ctx->pos = blknr_to_addr(i) + ofs; | |
133 | ||
134 | if (unlikely(err)) | |
135 | break; | |
136 | ++i; | |
137 | ofs = 0; | |
138 | } | |
139 | return err < 0 ? err : 0; | |
140 | } | |
141 | ||
142 | const struct file_operations erofs_dir_fops = { | |
143 | .llseek = generic_file_llseek, | |
144 | .read = generic_read_dir, | |
145 | .iterate = erofs_readdir, | |
146 | }; | |
147 |