diff options
author | Dave Chinner <dchinner@redhat.com> | 2012-11-12 06:54:12 -0500 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2012-11-15 22:34:41 -0500 |
commit | 82025d7f79148fe66a1594a0ebe4ab38152cf9e6 (patch) | |
tree | fb47428d6604ebb5f941d3f8c58e1de9d54a4b59 /fs/xfs/xfs_dir2_data.c | |
parent | 20f7e9f3726a27cccade65c28265eef8ca50eecb (diff) |
xfs: verify dir2 block format buffers
Add a dir2 block format read verifier. To fully verify every block
when read, call xfs_dir2_data_check() on them. Change
xfs_dir2_data_check() to do runtime checking, convert ASSERT()
checks to XFS_WANT_CORRUPTED_RETURN(), which will trigger an ASSERT
failure on debug kernels, but on production kernels will dump an
error to dmesg and return EFSCORRUPTED to the caller.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Phil White <pwhite@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_dir2_data.c')
-rw-r--r-- | fs/xfs/xfs_dir2_data.c | 73 |
1 files changed, 44 insertions, 29 deletions
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index 44ffd4d6bc91..cb117234e32e 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,30 @@ 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; | ||
170 | } | 186 | } |
171 | #endif | ||
172 | 187 | ||
173 | /* | 188 | /* |
174 | * Given a data block and an unused entry from that block, | 189 | * Given a data block and an unused entry from that block, |