Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Copyright (C) 2022-2023 Oracle. All Rights Reserved.
4 : * Author: Darrick J. Wong <djwong@kernel.org>
5 : */
6 : #include "xfs.h"
7 : #include "xfs_fs.h"
8 : #include "xfs_shared.h"
9 : #include "xfs_format.h"
10 : #include "xfs_log_format.h"
11 : #include "xfs_trans_resv.h"
12 : #include "xfs_mount.h"
13 : #include "xfs_inode.h"
14 : #include "xfs_dir2.h"
15 : #include "xfs_dir2_priv.h"
16 : #include "xfs_trace.h"
17 : #include "xfs_bmap.h"
18 : #include "xfs_trans.h"
19 : #include "xfs_error.h"
20 : #include "scrub/scrub.h"
21 : #include "scrub/readdir.h"
22 :
23 : /* Call a function for every entry in a shortform directory. */
24 : STATIC int
25 5863055192 : xchk_dir_walk_sf(
26 : struct xfs_scrub *sc,
27 : struct xfs_inode *dp,
28 : xchk_dirent_fn dirent_fn,
29 : void *priv)
30 : {
31 5863055192 : struct xfs_name name = {
32 : .name = ".",
33 : .len = 1,
34 : .type = XFS_DIR3_FT_DIR,
35 : };
36 5863055192 : struct xfs_mount *mp = dp->i_mount;
37 5863055192 : struct xfs_da_geometry *geo = mp->m_dir_geo;
38 5863055192 : struct xfs_dir2_sf_entry *sfep;
39 5863055192 : struct xfs_dir2_sf_hdr *sfp;
40 5863055192 : xfs_ino_t ino;
41 5863055192 : xfs_dir2_dataptr_t dapos;
42 5863055192 : unsigned int i;
43 5863055192 : int error;
44 :
45 5863055192 : ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
46 5863055192 : ASSERT(dp->i_df.if_u1.if_data != NULL);
47 :
48 5863055192 : sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
49 :
50 : /* dot entry */
51 5863055192 : dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
52 5863055192 : geo->data_entry_offset);
53 :
54 5862752245 : error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
55 5863466662 : if (error)
56 : return error;
57 :
58 : /* dotdot entry */
59 5865199844 : dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
60 5865199844 : geo->data_entry_offset +
61 : xfs_dir2_data_entsize(mp, sizeof(".") - 1));
62 5864044288 : ino = xfs_dir2_sf_get_parent_ino(sfp);
63 5858793216 : name.name = "..";
64 5858793216 : name.len = 2;
65 :
66 5858793216 : error = dirent_fn(sc, dp, dapos, &name, ino, priv);
67 5861582908 : if (error)
68 : return error;
69 :
70 : /* iterate everything else */
71 5861582907 : sfep = xfs_dir2_sf_firstentry(sfp);
72 25531239843 : for (i = 0; i < sfp->count; i++) {
73 19665141191 : dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
74 : xfs_dir2_sf_get_offset(sfep));
75 19682783193 : ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
76 19673970516 : name.name = sfep->name;
77 19673970516 : name.len = sfep->namelen;
78 19673970516 : name.type = xfs_dir2_sf_get_ftype(mp, sfep);
79 :
80 19667861378 : error = dirent_fn(sc, dp, dapos, &name, ino, priv);
81 19673681891 : if (error)
82 133 : return error;
83 :
84 19673681758 : sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
85 : }
86 :
87 : return 0;
88 : }
89 :
90 : /* Call a function for every entry in a block directory. */
91 : STATIC int
92 195423555 : xchk_dir_walk_block(
93 : struct xfs_scrub *sc,
94 : struct xfs_inode *dp,
95 : xchk_dirent_fn dirent_fn,
96 : void *priv)
97 : {
98 195423555 : struct xfs_mount *mp = dp->i_mount;
99 195423555 : struct xfs_da_geometry *geo = mp->m_dir_geo;
100 195423555 : struct xfs_buf *bp;
101 195423555 : unsigned int off, next_off, end;
102 195423555 : int error;
103 :
104 195423555 : error = xfs_dir3_block_read(sc->tp, dp, dp->i_ino, &bp);
105 195430483 : if (error)
106 : return error;
107 :
108 : /* Walk each directory entry. */
109 195431193 : end = xfs_dir3_data_end_offset(geo, bp->b_addr);
110 5812236114 : for (off = geo->data_entry_offset; off < end; off = next_off) {
111 5616804529 : struct xfs_name name = { };
112 5616804529 : struct xfs_dir2_data_unused *dup = bp->b_addr + off;
113 5616804529 : struct xfs_dir2_data_entry *dep = bp->b_addr + off;
114 5616804529 : xfs_ino_t ino;
115 5616804529 : xfs_dir2_dataptr_t dapos;
116 :
117 : /* Skip an empty entry. */
118 5616804529 : if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
119 469009790 : next_off = off + be16_to_cpu(dup->length);
120 469009790 : continue;
121 : }
122 :
123 : /* Otherwise, find the next entry and report it. */
124 5147794739 : next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
125 5147794739 : if (next_off > end)
126 : break;
127 :
128 5147794739 : dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
129 5147718103 : ino = be64_to_cpu(dep->inumber);
130 5147718103 : name.name = dep->name;
131 5147718103 : name.len = dep->namelen;
132 5147718103 : name.type = xfs_dir2_data_get_ftype(mp, dep);
133 :
134 5147783254 : error = dirent_fn(sc, dp, dapos, &name, ino, priv);
135 5147801490 : if (error)
136 : break;
137 : }
138 :
139 195431585 : xfs_trans_brelse(sc->tp, bp);
140 195431585 : return error;
141 : }
142 :
143 : /* Read a leaf-format directory buffer. */
144 : STATIC int
145 287254525 : xchk_read_leaf_dir_buf(
146 : struct xfs_trans *tp,
147 : struct xfs_inode *dp,
148 : struct xfs_da_geometry *geo,
149 : xfs_dir2_off_t *curoff,
150 : struct xfs_buf **bpp)
151 : {
152 287254525 : struct xfs_iext_cursor icur;
153 287254525 : struct xfs_bmbt_irec map;
154 287254525 : struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
155 287254525 : xfs_dablk_t last_da;
156 287254525 : xfs_dablk_t map_off;
157 287254525 : xfs_dir2_off_t new_off;
158 :
159 287254525 : *bpp = NULL;
160 :
161 : /*
162 : * Look for mapped directory blocks at or above the current offset.
163 : * Truncate down to the nearest directory block to start the scanning
164 : * operation.
165 : */
166 287254525 : last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
167 286690535 : map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
168 :
169 286910281 : if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
170 : return 0;
171 287339411 : if (map.br_startoff >= last_da)
172 : return 0;
173 262004134 : xfs_trim_extent(&map, map_off, last_da - map_off);
174 :
175 : /* Read the directory block of that first mapping. */
176 261898608 : new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
177 261790981 : if (new_off > *curoff)
178 12740 : *curoff = new_off;
179 :
180 261790981 : return xfs_dir3_data_read(tp, dp, dp->i_ino, map.br_startoff, 0, bpp);
181 : }
182 :
183 : /* Call a function for every entry in a leaf directory. */
184 : STATIC int
185 25368607 : xchk_dir_walk_leaf(
186 : struct xfs_scrub *sc,
187 : struct xfs_inode *dp,
188 : xchk_dirent_fn dirent_fn,
189 : void *priv)
190 : {
191 25368607 : struct xfs_mount *mp = dp->i_mount;
192 25368607 : struct xfs_da_geometry *geo = mp->m_dir_geo;
193 25368607 : struct xfs_buf *bp = NULL;
194 25368607 : xfs_dir2_off_t curoff = 0;
195 25368607 : unsigned int offset = 0;
196 25368607 : int error;
197 :
198 : /* Iterate every directory offset in this directory. */
199 32389110408 : while (curoff < XFS_DIR2_LEAF_OFFSET) {
200 32389110408 : struct xfs_name name = { };
201 32389110408 : struct xfs_dir2_data_unused *dup;
202 32389110408 : struct xfs_dir2_data_entry *dep;
203 32389110408 : xfs_ino_t ino;
204 32389110408 : unsigned int length;
205 32389110408 : xfs_dir2_dataptr_t dapos;
206 :
207 : /*
208 : * If we have no buffer, or we're off the end of the
209 : * current buffer, need to get another one.
210 : */
211 32389110408 : if (!bp || offset >= geo->blksize) {
212 287425502 : if (bp) {
213 262057290 : xfs_trans_brelse(sc->tp, bp);
214 262078683 : bp = NULL;
215 : }
216 :
217 287446895 : error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
218 : &bp);
219 287373750 : if (error || !bp)
220 : break;
221 :
222 : /*
223 : * Find our position in the block.
224 : */
225 262004287 : offset = geo->data_entry_offset;
226 262004287 : curoff += geo->data_entry_offset;
227 : }
228 :
229 : /* Skip an empty entry. */
230 32363689193 : dup = bp->b_addr + offset;
231 32363689193 : if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
232 733697297 : length = be16_to_cpu(dup->length);
233 733697297 : offset += length;
234 733697297 : curoff += length;
235 733697297 : continue;
236 : }
237 :
238 : /* Otherwise, find the next entry and report it. */
239 31629991896 : dep = bp->b_addr + offset;
240 31629991896 : length = xfs_dir2_data_entsize(mp, dep->namelen);
241 :
242 31629991896 : dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
243 31629991896 : ino = be64_to_cpu(dep->inumber);
244 31629991896 : name.name = dep->name;
245 31629991896 : name.len = dep->namelen;
246 31629991896 : name.type = xfs_dir2_data_get_ftype(mp, dep);
247 :
248 31651089720 : error = dirent_fn(sc, dp, dapos, &name, ino, priv);
249 31630044511 : if (error)
250 : break;
251 :
252 : /* Advance to the next entry. */
253 31630044504 : offset += length;
254 31630044504 : curoff += length;
255 : }
256 :
257 25369470 : if (bp)
258 7 : xfs_trans_brelse(sc->tp, bp);
259 25369470 : return error;
260 : }
261 :
262 : /*
263 : * Call a function for every entry in a directory.
264 : *
265 : * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*.
266 : */
267 : int
268 6083919935 : xchk_dir_walk(
269 : struct xfs_scrub *sc,
270 : struct xfs_inode *dp,
271 : xchk_dirent_fn dirent_fn,
272 : void *priv)
273 : {
274 6083919935 : struct xfs_da_args args = {
275 : .dp = dp,
276 6083919935 : .geo = dp->i_mount->m_dir_geo,
277 6083919935 : .trans = sc->tp,
278 6083919935 : .owner = dp->i_ino,
279 : };
280 6083919935 : bool isblock;
281 6083919935 : int error;
282 :
283 12167839870 : if (xfs_is_shutdown(dp->i_mount))
284 : return -EIO;
285 :
286 6083919935 : ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
287 6083919935 : ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
288 :
289 6085949269 : if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
290 5865153687 : return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
291 :
292 : /* dir2 functions require that the data fork is loaded */
293 220795582 : error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
294 220792010 : if (error)
295 : return error;
296 :
297 220792305 : error = xfs_dir2_isblock(&args, &isblock);
298 220806178 : if (error)
299 : return error;
300 :
301 220806178 : if (isblock)
302 195437305 : return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
303 :
304 25368873 : return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
305 : }
306 :
307 : /*
308 : * Look up the inode number for an exact name in a directory.
309 : *
310 : * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not
311 : * checked for correctness.
312 : */
313 : int
314 306115048 : xchk_dir_lookup(
315 : struct xfs_scrub *sc,
316 : struct xfs_inode *dp,
317 : const struct xfs_name *name,
318 : xfs_ino_t *ino)
319 : {
320 612330601 : struct xfs_da_args args = {
321 : .dp = dp,
322 306115048 : .geo = dp->i_mount->m_dir_geo,
323 306215553 : .trans = sc->tp,
324 306115048 : .name = name->name,
325 306115048 : .namelen = name->len,
326 306115048 : .filetype = name->type,
327 306115048 : .hashval = xfs_dir2_hashname(dp->i_mount, name),
328 : .whichfork = XFS_DATA_FORK,
329 : .op_flags = XFS_DA_OP_OKNOENT,
330 306215553 : .owner = dp->i_ino,
331 : };
332 306215553 : bool isblock, isleaf;
333 306215553 : int error;
334 :
335 612431106 : if (xfs_is_shutdown(dp->i_mount))
336 : return -EIO;
337 :
338 : /*
339 : * A temporary directory's block headers are written with the owner
340 : * set to sc->ip, so we must switch the owner here for the lookup.
341 : */
342 306215553 : if (dp == sc->tempip)
343 2489303 : args.owner = sc->ip->i_ino;
344 :
345 306215553 : ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
346 306215553 : ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
347 :
348 305802988 : if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
349 199472583 : error = xfs_dir2_sf_lookup(&args);
350 199779168 : goto out_check_rval;
351 : }
352 :
353 : /* dir2 functions require that the data fork is loaded */
354 106330405 : error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
355 106286869 : if (error)
356 : return error;
357 :
358 106299959 : error = xfs_dir2_isblock(&args, &isblock);
359 106377427 : if (error)
360 : return error;
361 :
362 106377427 : if (isblock) {
363 11536440 : error = xfs_dir2_block_lookup(&args);
364 11539395 : goto out_check_rval;
365 : }
366 :
367 94840987 : error = xfs_dir2_isleaf(&args, &isleaf);
368 95273236 : if (error)
369 : return error;
370 :
371 95273236 : if (isleaf) {
372 6798104 : error = xfs_dir2_leaf_lookup(&args);
373 6799728 : goto out_check_rval;
374 : }
375 :
376 88475132 : error = xfs_dir2_node_lookup(&args);
377 :
378 306625212 : out_check_rval:
379 306625212 : if (error == -EEXIST)
380 : error = 0;
381 2489362 : if (!error)
382 304135850 : *ino = args.inumber;
383 : return error;
384 : }
|