1818#include "scrub/scrub.h"
1919#include "scrub/common.h"
2020#include "scrub/dabtree.h"
21+ #include "scrub/readdir.h"
2122
2223/* Set us up to scrub directories. */
2324int
@@ -31,168 +32,120 @@ xchk_setup_directory(
3132
3233/* Scrub a directory entry. */
3334
34- struct xchk_dir_ctx {
35- /* VFS fill-directory iterator */
36- struct dir_context dir_iter ;
37-
38- struct xfs_scrub * sc ;
39- };
40-
41- /* Check that an inode's mode matches a given DT_ type. */
42- STATIC int
35+ /* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */
36+ STATIC void
4337xchk_dir_check_ftype (
44- struct xchk_dir_ctx * sdc ,
38+ struct xfs_scrub * sc ,
4539 xfs_fileoff_t offset ,
46- xfs_ino_t inum ,
47- int dtype )
40+ struct xfs_inode * ip ,
41+ int ftype )
4842{
49- struct xfs_mount * mp = sdc -> sc -> mp ;
50- struct xfs_inode * ip ;
51- int ino_dtype ;
52- int error = 0 ;
43+ struct xfs_mount * mp = sc -> mp ;
5344
5445 if (!xfs_has_ftype (mp )) {
55- if (dtype != DT_UNKNOWN && dtype != DT_DIR )
56- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
57- offset );
58- goto out ;
59- }
60-
61- /*
62- * Grab the inode pointed to by the dirent. We release the
63- * inode before we cancel the scrub transaction. Since we're
64- * don't know a priori that releasing the inode won't trigger
65- * eofblocks cleanup (which allocates what would be a nested
66- * transaction), we can't use DONTCACHE here because DONTCACHE
67- * inodes can trigger immediate inactive cleanup of the inode.
68- *
69- * If _iget returns -EINVAL or -ENOENT then the child inode number is
70- * garbage and the directory is corrupt. If the _iget returns
71- * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a
72- * cross referencing error. Any other error is an operational error.
73- */
74- error = xfs_iget (mp , sdc -> sc -> tp , inum , 0 , 0 , & ip );
75- if (error == - EINVAL || error == - ENOENT ) {
76- error = - EFSCORRUPTED ;
77- xchk_fblock_process_error (sdc -> sc , XFS_DATA_FORK , 0 , & error );
78- goto out ;
46+ if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR )
47+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
48+ return ;
7949 }
80- if (!xchk_fblock_xref_process_error (sdc -> sc , XFS_DATA_FORK , offset ,
81- & error ))
82- goto out ;
8350
84- /* Convert mode to the DT_* values that dir_emit uses. */
85- ino_dtype = xfs_dir3_get_dtype (mp ,
86- xfs_mode_to_ftype (VFS_I (ip )-> i_mode ));
87- if (ino_dtype != dtype )
88- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
89- xfs_irele (ip );
90- out :
91- return error ;
51+ if (xfs_mode_to_ftype (VFS_I (ip )-> i_mode ) != ftype )
52+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
9253}
9354
9455/*
9556 * Scrub a single directory entry.
9657 *
97- * We use the VFS directory iterator (i.e. readdir) to call this
98- * function for every directory entry in a directory. Once we're here,
99- * we check the inode number to make sure it's sane, then we check that
100- * we can look up this filename. Finally, we check the ftype.
58+ * Check the inode number to make sure it's sane, then we check that we can
59+ * look up this filename. Finally, we check the ftype.
10160 */
102- STATIC bool
61+ STATIC int
10362xchk_dir_actor (
104- struct dir_context * dir_iter ,
105- const char * name ,
106- int namelen ,
107- loff_t pos ,
108- u64 ino ,
109- unsigned type )
63+ struct xfs_scrub * sc ,
64+ struct xfs_inode * dp ,
65+ xfs_dir2_dataptr_t dapos ,
66+ const struct xfs_name * name ,
67+ xfs_ino_t ino ,
68+ void * priv )
11069{
111- struct xfs_mount * mp ;
70+ struct xfs_mount * mp = dp -> i_mount ;
11271 struct xfs_inode * ip ;
113- struct xchk_dir_ctx * sdc ;
114- struct xfs_name xname ;
11572 xfs_ino_t lookup_ino ;
11673 xfs_dablk_t offset ;
117- bool checked_ftype = false;
11874 int error = 0 ;
11975
120- sdc = container_of (dir_iter , struct xchk_dir_ctx , dir_iter );
121- ip = sdc -> sc -> ip ;
122- mp = ip -> i_mount ;
12376 offset = xfs_dir2_db_to_da (mp -> m_dir_geo ,
124- xfs_dir2_dataptr_to_db (mp -> m_dir_geo , pos ));
77+ xfs_dir2_dataptr_to_db (mp -> m_dir_geo , dapos ));
12578
126- if (xchk_should_terminate (sdc -> sc , & error ))
127- return ! error ;
79+ if (xchk_should_terminate (sc , & error ))
80+ return error ;
12881
12982 /* Does this inode number make sense? */
13083 if (!xfs_verify_dir_ino (mp , ino )) {
131- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
132- goto out ;
84+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
85+ return - ECANCELED ;
13386 }
13487
13588 /* Does this name make sense? */
136- if (!xfs_dir2_namecheck (name , namelen )) {
137- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
138- goto out ;
89+ if (!xfs_dir2_namecheck (name -> name , name -> len )) {
90+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
91+ return - ECANCELED ;
13992 }
14093
141- if (!strncmp ("." , name , namelen )) {
94+ if (!strncmp ("." , name -> name , name -> len )) {
14295 /* If this is "." then check that the inum matches the dir. */
143- if (xfs_has_ftype (mp ) && type != DT_DIR )
144- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
145- offset );
146- checked_ftype = true;
147- if (ino != ip -> i_ino )
148- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
149- offset );
150- } else if (!strncmp (".." , name , namelen )) {
96+ if (ino != dp -> i_ino )
97+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
98+ } else if (!strncmp (".." , name -> name , name -> len )) {
15199 /*
152100 * If this is ".." in the root inode, check that the inum
153101 * matches this dir.
154102 */
155- if (xfs_has_ftype (mp ) && type != DT_DIR )
156- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
157- offset );
158- checked_ftype = true;
159- if (ip -> i_ino == mp -> m_sb .sb_rootino && ino != ip -> i_ino )
160- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
161- offset );
103+ if (dp -> i_ino == mp -> m_sb .sb_rootino && ino != dp -> i_ino )
104+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
162105 }
163106
164107 /* Verify that we can look up this name by hash. */
165- xname .name = name ;
166- xname .len = namelen ;
167- xname .type = XFS_DIR3_FT_UNKNOWN ;
168-
169- error = xfs_dir_lookup (sdc -> sc -> tp , ip , & xname , & lookup_ino , NULL );
108+ error = xchk_dir_lookup (sc , dp , name , & lookup_ino );
170109 /* ENOENT means the hash lookup failed and the dir is corrupt */
171110 if (error == - ENOENT )
172111 error = - EFSCORRUPTED ;
173- if (!xchk_fblock_process_error (sdc -> sc , XFS_DATA_FORK , offset ,
174- & error ))
112+ if (!xchk_fblock_process_error (sc , XFS_DATA_FORK , offset , & error ))
175113 goto out ;
176114 if (lookup_ino != ino ) {
177- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
178- goto out ;
115+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
116+ return - ECANCELED ;
179117 }
180118
181- /* Verify the file type. This function absorbs error codes. */
182- if (!checked_ftype ) {
183- error = xchk_dir_check_ftype (sdc , offset , lookup_ino , type );
184- if (error )
185- goto out ;
186- }
187- out :
188119 /*
189- * A negative error code returned here is supposed to cause the
190- * dir_emit caller (xfs_readdir) to abort the directory iteration
191- * and return zero to xchk_directory.
120+ * Grab the inode pointed to by the dirent. We release the
121+ * inode before we cancel the scrub transaction. Since we're
122+ * don't know a priori that releasing the inode won't trigger
123+ * eofblocks cleanup (which allocates what would be a nested
124+ * transaction), we can't use DONTCACHE here because DONTCACHE
125+ * inodes can trigger immediate inactive cleanup of the inode.
126+ * Use UNTRUSTED here to check the allocation status of the inode in
127+ * the inode btrees.
128+ *
129+ * If _iget returns -EINVAL or -ENOENT then the child inode number is
130+ * garbage and the directory is corrupt. If the _iget returns
131+ * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a
132+ * cross referencing error. Any other error is an operational error.
192133 */
193- if (error == 0 && sdc -> sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
194- return false;
195- return !error ;
134+ error = xfs_iget (mp , sc -> tp , ino , XFS_IGET_UNTRUSTED , 0 , & ip );
135+ if (error == - EINVAL || error == - ENOENT ) {
136+ error = - EFSCORRUPTED ;
137+ xchk_fblock_process_error (sc , XFS_DATA_FORK , 0 , & error );
138+ goto out ;
139+ }
140+ if (!xchk_fblock_xref_process_error (sc , XFS_DATA_FORK , offset , & error ))
141+ goto out ;
142+
143+ xchk_dir_check_ftype (sc , offset , ip , name -> type );
144+ xfs_irele (ip );
145+ out :
146+ if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
147+ return - ECANCELED ;
148+ return error ;
196149}
197150
198151/* Scrub a directory btree record. */
@@ -201,6 +154,7 @@ xchk_dir_rec(
201154 struct xchk_da_btree * ds ,
202155 int level )
203156{
157+ struct xfs_name dname = { };
204158 struct xfs_da_state_blk * blk = & ds -> state -> path .blk [level ];
205159 struct xfs_mount * mp = ds -> state -> mp ;
206160 struct xfs_inode * dp = ds -> dargs .dp ;
@@ -297,7 +251,11 @@ xchk_dir_rec(
297251 xchk_fblock_set_corrupt (ds -> sc , XFS_DATA_FORK , rec_bno );
298252 goto out_relse ;
299253 }
300- calc_hash = xfs_da_hashname (dent -> name , dent -> namelen );
254+
255+ /* Does the directory hash match? */
256+ dname .name = dent -> name ;
257+ dname .len = dent -> namelen ;
258+ calc_hash = xfs_dir2_hashname (mp , & dname );
301259 if (calc_hash != hash )
302260 xchk_fblock_set_corrupt (ds -> sc , XFS_DATA_FORK , rec_bno );
303261
@@ -803,22 +761,15 @@ int
803761xchk_directory (
804762 struct xfs_scrub * sc )
805763{
806- struct xchk_dir_ctx sdc = {
807- .dir_iter .actor = xchk_dir_actor ,
808- .dir_iter .pos = 0 ,
809- .sc = sc ,
810- };
811- size_t bufsize ;
812- loff_t oldpos ;
813- int error = 0 ;
764+ int error ;
814765
815766 if (!S_ISDIR (VFS_I (sc -> ip )-> i_mode ))
816767 return - ENOENT ;
817768
818769 /* Plausible size? */
819770 if (sc -> ip -> i_disk_size < xfs_dir2_sf_hdr_size (0 )) {
820771 xchk_ino_set_corrupt (sc , sc -> ip -> i_ino );
821- goto out ;
772+ return 0 ;
822773 }
823774
824775 /* Check directory tree structure */
@@ -827,52 +778,19 @@ xchk_directory(
827778 return error ;
828779
829780 if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
830- return error ;
781+ return 0 ;
831782
832783 /* Check the freespace. */
833784 error = xchk_directory_blocks (sc );
834785 if (error )
835786 return error ;
836787
837788 if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
838- return error ;
839-
840- /*
841- * Check that every dirent we see can also be looked up by hash.
842- * Userspace usually asks for a 32k buffer, so we will too.
843- */
844- bufsize = (size_t )min_t (loff_t , XFS_READDIR_BUFSIZE ,
845- sc -> ip -> i_disk_size );
846-
847- /*
848- * Look up every name in this directory by hash.
849- *
850- * Use the xfs_readdir function to call xchk_dir_actor on
851- * every directory entry in this directory. In _actor, we check
852- * the name, inode number, and ftype (if applicable) of the
853- * entry. xfs_readdir uses the VFS filldir functions to provide
854- * iteration context.
855- *
856- * The VFS grabs a read or write lock via i_rwsem before it reads
857- * or writes to a directory. If we've gotten this far we've
858- * already obtained IOLOCK_EXCL, which (since 4.10) is the same as
859- * getting a write lock on i_rwsem. Therefore, it is safe for us
860- * to drop the ILOCK here in order to reuse the _readdir and
861- * _dir_lookup routines, which do their own ILOCK locking.
862- */
863- oldpos = 0 ;
864- sc -> ilock_flags &= ~XFS_ILOCK_EXCL ;
865- xfs_iunlock (sc -> ip , XFS_ILOCK_EXCL );
866- while (true) {
867- error = xfs_readdir (sc -> tp , sc -> ip , & sdc .dir_iter , bufsize );
868- if (!xchk_fblock_process_error (sc , XFS_DATA_FORK , 0 ,
869- & error ))
870- goto out ;
871- if (oldpos == sdc .dir_iter .pos )
872- break ;
873- oldpos = sdc .dir_iter .pos ;
874- }
789+ return 0 ;
875790
876- out :
791+ /* Look up every name in this directory by hash. */
792+ error = xchk_dir_walk (sc , sc -> ip , xchk_dir_actor , NULL );
793+ if (error == - ECANCELED )
794+ error = 0 ;
877795 return error ;
878796}
0 commit comments