15
15
16
16
#include <trace/events/erofs.h>
17
17
18
- struct erofs_qstr {
19
- const unsigned char * name ;
20
- const unsigned char * end ;
21
- };
22
-
23
- /* based on the end of qn is accurate and it must have the trailing '\0' */
24
- static inline int dirnamecmp (const struct erofs_qstr * qn ,
25
- const struct erofs_qstr * qd ,
26
- unsigned int * matched )
18
+ /* based on the value of qn->len is accurate */
19
+ static inline int dirnamecmp (struct qstr * qn ,
20
+ struct qstr * qd , unsigned int * matched )
27
21
{
28
- unsigned int i = * matched ;
29
-
30
- /*
31
- * on-disk error, let's only BUG_ON in the debugging mode.
32
- * otherwise, it will return 1 to just skip the invalid name
33
- * and go on (in consideration of the lookup performance).
34
- */
35
- DBG_BUGON (qd -> name > qd -> end );
36
-
37
- /* qd could not have trailing '\0' */
38
- /* However it is absolutely safe if < qd->end */
39
- while (qd -> name + i < qd -> end && qd -> name [i ] != '\0' ) {
40
- if (qn -> name [i ] != qd -> name [i ]) {
41
- * matched = i ;
42
- return qn -> name [i ] > qd -> name [i ] ? 1 : -1 ;
22
+ unsigned int i = * matched , len = min (qn -> len , qd -> len );
23
+ loop :
24
+ if (unlikely (i >= len )) {
25
+ * matched = i ;
26
+ if (qn -> len < qd -> len ) {
27
+ /*
28
+ * actually (qn->len == qd->len)
29
+ * when qd->name[i] == '\0'
30
+ */
31
+ return qd -> name [i ] == '\0' ? 0 : -1 ;
43
32
}
44
- ++ i ;
33
+ return (qn -> len > qd -> len );
34
+ }
35
+
36
+ if (qn -> name [i ] != qd -> name [i ]) {
37
+ * matched = i ;
38
+ return qn -> name [i ] > qd -> name [i ] ? 1 : -1 ;
45
39
}
46
- * matched = i ;
47
- /* See comments in __d_alloc on the terminating NUL character */
48
- return qn -> name [i ] == '\0' ? 0 : 1 ;
49
- }
50
40
51
- #define nameoff_from_disk (off , sz ) (le16_to_cpu(off) & ((sz) - 1))
41
+ ++ i ;
42
+ goto loop ;
43
+ }
52
44
53
- static struct erofs_dirent * find_target_dirent (struct erofs_qstr * name ,
54
- u8 * data ,
55
- unsigned int dirblksize ,
56
- const int ndirents )
45
+ static struct erofs_dirent * find_target_dirent (
46
+ struct qstr * name ,
47
+ u8 * data , int maxsize )
57
48
{
58
- int head , back ;
49
+ unsigned int ndirents , head , back ;
59
50
unsigned int startprfx , endprfx ;
60
51
struct erofs_dirent * const de = (struct erofs_dirent * )data ;
61
52
53
+ /* make sure that maxsize is valid */
54
+ BUG_ON (maxsize < sizeof (struct erofs_dirent ));
55
+
56
+ ndirents = le16_to_cpu (de -> nameoff ) / sizeof (* de );
57
+
58
+ /* corrupted dir (may be unnecessary...) */
59
+ BUG_ON (!ndirents );
60
+
62
61
head = 0 ;
63
62
back = ndirents - 1 ;
64
63
startprfx = endprfx = 0 ;
65
64
66
65
while (head <= back ) {
67
- const int mid = head + (back - head ) / 2 ;
68
- const int nameoff = nameoff_from_disk (de [mid ].nameoff ,
69
- dirblksize );
66
+ unsigned int mid = head + (back - head ) / 2 ;
67
+ unsigned int nameoff = le16_to_cpu (de [mid ].nameoff );
70
68
unsigned int matched = min (startprfx , endprfx );
71
- struct erofs_qstr dname = {
72
- .name = data + nameoff ,
73
- .end = unlikely (mid >= ndirents - 1 ) ?
74
- data + dirblksize :
75
- data + nameoff_from_disk (de [mid + 1 ].nameoff ,
76
- dirblksize )
77
- };
69
+
70
+ struct qstr dname = QSTR_INIT (data + nameoff ,
71
+ unlikely (mid >= ndirents - 1 ) ?
72
+ maxsize - nameoff :
73
+ le16_to_cpu (de [mid + 1 ].nameoff ) - nameoff );
78
74
79
75
/* string comparison without already matched prefix */
80
76
int ret = dirnamecmp (name , & dname , & matched );
81
77
82
- if (unlikely (!ret )) {
78
+ if (unlikely (!ret ))
83
79
return de + mid ;
84
- } else if (ret > 0 ) {
80
+ else if (ret > 0 ) {
85
81
head = mid + 1 ;
86
82
startprfx = matched ;
87
- } else {
83
+ } else if (unlikely (mid < 1 )) /* fix "mid" overflow */
84
+ break ;
85
+ else {
88
86
back = mid - 1 ;
89
87
endprfx = matched ;
90
88
}
@@ -93,13 +91,12 @@ static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name,
93
91
return ERR_PTR (- ENOENT );
94
92
}
95
93
96
- static struct page * find_target_block_classic (struct inode * dir ,
97
- struct erofs_qstr * name ,
98
- int * _diff ,
99
- int * _ndirents )
94
+ static struct page * find_target_block_classic (
95
+ struct inode * dir ,
96
+ struct qstr * name , int * _diff )
100
97
{
101
98
unsigned int startprfx , endprfx ;
102
- int head , back ;
99
+ unsigned int head , back ;
103
100
struct address_space * const mapping = dir -> i_mapping ;
104
101
struct page * candidate = ERR_PTR (- ENOENT );
105
102
@@ -108,85 +105,77 @@ static struct page *find_target_block_classic(struct inode *dir,
108
105
back = inode_datablocks (dir ) - 1 ;
109
106
110
107
while (head <= back ) {
111
- const int mid = head + (back - head ) / 2 ;
108
+ unsigned int mid = head + (back - head ) / 2 ;
112
109
struct page * page = read_mapping_page (mapping , mid , NULL );
113
110
114
- if (!IS_ERR (page )) {
115
- struct erofs_dirent * de = kmap_atomic (page );
116
- const int nameoff = nameoff_from_disk (de -> nameoff ,
117
- EROFS_BLKSIZ );
118
- const int ndirents = nameoff / sizeof (* de );
111
+ if (IS_ERR (page )) {
112
+ exact_out :
113
+ if (!IS_ERR (candidate )) /* valid candidate */
114
+ put_page (candidate );
115
+ return page ;
116
+ } else {
119
117
int diff ;
120
- unsigned int matched ;
121
- struct erofs_qstr dname ;
118
+ unsigned int ndirents , matched ;
119
+ struct qstr dname ;
120
+ struct erofs_dirent * de = kmap_atomic (page );
121
+ unsigned int nameoff = le16_to_cpu (de -> nameoff );
122
122
123
- if (unlikely (!ndirents )) {
124
- DBG_BUGON (1 );
125
- put_page (page );
126
- page = ERR_PTR (- EIO );
127
- goto out ;
128
- }
123
+ ndirents = nameoff / sizeof (* de );
124
+
125
+ /* corrupted dir (should have one entry at least) */
126
+ BUG_ON (!ndirents || nameoff > PAGE_SIZE );
129
127
130
128
matched = min (startprfx , endprfx );
131
129
132
130
dname .name = (u8 * )de + nameoff ;
133
- if (ndirents == 1 )
134
- dname .end = (u8 * )de + EROFS_BLKSIZ ;
135
- else
136
- dname .end = (u8 * )de +
137
- nameoff_from_disk (de [1 ].nameoff ,
138
- EROFS_BLKSIZ );
131
+ dname .len = ndirents == 1 ?
132
+ /* since the rest of the last page is 0 */
133
+ EROFS_BLKSIZ - nameoff
134
+ : le16_to_cpu (de [1 ].nameoff ) - nameoff ;
139
135
140
136
/* string comparison without already matched prefix */
141
137
diff = dirnamecmp (name , & dname , & matched );
142
138
kunmap_atomic (de );
143
139
144
140
if (unlikely (!diff )) {
145
141
* _diff = 0 ;
146
- goto out ;
142
+ goto exact_out ;
147
143
} else if (diff > 0 ) {
148
144
head = mid + 1 ;
149
145
startprfx = matched ;
150
146
151
147
if (likely (!IS_ERR (candidate )))
152
148
put_page (candidate );
153
149
candidate = page ;
154
- * _ndirents = ndirents ;
155
150
} else {
156
151
put_page (page );
157
152
153
+ if (unlikely (mid < 1 )) /* fix "mid" overflow */
154
+ break ;
155
+
158
156
back = mid - 1 ;
159
157
endprfx = matched ;
160
158
}
161
- continue ;
162
159
}
163
- out : /* free if the candidate is valid */
164
- if (!IS_ERR (candidate ))
165
- put_page (candidate );
166
- return page ;
167
160
}
168
161
* _diff = 1 ;
169
162
return candidate ;
170
163
}
171
164
172
165
int erofs_namei (struct inode * dir ,
173
- struct qstr * name ,
174
- erofs_nid_t * nid , unsigned int * d_type )
166
+ struct qstr * name ,
167
+ erofs_nid_t * nid , unsigned int * d_type )
175
168
{
176
- int diff , ndirents ;
169
+ int diff ;
177
170
struct page * page ;
178
171
u8 * data ;
179
172
struct erofs_dirent * de ;
180
- struct erofs_qstr qn ;
181
173
182
174
if (unlikely (!dir -> i_size ))
183
175
return - ENOENT ;
184
176
185
- qn .name = name -> name ;
186
- qn .end = name -> name + name -> len ;
187
-
188
177
diff = 1 ;
189
- page = find_target_block_classic (dir , & qn , & diff , & ndirents );
178
+ page = find_target_block_classic (dir , name , & diff );
190
179
191
180
if (unlikely (IS_ERR (page )))
192
181
return PTR_ERR (page );
@@ -195,7 +184,7 @@ int erofs_namei(struct inode *dir,
195
184
/* the target page has been mapped */
196
185
de = likely (diff ) ?
197
186
/* since the rest of the last page is 0 */
198
- find_target_dirent (& qn , data , EROFS_BLKSIZ , ndirents ) :
187
+ find_target_dirent (name , data , EROFS_BLKSIZ ) :
199
188
(struct erofs_dirent * )data ;
200
189
201
190
if (likely (!IS_ERR (de ))) {
0 commit comments