]> Git Repo - linux.git/blob - fs/xfs/scrub/readdir.c
media: ipu-bridge: Fix Kconfig dependencies
[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;
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(dp->i_df.if_u1.if_data != NULL);
47
48         sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
49
50         /* dot entry */
51         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
52                         geo->data_entry_offset);
53
54         error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
55         if (error)
56                 return error;
57
58         /* dotdot entry */
59         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
60                         geo->data_entry_offset +
61                         xfs_dir2_data_entsize(mp, sizeof(".") - 1));
62         ino = xfs_dir2_sf_get_parent_ino(sfp);
63         name.name = "..";
64         name.len = 2;
65
66         error = dirent_fn(sc, dp, dapos, &name, ino, priv);
67         if (error)
68                 return error;
69
70         /* iterate everything else */
71         sfep = xfs_dir2_sf_firstentry(sfp);
72         for (i = 0; i < sfp->count; i++) {
73                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
74                                 xfs_dir2_sf_get_offset(sfep));
75                 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
76                 name.name = sfep->name;
77                 name.len = sfep->namelen;
78                 name.type = xfs_dir2_sf_get_ftype(mp, sfep);
79
80                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
81                 if (error)
82                         return error;
83
84                 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 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         struct xfs_mount        *mp = dp->i_mount;
99         struct xfs_da_geometry  *geo = mp->m_dir_geo;
100         struct xfs_buf          *bp;
101         unsigned int            off, next_off, end;
102         int                     error;
103
104         error = xfs_dir3_block_read(sc->tp, dp, &bp);
105         if (error)
106                 return error;
107
108         /* Walk each directory entry. */
109         end = xfs_dir3_data_end_offset(geo, bp->b_addr);
110         for (off = geo->data_entry_offset; off < end; off = next_off) {
111                 struct xfs_name                 name = { };
112                 struct xfs_dir2_data_unused     *dup = bp->b_addr + off;
113                 struct xfs_dir2_data_entry      *dep = bp->b_addr + off;
114                 xfs_ino_t                       ino;
115                 xfs_dir2_dataptr_t              dapos;
116
117                 /* Skip an empty entry. */
118                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
119                         next_off = off + be16_to_cpu(dup->length);
120                         continue;
121                 }
122
123                 /* Otherwise, find the next entry and report it. */
124                 next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
125                 if (next_off > end)
126                         break;
127
128                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
129                 ino = be64_to_cpu(dep->inumber);
130                 name.name = dep->name;
131                 name.len = dep->namelen;
132                 name.type = xfs_dir2_data_get_ftype(mp, dep);
133
134                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
135                 if (error)
136                         break;
137         }
138
139         xfs_trans_brelse(sc->tp, bp);
140         return error;
141 }
142
143 /* Read a leaf-format directory buffer. */
144 STATIC int
145 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         struct xfs_iext_cursor  icur;
153         struct xfs_bmbt_irec    map;
154         struct xfs_ifork        *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
155         xfs_dablk_t             last_da;
156         xfs_dablk_t             map_off;
157         xfs_dir2_off_t          new_off;
158
159         *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         last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
167         map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
168
169         if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
170                 return 0;
171         if (map.br_startoff >= last_da)
172                 return 0;
173         xfs_trim_extent(&map, map_off, last_da - map_off);
174
175         /* Read the directory block of that first mapping. */
176         new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
177         if (new_off > *curoff)
178                 *curoff = new_off;
179
180         return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
181 }
182
183 /* Call a function for every entry in a leaf directory. */
184 STATIC int
185 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         struct xfs_mount        *mp = dp->i_mount;
192         struct xfs_da_geometry  *geo = mp->m_dir_geo;
193         struct xfs_buf          *bp = NULL;
194         xfs_dir2_off_t          curoff = 0;
195         unsigned int            offset = 0;
196         int                     error;
197
198         /* Iterate every directory offset in this directory. */
199         while (curoff < XFS_DIR2_LEAF_OFFSET) {
200                 struct xfs_name                 name = { };
201                 struct xfs_dir2_data_unused     *dup;
202                 struct xfs_dir2_data_entry      *dep;
203                 xfs_ino_t                       ino;
204                 unsigned int                    length;
205                 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                 if (!bp || offset >= geo->blksize) {
212                         if (bp) {
213                                 xfs_trans_brelse(sc->tp, bp);
214                                 bp = NULL;
215                         }
216
217                         error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
218                                         &bp);
219                         if (error || !bp)
220                                 break;
221
222                         /*
223                          * Find our position in the block.
224                          */
225                         offset = geo->data_entry_offset;
226                         curoff += geo->data_entry_offset;
227                 }
228
229                 /* Skip an empty entry. */
230                 dup = bp->b_addr + offset;
231                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
232                         length = be16_to_cpu(dup->length);
233                         offset += length;
234                         curoff += length;
235                         continue;
236                 }
237
238                 /* Otherwise, find the next entry and report it. */
239                 dep = bp->b_addr + offset;
240                 length = xfs_dir2_data_entsize(mp, dep->namelen);
241
242                 dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
243                 ino = be64_to_cpu(dep->inumber);
244                 name.name = dep->name;
245                 name.len = dep->namelen;
246                 name.type = xfs_dir2_data_get_ftype(mp, dep);
247
248                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
249                 if (error)
250                         break;
251
252                 /* Advance to the next entry. */
253                 offset += length;
254                 curoff += length;
255         }
256
257         if (bp)
258                 xfs_trans_brelse(sc->tp, bp);
259         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 xchk_dir_walk(
269         struct xfs_scrub        *sc,
270         struct xfs_inode        *dp,
271         xchk_dirent_fn          dirent_fn,
272         void                    *priv)
273 {
274         struct xfs_da_args      args = {
275                 .dp             = dp,
276                 .geo            = dp->i_mount->m_dir_geo,
277                 .trans          = sc->tp,
278         };
279         bool                    isblock;
280         int                     error;
281
282         if (xfs_is_shutdown(dp->i_mount))
283                 return -EIO;
284
285         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
286         ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
287
288         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
289                 return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
290
291         /* dir2 functions require that the data fork is loaded */
292         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
293         if (error)
294                 return error;
295
296         error = xfs_dir2_isblock(&args, &isblock);
297         if (error)
298                 return error;
299
300         if (isblock)
301                 return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
302
303         return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
304 }
305
306 /*
307  * Look up the inode number for an exact name in a directory.
308  *
309  * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
310  * checked for correctness.
311  */
312 int
313 xchk_dir_lookup(
314         struct xfs_scrub        *sc,
315         struct xfs_inode        *dp,
316         const struct xfs_name   *name,
317         xfs_ino_t               *ino)
318 {
319         struct xfs_da_args      args = {
320                 .dp             = dp,
321                 .geo            = dp->i_mount->m_dir_geo,
322                 .trans          = sc->tp,
323                 .name           = name->name,
324                 .namelen        = name->len,
325                 .filetype       = name->type,
326                 .hashval        = xfs_dir2_hashname(dp->i_mount, name),
327                 .whichfork      = XFS_DATA_FORK,
328                 .op_flags       = XFS_DA_OP_OKNOENT,
329         };
330         bool                    isblock, isleaf;
331         int                     error;
332
333         if (xfs_is_shutdown(dp->i_mount))
334                 return -EIO;
335
336         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
337         ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
338
339         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
340                 error = xfs_dir2_sf_lookup(&args);
341                 goto out_check_rval;
342         }
343
344         /* dir2 functions require that the data fork is loaded */
345         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
346         if (error)
347                 return error;
348
349         error = xfs_dir2_isblock(&args, &isblock);
350         if (error)
351                 return error;
352
353         if (isblock) {
354                 error = xfs_dir2_block_lookup(&args);
355                 goto out_check_rval;
356         }
357
358         error = xfs_dir2_isleaf(&args, &isleaf);
359         if (error)
360                 return error;
361
362         if (isleaf) {
363                 error = xfs_dir2_leaf_lookup(&args);
364                 goto out_check_rval;
365         }
366
367         error = xfs_dir2_node_lookup(&args);
368
369 out_check_rval:
370         if (error == -EEXIST)
371                 error = 0;
372         if (!error)
373                 *ino = args.inumber;
374         return error;
375 }
This page took 0.053857 seconds and 4 git commands to generate.