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