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,30 +32,21 @@ 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. */
35+ /* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */
4236STATIC int
4337xchk_dir_check_ftype (
44- struct xchk_dir_ctx * sdc ,
38+ struct xfs_scrub * sc ,
4539 xfs_fileoff_t offset ,
4640 xfs_ino_t inum ,
47- int dtype )
41+ int ftype )
4842{
49- struct xfs_mount * mp = sdc -> sc -> mp ;
43+ struct xfs_mount * mp = sc -> mp ;
5044 struct xfs_inode * ip ;
51- int ino_dtype ;
5245 int error = 0 ;
5346
5447 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 );
48+ if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR )
49+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
5850 goto out ;
5951 }
6052
@@ -71,21 +63,17 @@ xchk_dir_check_ftype(
7163 * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a
7264 * cross referencing error. Any other error is an operational error.
7365 */
74- error = xfs_iget (mp , sdc -> sc -> tp , inum , 0 , 0 , & ip );
66+ error = xfs_iget (mp , sc -> tp , inum , 0 , 0 , & ip );
7567 if (error == - EINVAL || error == - ENOENT ) {
7668 error = - EFSCORRUPTED ;
77- xchk_fblock_process_error (sdc -> sc , XFS_DATA_FORK , 0 , & error );
69+ xchk_fblock_process_error (sc , XFS_DATA_FORK , 0 , & error );
7870 goto out ;
7971 }
80- if (!xchk_fblock_xref_process_error (sdc -> sc , XFS_DATA_FORK , offset ,
81- & error ))
72+ if (!xchk_fblock_xref_process_error (sc , XFS_DATA_FORK , offset , & error ))
8273 goto out ;
8374
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 );
75+ if (xfs_mode_to_ftype (VFS_I (ip )-> i_mode ) != ftype )
76+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
8977 xfs_irele (ip );
9078out :
9179 return error ;
@@ -94,105 +82,85 @@ xchk_dir_check_ftype(
9482/*
9583 * Scrub a single directory entry.
9684 *
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.
85+ * Check the inode number to make sure it's sane, then we check that we can
86+ * look up this filename. Finally, we check the ftype.
10187 */
102- STATIC bool
88+ STATIC int
10389xchk_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 )
90+ struct xfs_scrub * sc ,
91+ struct xfs_inode * dp ,
92+ xfs_dir2_dataptr_t dapos ,
93+ const struct xfs_name * name ,
94+ xfs_ino_t ino ,
95+ void * priv )
11096{
111- struct xfs_mount * mp ;
112- struct xfs_inode * ip ;
113- struct xchk_dir_ctx * sdc ;
114- struct xfs_name xname ;
97+ struct xfs_mount * mp = dp -> i_mount ;
11598 xfs_ino_t lookup_ino ;
11699 xfs_dablk_t offset ;
117100 bool checked_ftype = false;
118101 int error = 0 ;
119102
120- sdc = container_of (dir_iter , struct xchk_dir_ctx , dir_iter );
121- ip = sdc -> sc -> ip ;
122- mp = ip -> i_mount ;
123103 offset = xfs_dir2_db_to_da (mp -> m_dir_geo ,
124- xfs_dir2_dataptr_to_db (mp -> m_dir_geo , pos ));
104+ xfs_dir2_dataptr_to_db (mp -> m_dir_geo , dapos ));
125105
126- if (xchk_should_terminate (sdc -> sc , & error ))
127- return ! error ;
106+ if (xchk_should_terminate (sc , & error ))
107+ return error ;
128108
129109 /* Does this inode number make sense? */
130110 if (!xfs_verify_dir_ino (mp , ino )) {
131- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
132- goto out ;
111+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
112+ return - ECANCELED ;
133113 }
134114
135115 /* 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 ;
116+ if (!xfs_dir2_namecheck (name -> name , name -> len )) {
117+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
118+ return - ECANCELED ;
139119 }
140120
141- if (!strncmp ("." , name , namelen )) {
121+ if (!strncmp ("." , name -> name , name -> len )) {
142122 /* 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 );
123+ if (xfs_has_ftype (mp ) && name -> type != XFS_DIR3_FT_DIR )
124+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
146125 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 )) {
126+ if (ino != dp -> i_ino )
127+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
128+ } else if (!strncmp (".." , name -> name , name -> len )) {
151129 /*
152130 * If this is ".." in the root inode, check that the inum
153131 * matches this dir.
154132 */
155- if (xfs_has_ftype (mp ) && type != DT_DIR )
156- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK ,
157- offset );
133+ if (xfs_has_ftype (mp ) && name -> type != XFS_DIR3_FT_DIR )
134+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
158135 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 );
136+ if (dp -> i_ino == mp -> m_sb .sb_rootino && ino != dp -> i_ino )
137+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
162138 }
163139
164140 /* 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 );
141+ error = xchk_dir_lookup (sc , dp , name , & lookup_ino );
170142 /* ENOENT means the hash lookup failed and the dir is corrupt */
171143 if (error == - ENOENT )
172144 error = - EFSCORRUPTED ;
173- if (!xchk_fblock_process_error (sdc -> sc , XFS_DATA_FORK , offset ,
174- & error ))
145+ if (!xchk_fblock_process_error (sc , XFS_DATA_FORK , offset , & error ))
175146 goto out ;
176147 if (lookup_ino != ino ) {
177- xchk_fblock_set_corrupt (sdc -> sc , XFS_DATA_FORK , offset );
178- goto out ;
148+ xchk_fblock_set_corrupt (sc , XFS_DATA_FORK , offset );
149+ return - ECANCELED ;
179150 }
180151
181152 /* Verify the file type. This function absorbs error codes. */
182153 if (!checked_ftype ) {
183- error = xchk_dir_check_ftype (sdc , offset , lookup_ino , type );
154+ error = xchk_dir_check_ftype (sc , offset , lookup_ino ,
155+ name -> type );
184156 if (error )
185157 goto out ;
186158 }
159+
187160out :
188- /*
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.
192- */
193- if (error == 0 && sdc -> sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
194- return false;
195- return !error ;
161+ if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
162+ return - ECANCELED ;
163+ return error ;
196164}
197165
198166/* Scrub a directory btree record. */
@@ -808,22 +776,15 @@ int
808776xchk_directory (
809777 struct xfs_scrub * sc )
810778{
811- struct xchk_dir_ctx sdc = {
812- .dir_iter .actor = xchk_dir_actor ,
813- .dir_iter .pos = 0 ,
814- .sc = sc ,
815- };
816- size_t bufsize ;
817- loff_t oldpos ;
818- int error = 0 ;
779+ int error ;
819780
820781 if (!S_ISDIR (VFS_I (sc -> ip )-> i_mode ))
821782 return - ENOENT ;
822783
823784 /* Plausible size? */
824785 if (sc -> ip -> i_disk_size < xfs_dir2_sf_hdr_size (0 )) {
825786 xchk_ino_set_corrupt (sc , sc -> ip -> i_ino );
826- goto out ;
787+ return 0 ;
827788 }
828789
829790 /* Check directory tree structure */
@@ -832,52 +793,19 @@ xchk_directory(
832793 return error ;
833794
834795 if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
835- return error ;
796+ return 0 ;
836797
837798 /* Check the freespace. */
838799 error = xchk_directory_blocks (sc );
839800 if (error )
840801 return error ;
841802
842803 if (sc -> sm -> sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
843- return error ;
844-
845- /*
846- * Check that every dirent we see can also be looked up by hash.
847- * Userspace usually asks for a 32k buffer, so we will too.
848- */
849- bufsize = (size_t )min_t (loff_t , XFS_READDIR_BUFSIZE ,
850- sc -> ip -> i_disk_size );
851-
852- /*
853- * Look up every name in this directory by hash.
854- *
855- * Use the xfs_readdir function to call xchk_dir_actor on
856- * every directory entry in this directory. In _actor, we check
857- * the name, inode number, and ftype (if applicable) of the
858- * entry. xfs_readdir uses the VFS filldir functions to provide
859- * iteration context.
860- *
861- * The VFS grabs a read or write lock via i_rwsem before it reads
862- * or writes to a directory. If we've gotten this far we've
863- * already obtained IOLOCK_EXCL, which (since 4.10) is the same as
864- * getting a write lock on i_rwsem. Therefore, it is safe for us
865- * to drop the ILOCK here in order to reuse the _readdir and
866- * _dir_lookup routines, which do their own ILOCK locking.
867- */
868- oldpos = 0 ;
869- sc -> ilock_flags &= ~XFS_ILOCK_EXCL ;
870- xfs_iunlock (sc -> ip , XFS_ILOCK_EXCL );
871- while (true) {
872- error = xfs_readdir (sc -> tp , sc -> ip , & sdc .dir_iter , bufsize );
873- if (!xchk_fblock_process_error (sc , XFS_DATA_FORK , 0 ,
874- & error ))
875- goto out ;
876- if (oldpos == sdc .dir_iter .pos )
877- break ;
878- oldpos = sdc .dir_iter .pos ;
879- }
804+ return 0 ;
880805
881- out :
806+ /* Look up every name in this directory by hash. */
807+ error = xchk_dir_walk (sc , sc -> ip , xchk_dir_actor , NULL );
808+ if (error == - ECANCELED )
809+ error = 0 ;
882810 return error ;
883811}
0 commit comments