]> Git Repo - J-linux.git/blob - fs/xfs/scrub/readdir.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[J-linux.git] / fs / xfs / scrub / readdir.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2022-2023 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <[email protected]>
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 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         struct xfs_name         name = {
32                 .name           = ".",
33                 .len            = 1,
34                 .type           = XFS_DIR3_FT_DIR,
35         };
36         struct xfs_mount        *mp = dp->i_mount;
37         struct xfs_da_geometry  *geo = mp->m_dir_geo;
38         struct xfs_dir2_sf_entry *sfep;
39         struct xfs_dir2_sf_hdr  *sfp = dp->i_df.if_data;
40         xfs_ino_t               ino;
41         xfs_dir2_dataptr_t      dapos;
42         unsigned int            i;
43         int                     error;
44
45         ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
46         ASSERT(sfp != NULL);
47
48         /* dot entry */
49         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
50                         geo->data_entry_offset);
51
52         error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
53         if (error)
54                 return error;
55
56         /* dotdot entry */
57         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
58                         geo->data_entry_offset +
59                         xfs_dir2_data_entsize(mp, sizeof(".") - 1));
60         ino = xfs_dir2_sf_get_parent_ino(sfp);
61         name.name = "..";
62         name.len = 2;
63
64         error = dirent_fn(sc, dp, dapos, &name, ino, priv);
65         if (error)
66                 return error;
67
68         /* iterate everything else */
69         sfep = xfs_dir2_sf_firstentry(sfp);
70         for (i = 0; i < sfp->count; i++) {
71                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
72                                 xfs_dir2_sf_get_offset(sfep));
73                 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
74                 name.name = sfep->name;
75                 name.len = sfep->namelen;
76                 name.type = xfs_dir2_sf_get_ftype(mp, sfep);
77
78                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
79                 if (error)
80                         return error;
81
82                 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
83         }
84
85         return 0;
86 }
87
88 /* Call a function for every entry in a block directory. */
89 STATIC int
90 xchk_dir_walk_block(
91         struct xfs_scrub        *sc,
92         struct xfs_inode        *dp,
93         xchk_dirent_fn          dirent_fn,
94         void                    *priv)
95 {
96         struct xfs_mount        *mp = dp->i_mount;
97         struct xfs_da_geometry  *geo = mp->m_dir_geo;
98         struct xfs_buf          *bp;
99         unsigned int            off, next_off, end;
100         int                     error;
101
102         error = xfs_dir3_block_read(sc->tp, dp, &bp);
103         if (error)
104                 return error;
105
106         /* Walk each directory entry. */
107         end = xfs_dir3_data_end_offset(geo, bp->b_addr);
108         for (off = geo->data_entry_offset; off < end; off = next_off) {
109                 struct xfs_name                 name = { };
110                 struct xfs_dir2_data_unused     *dup = bp->b_addr + off;
111                 struct xfs_dir2_data_entry      *dep = bp->b_addr + off;
112                 xfs_ino_t                       ino;
113                 xfs_dir2_dataptr_t              dapos;
114
115                 /* Skip an empty entry. */
116                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
117                         next_off = off + be16_to_cpu(dup->length);
118                         continue;
119                 }
120
121                 /* Otherwise, find the next entry and report it. */
122                 next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
123                 if (next_off > end)
124                         break;
125
126                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
127                 ino = be64_to_cpu(dep->inumber);
128                 name.name = dep->name;
129                 name.len = dep->namelen;
130                 name.type = xfs_dir2_data_get_ftype(mp, dep);
131
132                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
133                 if (error)
134                         break;
135         }
136
137         xfs_trans_brelse(sc->tp, bp);
138         return error;
139 }
140
141 /* Read a leaf-format directory buffer. */
142 STATIC int
143 xchk_read_leaf_dir_buf(
144         struct xfs_trans        *tp,
145         struct xfs_inode        *dp,
146         struct xfs_da_geometry  *geo,
147         xfs_dir2_off_t          *curoff,
148         struct xfs_buf          **bpp)
149 {
150         struct xfs_iext_cursor  icur;
151         struct xfs_bmbt_irec    map;
152         struct xfs_ifork        *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
153         xfs_dablk_t             last_da;
154         xfs_dablk_t             map_off;
155         xfs_dir2_off_t          new_off;
156
157         *bpp = NULL;
158
159         /*
160          * Look for mapped directory blocks at or above the current offset.
161          * Truncate down to the nearest directory block to start the scanning
162          * operation.
163          */
164         last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
165         map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
166
167         if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
168                 return 0;
169         if (map.br_startoff >= last_da)
170                 return 0;
171         xfs_trim_extent(&map, map_off, last_da - map_off);
172
173         /* Read the directory block of that first mapping. */
174         new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
175         if (new_off > *curoff)
176                 *curoff = new_off;
177
178         return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
179 }
180
181 /* Call a function for every entry in a leaf directory. */
182 STATIC int
183 xchk_dir_walk_leaf(
184         struct xfs_scrub        *sc,
185         struct xfs_inode        *dp,
186         xchk_dirent_fn          dirent_fn,
187         void                    *priv)
188 {
189         struct xfs_mount        *mp = dp->i_mount;
190         struct xfs_da_geometry  *geo = mp->m_dir_geo;
191         struct xfs_buf          *bp = NULL;
192         xfs_dir2_off_t          curoff = 0;
193         unsigned int            offset = 0;
194         int                     error;
195
196         /* Iterate every directory offset in this directory. */
197         while (curoff < XFS_DIR2_LEAF_OFFSET) {
198                 struct xfs_name                 name = { };
199                 struct xfs_dir2_data_unused     *dup;
200                 struct xfs_dir2_data_entry      *dep;
201                 xfs_ino_t                       ino;
202                 unsigned int                    length;
203                 xfs_dir2_dataptr_t              dapos;
204
205                 /*
206                  * If we have no buffer, or we're off the end of the
207                  * current buffer, need to get another one.
208                  */
209                 if (!bp || offset >= geo->blksize) {
210                         if (bp) {
211                                 xfs_trans_brelse(sc->tp, bp);
212                                 bp = NULL;
213                         }
214
215                         error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
216                                         &bp);
217                         if (error || !bp)
218                                 break;
219
220                         /*
221                          * Find our position in the block.
222                          */
223                         offset = geo->data_entry_offset;
224                         curoff += geo->data_entry_offset;
225                 }
226
227                 /* Skip an empty entry. */
228                 dup = bp->b_addr + offset;
229                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
230                         length = be16_to_cpu(dup->length);
231                         offset += length;
232                         curoff += length;
233                         continue;
234                 }
235
236                 /* Otherwise, find the next entry and report it. */
237                 dep = bp->b_addr + offset;
238                 length = xfs_dir2_data_entsize(mp, dep->namelen);
239
240                 dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
241                 ino = be64_to_cpu(dep->inumber);
242                 name.name = dep->name;
243                 name.len = dep->namelen;
244                 name.type = xfs_dir2_data_get_ftype(mp, dep);
245
246                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
247                 if (error)
248                         break;
249
250                 /* Advance to the next entry. */
251                 offset += length;
252                 curoff += length;
253         }
254
255         if (bp)
256                 xfs_trans_brelse(sc->tp, bp);
257         return error;
258 }
259
260 /*
261  * Call a function for every entry in a directory.
262  *
263  * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
264  */
265 int
266 xchk_dir_walk(
267         struct xfs_scrub        *sc,
268         struct xfs_inode        *dp,
269         xchk_dirent_fn          dirent_fn,
270         void                    *priv)
271 {
272         struct xfs_da_args      args = {
273                 .dp             = dp,
274                 .geo            = dp->i_mount->m_dir_geo,
275                 .trans          = sc->tp,
276         };
277         bool                    isblock;
278         int                     error;
279
280         if (xfs_is_shutdown(dp->i_mount))
281                 return -EIO;
282
283         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
284         xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
285
286         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
287                 return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
288
289         /* dir2 functions require that the data fork is loaded */
290         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
291         if (error)
292                 return error;
293
294         error = xfs_dir2_isblock(&args, &isblock);
295         if (error)
296                 return error;
297
298         if (isblock)
299                 return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
300
301         return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
302 }
303
304 /*
305  * Look up the inode number for an exact name in a directory.
306  *
307  * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
308  * checked for correctness.
309  */
310 int
311 xchk_dir_lookup(
312         struct xfs_scrub        *sc,
313         struct xfs_inode        *dp,
314         const struct xfs_name   *name,
315         xfs_ino_t               *ino)
316 {
317         struct xfs_da_args      args = {
318                 .dp             = dp,
319                 .geo            = dp->i_mount->m_dir_geo,
320                 .trans          = sc->tp,
321                 .name           = name->name,
322                 .namelen        = name->len,
323                 .filetype       = name->type,
324                 .hashval        = xfs_dir2_hashname(dp->i_mount, name),
325                 .whichfork      = XFS_DATA_FORK,
326                 .op_flags       = XFS_DA_OP_OKNOENT,
327         };
328         bool                    isblock, isleaf;
329         int                     error;
330
331         if (xfs_is_shutdown(dp->i_mount))
332                 return -EIO;
333
334         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
335         xfs_assert_ilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
336
337         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
338                 error = xfs_dir2_sf_lookup(&args);
339                 goto out_check_rval;
340         }
341
342         /* dir2 functions require that the data fork is loaded */
343         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
344         if (error)
345                 return error;
346
347         error = xfs_dir2_isblock(&args, &isblock);
348         if (error)
349                 return error;
350
351         if (isblock) {
352                 error = xfs_dir2_block_lookup(&args);
353                 goto out_check_rval;
354         }
355
356         error = xfs_dir2_isleaf(&args, &isleaf);
357         if (error)
358                 return error;
359
360         if (isleaf) {
361                 error = xfs_dir2_leaf_lookup(&args);
362                 goto out_check_rval;
363         }
364
365         error = xfs_dir2_node_lookup(&args);
366
367 out_check_rval:
368         if (error == -EEXIST)
369                 error = 0;
370         if (!error)
371                 *ino = args.inumber;
372         return error;
373 }
This page took 0.051087 seconds and 4 git commands to generate.