diff options
Diffstat (limited to 'fs/xfs/xfs_dir2_data.c')
-rw-r--r-- | fs/xfs/xfs_dir2_data.c | 170 |
1 files changed, 138 insertions, 32 deletions
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index 44ffd4d6bc91..ffcf1774152e 100644 --- a/fs/xfs/xfs_dir2_data.c +++ b/fs/xfs/xfs_dir2_data.c | |||
@@ -34,14 +34,13 @@ | |||
34 | STATIC xfs_dir2_data_free_t * | 34 | STATIC xfs_dir2_data_free_t * |
35 | xfs_dir2_data_freefind(xfs_dir2_data_hdr_t *hdr, xfs_dir2_data_unused_t *dup); | 35 | xfs_dir2_data_freefind(xfs_dir2_data_hdr_t *hdr, xfs_dir2_data_unused_t *dup); |
36 | 36 | ||
37 | #ifdef DEBUG | ||
38 | /* | 37 | /* |
39 | * Check the consistency of the data block. | 38 | * Check the consistency of the data block. |
40 | * The input can also be a block-format directory. | 39 | * The input can also be a block-format directory. |
41 | * Pop an assert if we find anything bad. | 40 | * Return 0 is the buffer is good, otherwise an error. |
42 | */ | 41 | */ |
43 | void | 42 | int |
44 | xfs_dir2_data_check( | 43 | __xfs_dir2_data_check( |
45 | struct xfs_inode *dp, /* incore inode pointer */ | 44 | struct xfs_inode *dp, /* incore inode pointer */ |
46 | struct xfs_buf *bp) /* data block's buffer */ | 45 | struct xfs_buf *bp) /* data block's buffer */ |
47 | { | 46 | { |
@@ -64,18 +63,23 @@ xfs_dir2_data_check( | |||
64 | int stale; /* count of stale leaves */ | 63 | int stale; /* count of stale leaves */ |
65 | struct xfs_name name; | 64 | struct xfs_name name; |
66 | 65 | ||
67 | mp = dp->i_mount; | 66 | mp = bp->b_target->bt_mount; |
68 | hdr = bp->b_addr; | 67 | hdr = bp->b_addr; |
69 | bf = hdr->bestfree; | 68 | bf = hdr->bestfree; |
70 | p = (char *)(hdr + 1); | 69 | p = (char *)(hdr + 1); |
71 | 70 | ||
72 | if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC)) { | 71 | switch (hdr->magic) { |
72 | case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC): | ||
73 | btp = xfs_dir2_block_tail_p(mp, hdr); | 73 | btp = xfs_dir2_block_tail_p(mp, hdr); |
74 | lep = xfs_dir2_block_leaf_p(btp); | 74 | lep = xfs_dir2_block_leaf_p(btp); |
75 | endp = (char *)lep; | 75 | endp = (char *)lep; |
76 | } else { | 76 | break; |
77 | ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC)); | 77 | case cpu_to_be32(XFS_DIR2_DATA_MAGIC): |
78 | endp = (char *)hdr + mp->m_dirblksize; | 78 | endp = (char *)hdr + mp->m_dirblksize; |
79 | break; | ||
80 | default: | ||
81 | XFS_ERROR_REPORT("Bad Magic", XFS_ERRLEVEL_LOW, mp); | ||
82 | return EFSCORRUPTED; | ||
79 | } | 83 | } |
80 | 84 | ||
81 | count = lastfree = freeseen = 0; | 85 | count = lastfree = freeseen = 0; |
@@ -83,19 +87,22 @@ xfs_dir2_data_check( | |||
83 | * Account for zero bestfree entries. | 87 | * Account for zero bestfree entries. |
84 | */ | 88 | */ |
85 | if (!bf[0].length) { | 89 | if (!bf[0].length) { |
86 | ASSERT(!bf[0].offset); | 90 | XFS_WANT_CORRUPTED_RETURN(!bf[0].offset); |
87 | freeseen |= 1 << 0; | 91 | freeseen |= 1 << 0; |
88 | } | 92 | } |
89 | if (!bf[1].length) { | 93 | if (!bf[1].length) { |
90 | ASSERT(!bf[1].offset); | 94 | XFS_WANT_CORRUPTED_RETURN(!bf[1].offset); |
91 | freeseen |= 1 << 1; | 95 | freeseen |= 1 << 1; |
92 | } | 96 | } |
93 | if (!bf[2].length) { | 97 | if (!bf[2].length) { |
94 | ASSERT(!bf[2].offset); | 98 | XFS_WANT_CORRUPTED_RETURN(!bf[2].offset); |
95 | freeseen |= 1 << 2; | 99 | freeseen |= 1 << 2; |
96 | } | 100 | } |
97 | ASSERT(be16_to_cpu(bf[0].length) >= be16_to_cpu(bf[1].length)); | 101 | |
98 | ASSERT(be16_to_cpu(bf[1].length) >= be16_to_cpu(bf[2].length)); | 102 | XFS_WANT_CORRUPTED_RETURN(be16_to_cpu(bf[0].length) >= |
103 | be16_to_cpu(bf[1].length)); | ||
104 | XFS_WANT_CORRUPTED_RETURN(be16_to_cpu(bf[1].length) >= | ||
105 | be16_to_cpu(bf[2].length)); | ||
99 | /* | 106 | /* |
100 | * Loop over the data/unused entries. | 107 | * Loop over the data/unused entries. |
101 | */ | 108 | */ |
@@ -107,17 +114,20 @@ xfs_dir2_data_check( | |||
107 | * doesn't need to be there. | 114 | * doesn't need to be there. |
108 | */ | 115 | */ |
109 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { | 116 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
110 | ASSERT(lastfree == 0); | 117 | XFS_WANT_CORRUPTED_RETURN(lastfree == 0); |
111 | ASSERT(be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) == | 118 | XFS_WANT_CORRUPTED_RETURN( |
112 | (char *)dup - (char *)hdr); | 119 | be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) == |
120 | (char *)dup - (char *)hdr); | ||
113 | dfp = xfs_dir2_data_freefind(hdr, dup); | 121 | dfp = xfs_dir2_data_freefind(hdr, dup); |
114 | if (dfp) { | 122 | if (dfp) { |
115 | i = (int)(dfp - bf); | 123 | i = (int)(dfp - bf); |
116 | ASSERT((freeseen & (1 << i)) == 0); | 124 | XFS_WANT_CORRUPTED_RETURN( |
125 | (freeseen & (1 << i)) == 0); | ||
117 | freeseen |= 1 << i; | 126 | freeseen |= 1 << i; |
118 | } else { | 127 | } else { |
119 | ASSERT(be16_to_cpu(dup->length) <= | 128 | XFS_WANT_CORRUPTED_RETURN( |
120 | be16_to_cpu(bf[2].length)); | 129 | be16_to_cpu(dup->length) <= |
130 | be16_to_cpu(bf[2].length)); | ||
121 | } | 131 | } |
122 | p += be16_to_cpu(dup->length); | 132 | p += be16_to_cpu(dup->length); |
123 | lastfree = 1; | 133 | lastfree = 1; |
@@ -130,10 +140,12 @@ xfs_dir2_data_check( | |||
130 | * The linear search is crude but this is DEBUG code. | 140 | * The linear search is crude but this is DEBUG code. |
131 | */ | 141 | */ |
132 | dep = (xfs_dir2_data_entry_t *)p; | 142 | dep = (xfs_dir2_data_entry_t *)p; |
133 | ASSERT(dep->namelen != 0); | 143 | XFS_WANT_CORRUPTED_RETURN(dep->namelen != 0); |
134 | ASSERT(xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber)) == 0); | 144 | XFS_WANT_CORRUPTED_RETURN( |
135 | ASSERT(be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep)) == | 145 | !xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber))); |
136 | (char *)dep - (char *)hdr); | 146 | XFS_WANT_CORRUPTED_RETURN( |
147 | be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep)) == | ||
148 | (char *)dep - (char *)hdr); | ||
137 | count++; | 149 | count++; |
138 | lastfree = 0; | 150 | lastfree = 0; |
139 | if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC)) { | 151 | if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC)) { |
@@ -148,27 +160,122 @@ xfs_dir2_data_check( | |||
148 | be32_to_cpu(lep[i].hashval) == hash) | 160 | be32_to_cpu(lep[i].hashval) == hash) |
149 | break; | 161 | break; |
150 | } | 162 | } |
151 | ASSERT(i < be32_to_cpu(btp->count)); | 163 | XFS_WANT_CORRUPTED_RETURN(i < be32_to_cpu(btp->count)); |
152 | } | 164 | } |
153 | p += xfs_dir2_data_entsize(dep->namelen); | 165 | p += xfs_dir2_data_entsize(dep->namelen); |
154 | } | 166 | } |
155 | /* | 167 | /* |
156 | * Need to have seen all the entries and all the bestfree slots. | 168 | * Need to have seen all the entries and all the bestfree slots. |
157 | */ | 169 | */ |
158 | ASSERT(freeseen == 7); | 170 | XFS_WANT_CORRUPTED_RETURN(freeseen == 7); |
159 | if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC)) { | 171 | if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC)) { |
160 | for (i = stale = 0; i < be32_to_cpu(btp->count); i++) { | 172 | for (i = stale = 0; i < be32_to_cpu(btp->count); i++) { |
161 | if (lep[i].address == | 173 | if (lep[i].address == |
162 | cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) | 174 | cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) |
163 | stale++; | 175 | stale++; |
164 | if (i > 0) | 176 | if (i > 0) |
165 | ASSERT(be32_to_cpu(lep[i].hashval) >= be32_to_cpu(lep[i - 1].hashval)); | 177 | XFS_WANT_CORRUPTED_RETURN( |
178 | be32_to_cpu(lep[i].hashval) >= | ||
179 | be32_to_cpu(lep[i - 1].hashval)); | ||
166 | } | 180 | } |
167 | ASSERT(count == be32_to_cpu(btp->count) - be32_to_cpu(btp->stale)); | 181 | XFS_WANT_CORRUPTED_RETURN(count == |
168 | ASSERT(stale == be32_to_cpu(btp->stale)); | 182 | be32_to_cpu(btp->count) - be32_to_cpu(btp->stale)); |
183 | XFS_WANT_CORRUPTED_RETURN(stale == be32_to_cpu(btp->stale)); | ||
169 | } | 184 | } |
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static void | ||
189 | xfs_dir2_data_verify( | ||
190 | struct xfs_buf *bp) | ||
191 | { | ||
192 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
193 | struct xfs_dir2_data_hdr *hdr = bp->b_addr; | ||
194 | int block_ok = 0; | ||
195 | |||
196 | block_ok = hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC); | ||
197 | block_ok = block_ok && __xfs_dir2_data_check(NULL, bp) == 0; | ||
198 | |||
199 | if (!block_ok) { | ||
200 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, hdr); | ||
201 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Readahead of the first block of the directory when it is opened is completely | ||
207 | * oblivious to the format of the directory. Hence we can either get a block | ||
208 | * format buffer or a data format buffer on readahead. | ||
209 | */ | ||
210 | static void | ||
211 | xfs_dir2_data_reada_verify( | ||
212 | struct xfs_buf *bp) | ||
213 | { | ||
214 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
215 | struct xfs_dir2_data_hdr *hdr = bp->b_addr; | ||
216 | |||
217 | switch (hdr->magic) { | ||
218 | case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC): | ||
219 | bp->b_ops = &xfs_dir2_block_buf_ops; | ||
220 | bp->b_ops->verify_read(bp); | ||
221 | return; | ||
222 | case cpu_to_be32(XFS_DIR2_DATA_MAGIC): | ||
223 | xfs_dir2_data_verify(bp); | ||
224 | return; | ||
225 | default: | ||
226 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, hdr); | ||
227 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
228 | break; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void | ||
233 | xfs_dir2_data_read_verify( | ||
234 | struct xfs_buf *bp) | ||
235 | { | ||
236 | xfs_dir2_data_verify(bp); | ||
237 | } | ||
238 | |||
239 | static void | ||
240 | xfs_dir2_data_write_verify( | ||
241 | struct xfs_buf *bp) | ||
242 | { | ||
243 | xfs_dir2_data_verify(bp); | ||
244 | } | ||
245 | |||
246 | const struct xfs_buf_ops xfs_dir2_data_buf_ops = { | ||
247 | .verify_read = xfs_dir2_data_read_verify, | ||
248 | .verify_write = xfs_dir2_data_write_verify, | ||
249 | }; | ||
250 | |||
251 | static const struct xfs_buf_ops xfs_dir2_data_reada_buf_ops = { | ||
252 | .verify_read = xfs_dir2_data_reada_verify, | ||
253 | .verify_write = xfs_dir2_data_write_verify, | ||
254 | }; | ||
255 | |||
256 | |||
257 | int | ||
258 | xfs_dir2_data_read( | ||
259 | struct xfs_trans *tp, | ||
260 | struct xfs_inode *dp, | ||
261 | xfs_dablk_t bno, | ||
262 | xfs_daddr_t mapped_bno, | ||
263 | struct xfs_buf **bpp) | ||
264 | { | ||
265 | return xfs_da_read_buf(tp, dp, bno, mapped_bno, bpp, | ||
266 | XFS_DATA_FORK, &xfs_dir2_data_buf_ops); | ||
267 | } | ||
268 | |||
269 | int | ||
270 | xfs_dir2_data_readahead( | ||
271 | struct xfs_trans *tp, | ||
272 | struct xfs_inode *dp, | ||
273 | xfs_dablk_t bno, | ||
274 | xfs_daddr_t mapped_bno) | ||
275 | { | ||
276 | return xfs_da_reada_buf(tp, dp, bno, mapped_bno, | ||
277 | XFS_DATA_FORK, &xfs_dir2_data_reada_buf_ops); | ||
170 | } | 278 | } |
171 | #endif | ||
172 | 279 | ||
173 | /* | 280 | /* |
174 | * Given a data block and an unused entry from that block, | 281 | * Given a data block and an unused entry from that block, |
@@ -409,10 +516,9 @@ xfs_dir2_data_init( | |||
409 | */ | 516 | */ |
410 | error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, blkno), -1, &bp, | 517 | error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, blkno), -1, &bp, |
411 | XFS_DATA_FORK); | 518 | XFS_DATA_FORK); |
412 | if (error) { | 519 | if (error) |
413 | return error; | 520 | return error; |
414 | } | 521 | bp->b_ops = &xfs_dir2_data_buf_ops; |
415 | ASSERT(bp != NULL); | ||
416 | 522 | ||
417 | /* | 523 | /* |
418 | * Initialize the header. | 524 | * Initialize the header. |