1b1991ee3SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 2b1991ee3SDarrick J. Wong /* 3b1991ee3SDarrick J. Wong * Copyright (c) 2020-2024 Oracle. All Rights Reserved. 4b1991ee3SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 5b1991ee3SDarrick J. Wong */ 6b1991ee3SDarrick J. Wong #include "xfs.h" 7b1991ee3SDarrick J. Wong #include "xfs_fs.h" 8b1991ee3SDarrick J. Wong #include "xfs_shared.h" 9b1991ee3SDarrick J. Wong #include "xfs_format.h" 10b1991ee3SDarrick J. Wong #include "xfs_trans_resv.h" 11b1991ee3SDarrick J. Wong #include "xfs_mount.h" 12b1991ee3SDarrick J. Wong #include "xfs_defer.h" 13b1991ee3SDarrick J. Wong #include "xfs_bit.h" 14b1991ee3SDarrick J. Wong #include "xfs_log_format.h" 15b1991ee3SDarrick J. Wong #include "xfs_trans.h" 16b1991ee3SDarrick J. Wong #include "xfs_sb.h" 17b1991ee3SDarrick J. Wong #include "xfs_inode.h" 18b1991ee3SDarrick J. Wong #include "xfs_icache.h" 19b1991ee3SDarrick J. Wong #include "xfs_da_format.h" 20b1991ee3SDarrick J. Wong #include "xfs_da_btree.h" 21b1991ee3SDarrick J. Wong #include "xfs_dir2.h" 22b1991ee3SDarrick J. Wong #include "xfs_dir2_priv.h" 23b1991ee3SDarrick J. Wong #include "xfs_bmap.h" 24b1991ee3SDarrick J. Wong #include "xfs_quota.h" 25b1991ee3SDarrick J. Wong #include "xfs_bmap_btree.h" 26b1991ee3SDarrick J. Wong #include "xfs_trans_space.h" 27b1991ee3SDarrick J. Wong #include "xfs_bmap_util.h" 28b1991ee3SDarrick J. Wong #include "xfs_exchmaps.h" 29b1991ee3SDarrick J. Wong #include "xfs_exchrange.h" 30b1991ee3SDarrick J. Wong #include "xfs_ag.h" 3176fc23b6SDarrick J. Wong #include "xfs_parent.h" 32b1991ee3SDarrick J. Wong #include "scrub/xfs_scrub.h" 33b1991ee3SDarrick J. Wong #include "scrub/scrub.h" 34b1991ee3SDarrick J. Wong #include "scrub/common.h" 35b1991ee3SDarrick J. Wong #include "scrub/trace.h" 36b1991ee3SDarrick J. Wong #include "scrub/repair.h" 37b1991ee3SDarrick J. Wong #include "scrub/tempfile.h" 38b1991ee3SDarrick J. Wong #include "scrub/tempexch.h" 39b1991ee3SDarrick J. Wong #include "scrub/xfile.h" 40b1991ee3SDarrick J. Wong #include "scrub/xfarray.h" 41b1991ee3SDarrick J. Wong #include "scrub/xfblob.h" 42a07b4557SDarrick J. Wong #include "scrub/iscan.h" 43b1991ee3SDarrick J. Wong #include "scrub/readdir.h" 44b1991ee3SDarrick J. Wong #include "scrub/reap.h" 45a07b4557SDarrick J. Wong #include "scrub/findparent.h" 461e58a8ccSDarrick J. Wong #include "scrub/orphanage.h" 4776fc23b6SDarrick J. Wong #include "scrub/listxattr.h" 48b1991ee3SDarrick J. Wong 49b1991ee3SDarrick J. Wong /* 50b1991ee3SDarrick J. Wong * Directory Repair 51b1991ee3SDarrick J. Wong * ================ 52b1991ee3SDarrick J. Wong * 53b1991ee3SDarrick J. Wong * We repair directories by reading the directory data blocks looking for 54b1991ee3SDarrick J. Wong * directory entries that look salvageable (name passes verifiers, entry points 55b1991ee3SDarrick J. Wong * to a valid allocated inode, etc). Each entry worth salvaging is stashed in 56b1991ee3SDarrick J. Wong * memory, and the stashed entries are periodically replayed into a temporary 57b1991ee3SDarrick J. Wong * directory to constrain memory use. Batching the construction of the 58b1991ee3SDarrick J. Wong * temporary directory in this fashion reduces lock cycling of the directory 59b1991ee3SDarrick J. Wong * being repaired and the temporary directory, and will later become important 60b1991ee3SDarrick J. Wong * for parent pointer scanning. 61b1991ee3SDarrick J. Wong * 6276fc23b6SDarrick J. Wong * If parent pointers are enabled on this filesystem, we instead reconstruct 6376fc23b6SDarrick J. Wong * the directory by visiting each parent pointer of each file in the filesystem 6476fc23b6SDarrick J. Wong * and translating the relevant parent pointer records into dirents. In this 6576fc23b6SDarrick J. Wong * case, it is advantageous to stash all directory entries created from parent 6676fc23b6SDarrick J. Wong * pointers for a single child file before replaying them into the temporary 6776fc23b6SDarrick J. Wong * directory. To save memory, the live filesystem scan reuses the findparent 6876fc23b6SDarrick J. Wong * fields. Directory repair chooses either parent pointer scanning or 6976fc23b6SDarrick J. Wong * directory entry salvaging, but not both. 7076fc23b6SDarrick J. Wong * 71b1991ee3SDarrick J. Wong * Directory entries added to the temporary directory do not elevate the link 72b1991ee3SDarrick J. Wong * counts of the inodes found. When salvaging completes, the remaining stashed 73b1991ee3SDarrick J. Wong * entries are replayed to the temporary directory. An atomic mapping exchange 74b1991ee3SDarrick J. Wong * is used to commit the new directory blocks to the directory being repaired. 75b1991ee3SDarrick J. Wong * This will disrupt readdir cursors. 76b1991ee3SDarrick J. Wong * 77b1991ee3SDarrick J. Wong * Locking Issues 78b1991ee3SDarrick J. Wong * -------------- 79b1991ee3SDarrick J. Wong * 80b1991ee3SDarrick J. Wong * If /a, /a/b, and /c are all directories, the VFS does not take i_rwsem on 81b1991ee3SDarrick J. Wong * /a/b for a "mv /a/b /c/" operation. This means that only b's ILOCK protects 82b1991ee3SDarrick J. Wong * b's dotdot update. This is in contrast to every other dotdot update (link, 83b1991ee3SDarrick J. Wong * remove, mkdir). If the repair code drops the ILOCK, it must either 84b1991ee3SDarrick J. Wong * revalidate the dotdot entry or use dirent hooks to capture updates from 85b1991ee3SDarrick J. Wong * other threads. 86b1991ee3SDarrick J. Wong */ 87b1991ee3SDarrick J. Wong 888559b21aSDarrick J. Wong /* Create a dirent in the tempdir. */ 898559b21aSDarrick J. Wong #define XREP_DIRENT_ADD (1) 908559b21aSDarrick J. Wong 918559b21aSDarrick J. Wong /* Remove a dirent from the tempdir. */ 928559b21aSDarrick J. Wong #define XREP_DIRENT_REMOVE (2) 938559b21aSDarrick J. Wong 94b1991ee3SDarrick J. Wong /* Directory entry to be restored in the new directory. */ 95b1991ee3SDarrick J. Wong struct xrep_dirent { 96b1991ee3SDarrick J. Wong /* Cookie for retrieval of the dirent name. */ 97b1991ee3SDarrick J. Wong xfblob_cookie name_cookie; 98b1991ee3SDarrick J. Wong 99b1991ee3SDarrick J. Wong /* Target inode number. */ 100b1991ee3SDarrick J. Wong xfs_ino_t ino; 101b1991ee3SDarrick J. Wong 102b1991ee3SDarrick J. Wong /* Length of the dirent name. */ 103b1991ee3SDarrick J. Wong uint8_t namelen; 104b1991ee3SDarrick J. Wong 105b1991ee3SDarrick J. Wong /* File type of the dirent. */ 106b1991ee3SDarrick J. Wong uint8_t ftype; 1078559b21aSDarrick J. Wong 1088559b21aSDarrick J. Wong /* XREP_DIRENT_{ADD,REMOVE} */ 1098559b21aSDarrick J. Wong uint8_t action; 110b1991ee3SDarrick J. Wong }; 111b1991ee3SDarrick J. Wong 112b1991ee3SDarrick J. Wong /* 113b1991ee3SDarrick J. Wong * Stash up to 8 pages of recovered dirent data in dir_entries and dir_names 114b1991ee3SDarrick J. Wong * before we write them to the temp dir. 115b1991ee3SDarrick J. Wong */ 116b1991ee3SDarrick J. Wong #define XREP_DIR_MAX_STASH_BYTES (PAGE_SIZE * 8) 117b1991ee3SDarrick J. Wong 118b1991ee3SDarrick J. Wong struct xrep_dir { 119b1991ee3SDarrick J. Wong struct xfs_scrub *sc; 120b1991ee3SDarrick J. Wong 121b1991ee3SDarrick J. Wong /* Fixed-size array of xrep_dirent structures. */ 122b1991ee3SDarrick J. Wong struct xfarray *dir_entries; 123b1991ee3SDarrick J. Wong 124b1991ee3SDarrick J. Wong /* Blobs containing directory entry names. */ 125b1991ee3SDarrick J. Wong struct xfblob *dir_names; 126b1991ee3SDarrick J. Wong 127b1991ee3SDarrick J. Wong /* Information for exchanging data forks at the end. */ 128b1991ee3SDarrick J. Wong struct xrep_tempexch tx; 129b1991ee3SDarrick J. Wong 130b1991ee3SDarrick J. Wong /* Preallocated args struct for performing dir operations */ 131b1991ee3SDarrick J. Wong struct xfs_da_args args; 132b1991ee3SDarrick J. Wong 133b1991ee3SDarrick J. Wong /* 134a07b4557SDarrick J. Wong * Information used to scan the filesystem to find the inumber of the 13576fc23b6SDarrick J. Wong * dotdot entry for this directory. For directory salvaging when 13676fc23b6SDarrick J. Wong * parent pointers are not enabled, we use the findparent_* functions 13776fc23b6SDarrick J. Wong * on this object and access only the parent_ino field directly. 13876fc23b6SDarrick J. Wong * 13976fc23b6SDarrick J. Wong * When parent pointers are enabled, however, the pptr scanner uses the 14076fc23b6SDarrick J. Wong * iscan, hooks, lock, and parent_ino fields of this object directly. 14176fc23b6SDarrick J. Wong * @pscan.lock coordinates access to dir_entries, dir_names, 14276fc23b6SDarrick J. Wong * parent_ino, subdirs, dirents, and args. This reduces the memory 14376fc23b6SDarrick J. Wong * requirements of this structure. 144b1991ee3SDarrick J. Wong */ 145a07b4557SDarrick J. Wong struct xrep_parent_scan_info pscan; 146b1991ee3SDarrick J. Wong 1471e58a8ccSDarrick J. Wong /* 1481e58a8ccSDarrick J. Wong * Context information for attaching this directory to the lost+found 1491e58a8ccSDarrick J. Wong * if this directory does not have a parent. 1501e58a8ccSDarrick J. Wong */ 1511e58a8ccSDarrick J. Wong struct xrep_adoption adoption; 1521e58a8ccSDarrick J. Wong 153b1991ee3SDarrick J. Wong /* How many subdirectories did we find? */ 154b1991ee3SDarrick J. Wong uint64_t subdirs; 155b1991ee3SDarrick J. Wong 156b1991ee3SDarrick J. Wong /* How many dirents did we find? */ 157b1991ee3SDarrick J. Wong unsigned int dirents; 158b1991ee3SDarrick J. Wong 1591e58a8ccSDarrick J. Wong /* Should we move this directory to the orphanage? */ 1601e58a8ccSDarrick J. Wong bool needs_adoption; 1611e58a8ccSDarrick J. Wong 162b1991ee3SDarrick J. Wong /* Directory entry name, plus the trailing null. */ 163b1991ee3SDarrick J. Wong struct xfs_name xname; 164b1991ee3SDarrick J. Wong unsigned char namebuf[MAXNAMELEN]; 165b1991ee3SDarrick J. Wong }; 166b1991ee3SDarrick J. Wong 167b1991ee3SDarrick J. Wong /* Tear down all the incore stuff we created. */ 168b1991ee3SDarrick J. Wong static void 169b1991ee3SDarrick J. Wong xrep_dir_teardown( 170b1991ee3SDarrick J. Wong struct xfs_scrub *sc) 171b1991ee3SDarrick J. Wong { 172b1991ee3SDarrick J. Wong struct xrep_dir *rd = sc->buf; 173b1991ee3SDarrick J. Wong 174a07b4557SDarrick J. Wong xrep_findparent_scan_teardown(&rd->pscan); 175b1991ee3SDarrick J. Wong xfblob_destroy(rd->dir_names); 176b1991ee3SDarrick J. Wong xfarray_destroy(rd->dir_entries); 177b1991ee3SDarrick J. Wong } 178b1991ee3SDarrick J. Wong 179b1991ee3SDarrick J. Wong /* Set up for a directory repair. */ 180b1991ee3SDarrick J. Wong int 181b1991ee3SDarrick J. Wong xrep_setup_directory( 182b1991ee3SDarrick J. Wong struct xfs_scrub *sc) 183b1991ee3SDarrick J. Wong { 184b1991ee3SDarrick J. Wong struct xrep_dir *rd; 185b1991ee3SDarrick J. Wong int error; 186b1991ee3SDarrick J. Wong 187a07b4557SDarrick J. Wong xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS); 188a07b4557SDarrick J. Wong 1891e58a8ccSDarrick J. Wong error = xrep_orphanage_try_create(sc); 1901e58a8ccSDarrick J. Wong if (error) 1911e58a8ccSDarrick J. Wong return error; 1921e58a8ccSDarrick J. Wong 193b1991ee3SDarrick J. Wong error = xrep_tempfile_create(sc, S_IFDIR); 194b1991ee3SDarrick J. Wong if (error) 195b1991ee3SDarrick J. Wong return error; 196b1991ee3SDarrick J. Wong 197b1991ee3SDarrick J. Wong rd = kvzalloc(sizeof(struct xrep_dir), XCHK_GFP_FLAGS); 198b1991ee3SDarrick J. Wong if (!rd) 199b1991ee3SDarrick J. Wong return -ENOMEM; 200b1991ee3SDarrick J. Wong rd->sc = sc; 201b1991ee3SDarrick J. Wong rd->xname.name = rd->namebuf; 202b1991ee3SDarrick J. Wong sc->buf = rd; 203b1991ee3SDarrick J. Wong 204b1991ee3SDarrick J. Wong return 0; 205b1991ee3SDarrick J. Wong } 206b1991ee3SDarrick J. Wong 207b1991ee3SDarrick J. Wong /* 208a07b4557SDarrick J. Wong * Look up the dotdot entry and confirm that it's really the parent. 209a07b4557SDarrick J. Wong * Returns NULLFSINO if we don't know what to do. 210b1991ee3SDarrick J. Wong */ 211b1991ee3SDarrick J. Wong static inline xfs_ino_t 212b1991ee3SDarrick J. Wong xrep_dir_lookup_parent( 213b1991ee3SDarrick J. Wong struct xrep_dir *rd) 214b1991ee3SDarrick J. Wong { 215b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 216b1991ee3SDarrick J. Wong xfs_ino_t ino; 217b1991ee3SDarrick J. Wong int error; 218b1991ee3SDarrick J. Wong 219b1991ee3SDarrick J. Wong error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &ino, NULL); 220b1991ee3SDarrick J. Wong if (error) 221b1991ee3SDarrick J. Wong return NULLFSINO; 222b1991ee3SDarrick J. Wong if (!xfs_verify_dir_ino(sc->mp, ino)) 223b1991ee3SDarrick J. Wong return NULLFSINO; 224b1991ee3SDarrick J. Wong 225a07b4557SDarrick J. Wong error = xrep_findparent_confirm(sc, &ino); 226a07b4557SDarrick J. Wong if (error) 227a07b4557SDarrick J. Wong return NULLFSINO; 228a07b4557SDarrick J. Wong 229b1991ee3SDarrick J. Wong return ino; 230b1991ee3SDarrick J. Wong } 231b1991ee3SDarrick J. Wong 23234c9382cSDarrick J. Wong /* 23334c9382cSDarrick J. Wong * Look up '..' in the dentry cache and confirm that it's really the parent. 23434c9382cSDarrick J. Wong * Returns NULLFSINO if the dcache misses or if the hit is implausible. 23534c9382cSDarrick J. Wong */ 23634c9382cSDarrick J. Wong static inline xfs_ino_t 23734c9382cSDarrick J. Wong xrep_dir_dcache_parent( 23834c9382cSDarrick J. Wong struct xrep_dir *rd) 23934c9382cSDarrick J. Wong { 24034c9382cSDarrick J. Wong struct xfs_scrub *sc = rd->sc; 24134c9382cSDarrick J. Wong xfs_ino_t parent_ino; 24234c9382cSDarrick J. Wong int error; 24334c9382cSDarrick J. Wong 24434c9382cSDarrick J. Wong parent_ino = xrep_findparent_from_dcache(sc); 24534c9382cSDarrick J. Wong if (parent_ino == NULLFSINO) 24634c9382cSDarrick J. Wong return parent_ino; 24734c9382cSDarrick J. Wong 24834c9382cSDarrick J. Wong error = xrep_findparent_confirm(sc, &parent_ino); 24934c9382cSDarrick J. Wong if (error) 25034c9382cSDarrick J. Wong return NULLFSINO; 25134c9382cSDarrick J. Wong 25234c9382cSDarrick J. Wong return parent_ino; 25334c9382cSDarrick J. Wong } 25434c9382cSDarrick J. Wong 255a07b4557SDarrick J. Wong /* Try to find the parent of the directory being repaired. */ 256b1991ee3SDarrick J. Wong STATIC int 257b1991ee3SDarrick J. Wong xrep_dir_find_parent( 258b1991ee3SDarrick J. Wong struct xrep_dir *rd) 259b1991ee3SDarrick J. Wong { 260b1991ee3SDarrick J. Wong xfs_ino_t ino; 261b1991ee3SDarrick J. Wong 262a07b4557SDarrick J. Wong ino = xrep_findparent_self_reference(rd->sc); 263b1991ee3SDarrick J. Wong if (ino != NULLFSINO) { 264a07b4557SDarrick J. Wong xrep_findparent_scan_finish_early(&rd->pscan, ino); 265b1991ee3SDarrick J. Wong return 0; 266b1991ee3SDarrick J. Wong } 267b1991ee3SDarrick J. Wong 26834c9382cSDarrick J. Wong ino = xrep_dir_dcache_parent(rd); 26934c9382cSDarrick J. Wong if (ino != NULLFSINO) { 27034c9382cSDarrick J. Wong xrep_findparent_scan_finish_early(&rd->pscan, ino); 27134c9382cSDarrick J. Wong return 0; 27234c9382cSDarrick J. Wong } 27334c9382cSDarrick J. Wong 274b1991ee3SDarrick J. Wong ino = xrep_dir_lookup_parent(rd); 275b1991ee3SDarrick J. Wong if (ino != NULLFSINO) { 276a07b4557SDarrick J. Wong xrep_findparent_scan_finish_early(&rd->pscan, ino); 277b1991ee3SDarrick J. Wong return 0; 278b1991ee3SDarrick J. Wong } 279b1991ee3SDarrick J. Wong 280a07b4557SDarrick J. Wong /* 281a07b4557SDarrick J. Wong * A full filesystem scan is the last resort. On a busy filesystem, 282a07b4557SDarrick J. Wong * the scan can fail with -EBUSY if we cannot grab IOLOCKs. That means 283a07b4557SDarrick J. Wong * that we don't know what who the parent is, so we should return to 284a07b4557SDarrick J. Wong * userspace. 285a07b4557SDarrick J. Wong */ 286a07b4557SDarrick J. Wong return xrep_findparent_scan(&rd->pscan); 287b1991ee3SDarrick J. Wong } 288b1991ee3SDarrick J. Wong 289b1991ee3SDarrick J. Wong /* 290b1991ee3SDarrick J. Wong * Decide if we want to salvage this entry. We don't bother with oversized 291b1991ee3SDarrick J. Wong * names or the dot entry. 292b1991ee3SDarrick J. Wong */ 293b1991ee3SDarrick J. Wong STATIC int 294b1991ee3SDarrick J. Wong xrep_dir_want_salvage( 295b1991ee3SDarrick J. Wong struct xrep_dir *rd, 296b1991ee3SDarrick J. Wong const char *name, 297b1991ee3SDarrick J. Wong int namelen, 298b1991ee3SDarrick J. Wong xfs_ino_t ino) 299b1991ee3SDarrick J. Wong { 300b1991ee3SDarrick J. Wong struct xfs_mount *mp = rd->sc->mp; 301b1991ee3SDarrick J. Wong 302b1991ee3SDarrick J. Wong /* No pointers to ourselves or to garbage. */ 303b1991ee3SDarrick J. Wong if (ino == rd->sc->ip->i_ino) 304b1991ee3SDarrick J. Wong return false; 305b1991ee3SDarrick J. Wong if (!xfs_verify_dir_ino(mp, ino)) 306b1991ee3SDarrick J. Wong return false; 307b1991ee3SDarrick J. Wong 308b1991ee3SDarrick J. Wong /* No weird looking names or dot entries. */ 309b1991ee3SDarrick J. Wong if (namelen >= MAXNAMELEN || namelen <= 0) 310b1991ee3SDarrick J. Wong return false; 311b1991ee3SDarrick J. Wong if (namelen == 1 && name[0] == '.') 312b1991ee3SDarrick J. Wong return false; 313b1991ee3SDarrick J. Wong if (!xfs_dir2_namecheck(name, namelen)) 314b1991ee3SDarrick J. Wong return false; 315b1991ee3SDarrick J. Wong 316b1991ee3SDarrick J. Wong return true; 317b1991ee3SDarrick J. Wong } 318b1991ee3SDarrick J. Wong 319b1991ee3SDarrick J. Wong /* 320b1991ee3SDarrick J. Wong * Remember that we want to create a dirent in the tempdir. These stashed 321b1991ee3SDarrick J. Wong * actions will be replayed later. 322b1991ee3SDarrick J. Wong */ 323b1991ee3SDarrick J. Wong STATIC int 324b1991ee3SDarrick J. Wong xrep_dir_stash_createname( 325b1991ee3SDarrick J. Wong struct xrep_dir *rd, 326b1991ee3SDarrick J. Wong const struct xfs_name *name, 327b1991ee3SDarrick J. Wong xfs_ino_t ino) 328b1991ee3SDarrick J. Wong { 329b1991ee3SDarrick J. Wong struct xrep_dirent dirent = { 3308559b21aSDarrick J. Wong .action = XREP_DIRENT_ADD, 331b1991ee3SDarrick J. Wong .ino = ino, 332b1991ee3SDarrick J. Wong .namelen = name->len, 333b1991ee3SDarrick J. Wong .ftype = name->type, 334b1991ee3SDarrick J. Wong }; 335b1991ee3SDarrick J. Wong int error; 336b1991ee3SDarrick J. Wong 337b1991ee3SDarrick J. Wong trace_xrep_dir_stash_createname(rd->sc->tempip, name, ino); 338b1991ee3SDarrick J. Wong 339b1991ee3SDarrick J. Wong error = xfblob_storename(rd->dir_names, &dirent.name_cookie, name); 340b1991ee3SDarrick J. Wong if (error) 341b1991ee3SDarrick J. Wong return error; 342b1991ee3SDarrick J. Wong 343b1991ee3SDarrick J. Wong return xfarray_append(rd->dir_entries, &dirent); 344b1991ee3SDarrick J. Wong } 345b1991ee3SDarrick J. Wong 3468559b21aSDarrick J. Wong /* 3478559b21aSDarrick J. Wong * Remember that we want to remove a dirent from the tempdir. These stashed 3488559b21aSDarrick J. Wong * actions will be replayed later. 3498559b21aSDarrick J. Wong */ 3508559b21aSDarrick J. Wong STATIC int 3518559b21aSDarrick J. Wong xrep_dir_stash_removename( 3528559b21aSDarrick J. Wong struct xrep_dir *rd, 3538559b21aSDarrick J. Wong const struct xfs_name *name, 3548559b21aSDarrick J. Wong xfs_ino_t ino) 3558559b21aSDarrick J. Wong { 3568559b21aSDarrick J. Wong struct xrep_dirent dirent = { 3578559b21aSDarrick J. Wong .action = XREP_DIRENT_REMOVE, 3588559b21aSDarrick J. Wong .ino = ino, 3598559b21aSDarrick J. Wong .namelen = name->len, 3608559b21aSDarrick J. Wong .ftype = name->type, 3618559b21aSDarrick J. Wong }; 3628559b21aSDarrick J. Wong int error; 3638559b21aSDarrick J. Wong 3648559b21aSDarrick J. Wong trace_xrep_dir_stash_removename(rd->sc->tempip, name, ino); 3658559b21aSDarrick J. Wong 3668559b21aSDarrick J. Wong error = xfblob_storename(rd->dir_names, &dirent.name_cookie, name); 3678559b21aSDarrick J. Wong if (error) 3688559b21aSDarrick J. Wong return error; 3698559b21aSDarrick J. Wong 3708559b21aSDarrick J. Wong return xfarray_append(rd->dir_entries, &dirent); 3718559b21aSDarrick J. Wong } 3728559b21aSDarrick J. Wong 373b1991ee3SDarrick J. Wong /* Allocate an in-core record to hold entries while we rebuild the dir data. */ 374b1991ee3SDarrick J. Wong STATIC int 375b1991ee3SDarrick J. Wong xrep_dir_salvage_entry( 376b1991ee3SDarrick J. Wong struct xrep_dir *rd, 377b1991ee3SDarrick J. Wong unsigned char *name, 378b1991ee3SDarrick J. Wong unsigned int namelen, 379b1991ee3SDarrick J. Wong xfs_ino_t ino) 380b1991ee3SDarrick J. Wong { 381b1991ee3SDarrick J. Wong struct xfs_name xname = { 382b1991ee3SDarrick J. Wong .name = name, 383b1991ee3SDarrick J. Wong }; 384b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 385b1991ee3SDarrick J. Wong struct xfs_inode *ip; 386b1991ee3SDarrick J. Wong unsigned int i = 0; 387b1991ee3SDarrick J. Wong int error = 0; 388b1991ee3SDarrick J. Wong 389b1991ee3SDarrick J. Wong if (xchk_should_terminate(sc, &error)) 390b1991ee3SDarrick J. Wong return error; 391b1991ee3SDarrick J. Wong 392b1991ee3SDarrick J. Wong /* 393b1991ee3SDarrick J. Wong * Truncate the name to the first character that would trip namecheck. 394b1991ee3SDarrick J. Wong * If we no longer have a name after that, ignore this entry. 395b1991ee3SDarrick J. Wong */ 396b1991ee3SDarrick J. Wong while (i < namelen && name[i] != 0 && name[i] != '/') 397b1991ee3SDarrick J. Wong i++; 398b1991ee3SDarrick J. Wong if (i == 0) 399b1991ee3SDarrick J. Wong return 0; 400b1991ee3SDarrick J. Wong xname.len = i; 401b1991ee3SDarrick J. Wong 402b1991ee3SDarrick J. Wong /* Ignore '..' entries; we already picked the new parent. */ 403b1991ee3SDarrick J. Wong if (xname.len == 2 && name[0] == '.' && name[1] == '.') { 404b1991ee3SDarrick J. Wong trace_xrep_dir_salvaged_parent(sc->ip, ino); 405b1991ee3SDarrick J. Wong return 0; 406b1991ee3SDarrick J. Wong } 407b1991ee3SDarrick J. Wong 408b1991ee3SDarrick J. Wong trace_xrep_dir_salvage_entry(sc->ip, &xname, ino); 409b1991ee3SDarrick J. Wong 410b1991ee3SDarrick J. Wong /* 411b1991ee3SDarrick J. Wong * Compute the ftype or dump the entry if we can't. We don't lock the 412b1991ee3SDarrick J. Wong * inode because inodes can't change type while we have a reference. 413b1991ee3SDarrick J. Wong */ 414b1991ee3SDarrick J. Wong error = xchk_iget(sc, ino, &ip); 415b1991ee3SDarrick J. Wong if (error) 416b1991ee3SDarrick J. Wong return 0; 417b1991ee3SDarrick J. Wong 4183d2c3411SDarrick J. Wong /* Don't mix metadata and regular directory trees. */ 4193d2c3411SDarrick J. Wong if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(rd->sc->ip)) { 4203d2c3411SDarrick J. Wong xchk_irele(sc, ip); 4213d2c3411SDarrick J. Wong return 0; 4223d2c3411SDarrick J. Wong } 4233d2c3411SDarrick J. Wong 424b1991ee3SDarrick J. Wong xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode); 425b1991ee3SDarrick J. Wong xchk_irele(sc, ip); 426b1991ee3SDarrick J. Wong 427b1991ee3SDarrick J. Wong return xrep_dir_stash_createname(rd, &xname, ino); 428b1991ee3SDarrick J. Wong } 429b1991ee3SDarrick J. Wong 430b1991ee3SDarrick J. Wong /* Record a shortform directory entry for later reinsertion. */ 431b1991ee3SDarrick J. Wong STATIC int 432b1991ee3SDarrick J. Wong xrep_dir_salvage_sf_entry( 433b1991ee3SDarrick J. Wong struct xrep_dir *rd, 434b1991ee3SDarrick J. Wong struct xfs_dir2_sf_hdr *sfp, 435b1991ee3SDarrick J. Wong struct xfs_dir2_sf_entry *sfep) 436b1991ee3SDarrick J. Wong { 437b1991ee3SDarrick J. Wong xfs_ino_t ino; 438b1991ee3SDarrick J. Wong 439b1991ee3SDarrick J. Wong ino = xfs_dir2_sf_get_ino(rd->sc->mp, sfp, sfep); 440b1991ee3SDarrick J. Wong if (!xrep_dir_want_salvage(rd, sfep->name, sfep->namelen, ino)) 441b1991ee3SDarrick J. Wong return 0; 442b1991ee3SDarrick J. Wong 443b1991ee3SDarrick J. Wong return xrep_dir_salvage_entry(rd, sfep->name, sfep->namelen, ino); 444b1991ee3SDarrick J. Wong } 445b1991ee3SDarrick J. Wong 446b1991ee3SDarrick J. Wong /* Record a regular directory entry for later reinsertion. */ 447b1991ee3SDarrick J. Wong STATIC int 448b1991ee3SDarrick J. Wong xrep_dir_salvage_data_entry( 449b1991ee3SDarrick J. Wong struct xrep_dir *rd, 450b1991ee3SDarrick J. Wong struct xfs_dir2_data_entry *dep) 451b1991ee3SDarrick J. Wong { 452b1991ee3SDarrick J. Wong xfs_ino_t ino; 453b1991ee3SDarrick J. Wong 454b1991ee3SDarrick J. Wong ino = be64_to_cpu(dep->inumber); 455b1991ee3SDarrick J. Wong if (!xrep_dir_want_salvage(rd, dep->name, dep->namelen, ino)) 456b1991ee3SDarrick J. Wong return 0; 457b1991ee3SDarrick J. Wong 458b1991ee3SDarrick J. Wong return xrep_dir_salvage_entry(rd, dep->name, dep->namelen, ino); 459b1991ee3SDarrick J. Wong } 460b1991ee3SDarrick J. Wong 461b1991ee3SDarrick J. Wong /* Try to recover block/data format directory entries. */ 462b1991ee3SDarrick J. Wong STATIC int 463b1991ee3SDarrick J. Wong xrep_dir_recover_data( 464b1991ee3SDarrick J. Wong struct xrep_dir *rd, 465b1991ee3SDarrick J. Wong struct xfs_buf *bp) 466b1991ee3SDarrick J. Wong { 467b1991ee3SDarrick J. Wong struct xfs_da_geometry *geo = rd->sc->mp->m_dir_geo; 468b1991ee3SDarrick J. Wong unsigned int offset; 469b1991ee3SDarrick J. Wong unsigned int end; 470b1991ee3SDarrick J. Wong int error = 0; 471b1991ee3SDarrick J. Wong 472b1991ee3SDarrick J. Wong /* 473b1991ee3SDarrick J. Wong * Loop over the data portion of the block. 474b1991ee3SDarrick J. Wong * Each object is a real entry (dep) or an unused one (dup). 475b1991ee3SDarrick J. Wong */ 476b1991ee3SDarrick J. Wong offset = geo->data_entry_offset; 477b1991ee3SDarrick J. Wong end = min_t(unsigned int, BBTOB(bp->b_length), 478b1991ee3SDarrick J. Wong xfs_dir3_data_end_offset(geo, bp->b_addr)); 479b1991ee3SDarrick J. Wong 480b1991ee3SDarrick J. Wong while (offset < end) { 481b1991ee3SDarrick J. Wong struct xfs_dir2_data_unused *dup = bp->b_addr + offset; 482b1991ee3SDarrick J. Wong struct xfs_dir2_data_entry *dep = bp->b_addr + offset; 483b1991ee3SDarrick J. Wong 484b1991ee3SDarrick J. Wong if (xchk_should_terminate(rd->sc, &error)) 485b1991ee3SDarrick J. Wong return error; 486b1991ee3SDarrick J. Wong 487b1991ee3SDarrick J. Wong /* Skip unused entries. */ 488b1991ee3SDarrick J. Wong if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 489b1991ee3SDarrick J. Wong offset += be16_to_cpu(dup->length); 490b1991ee3SDarrick J. Wong continue; 491b1991ee3SDarrick J. Wong } 492b1991ee3SDarrick J. Wong 493b1991ee3SDarrick J. Wong /* Don't walk off the end of the block. */ 494b1991ee3SDarrick J. Wong offset += xfs_dir2_data_entsize(rd->sc->mp, dep->namelen); 495b1991ee3SDarrick J. Wong if (offset > end) 496b1991ee3SDarrick J. Wong break; 497b1991ee3SDarrick J. Wong 498b1991ee3SDarrick J. Wong /* Ok, let's save this entry. */ 499b1991ee3SDarrick J. Wong error = xrep_dir_salvage_data_entry(rd, dep); 500b1991ee3SDarrick J. Wong if (error) 501b1991ee3SDarrick J. Wong return error; 502b1991ee3SDarrick J. Wong 503b1991ee3SDarrick J. Wong } 504b1991ee3SDarrick J. Wong 505b1991ee3SDarrick J. Wong return 0; 506b1991ee3SDarrick J. Wong } 507b1991ee3SDarrick J. Wong 508b1991ee3SDarrick J. Wong /* Try to recover shortform directory entries. */ 509b1991ee3SDarrick J. Wong STATIC int 510b1991ee3SDarrick J. Wong xrep_dir_recover_sf( 511b1991ee3SDarrick J. Wong struct xrep_dir *rd) 512b1991ee3SDarrick J. Wong { 513b1991ee3SDarrick J. Wong struct xfs_dir2_sf_hdr *hdr; 514b1991ee3SDarrick J. Wong struct xfs_dir2_sf_entry *sfep; 515b1991ee3SDarrick J. Wong struct xfs_dir2_sf_entry *next; 516b1991ee3SDarrick J. Wong struct xfs_ifork *ifp; 517b1991ee3SDarrick J. Wong xfs_ino_t ino; 518b1991ee3SDarrick J. Wong unsigned char *end; 519b1991ee3SDarrick J. Wong int error = 0; 520b1991ee3SDarrick J. Wong 521b1991ee3SDarrick J. Wong ifp = xfs_ifork_ptr(rd->sc->ip, XFS_DATA_FORK); 522b1991ee3SDarrick J. Wong hdr = ifp->if_data; 523b1991ee3SDarrick J. Wong end = (unsigned char *)ifp->if_data + ifp->if_bytes; 524b1991ee3SDarrick J. Wong 525b1991ee3SDarrick J. Wong ino = xfs_dir2_sf_get_parent_ino(hdr); 526b1991ee3SDarrick J. Wong trace_xrep_dir_salvaged_parent(rd->sc->ip, ino); 527b1991ee3SDarrick J. Wong 528b1991ee3SDarrick J. Wong sfep = xfs_dir2_sf_firstentry(hdr); 529b1991ee3SDarrick J. Wong while ((unsigned char *)sfep < end) { 530b1991ee3SDarrick J. Wong if (xchk_should_terminate(rd->sc, &error)) 531b1991ee3SDarrick J. Wong return error; 532b1991ee3SDarrick J. Wong 533b1991ee3SDarrick J. Wong next = xfs_dir2_sf_nextentry(rd->sc->mp, hdr, sfep); 534b1991ee3SDarrick J. Wong if ((unsigned char *)next > end) 535b1991ee3SDarrick J. Wong break; 536b1991ee3SDarrick J. Wong 537b1991ee3SDarrick J. Wong /* Ok, let's save this entry. */ 538b1991ee3SDarrick J. Wong error = xrep_dir_salvage_sf_entry(rd, hdr, sfep); 539b1991ee3SDarrick J. Wong if (error) 540b1991ee3SDarrick J. Wong return error; 541b1991ee3SDarrick J. Wong 542b1991ee3SDarrick J. Wong sfep = next; 543b1991ee3SDarrick J. Wong } 544b1991ee3SDarrick J. Wong 545b1991ee3SDarrick J. Wong return 0; 546b1991ee3SDarrick J. Wong } 547b1991ee3SDarrick J. Wong 548b1991ee3SDarrick J. Wong /* 549b1991ee3SDarrick J. Wong * Try to figure out the format of this directory from the data fork mappings 550b1991ee3SDarrick J. Wong * and the directory size. If we can be reasonably sure of format, we can be 551b1991ee3SDarrick J. Wong * more aggressive in salvaging directory entries. On return, @magic_guess 552b1991ee3SDarrick J. Wong * will be set to DIR3_BLOCK_MAGIC if we think this is a "block format" 553b1991ee3SDarrick J. Wong * directory; DIR3_DATA_MAGIC if we think this is a "data format" directory, 554b1991ee3SDarrick J. Wong * and 0 if we can't tell. 555b1991ee3SDarrick J. Wong */ 556b1991ee3SDarrick J. Wong STATIC void 557b1991ee3SDarrick J. Wong xrep_dir_guess_format( 558b1991ee3SDarrick J. Wong struct xrep_dir *rd, 559b1991ee3SDarrick J. Wong __be32 *magic_guess) 560b1991ee3SDarrick J. Wong { 561b1991ee3SDarrick J. Wong struct xfs_inode *dp = rd->sc->ip; 562b1991ee3SDarrick J. Wong struct xfs_mount *mp = rd->sc->mp; 563b1991ee3SDarrick J. Wong struct xfs_da_geometry *geo = mp->m_dir_geo; 564b1991ee3SDarrick J. Wong xfs_fileoff_t last; 565b1991ee3SDarrick J. Wong int error; 566b1991ee3SDarrick J. Wong 567b1991ee3SDarrick J. Wong ASSERT(xfs_has_crc(mp)); 568b1991ee3SDarrick J. Wong 569b1991ee3SDarrick J. Wong *magic_guess = 0; 570b1991ee3SDarrick J. Wong 571b1991ee3SDarrick J. Wong /* 572b1991ee3SDarrick J. Wong * If there's a single directory block and the directory size is 573b1991ee3SDarrick J. Wong * exactly one block, this has to be a single block format directory. 574b1991ee3SDarrick J. Wong */ 575b1991ee3SDarrick J. Wong error = xfs_bmap_last_offset(dp, &last, XFS_DATA_FORK); 576b1991ee3SDarrick J. Wong if (!error && XFS_FSB_TO_B(mp, last) == geo->blksize && 577b1991ee3SDarrick J. Wong dp->i_disk_size == geo->blksize) { 578b1991ee3SDarrick J. Wong *magic_guess = cpu_to_be32(XFS_DIR3_BLOCK_MAGIC); 579b1991ee3SDarrick J. Wong return; 580b1991ee3SDarrick J. Wong } 581b1991ee3SDarrick J. Wong 582b1991ee3SDarrick J. Wong /* 583b1991ee3SDarrick J. Wong * If the last extent before the leaf offset matches the directory 584b1991ee3SDarrick J. Wong * size and the directory size is larger than 1 block, this is a 585b1991ee3SDarrick J. Wong * data format directory. 586b1991ee3SDarrick J. Wong */ 587b1991ee3SDarrick J. Wong last = geo->leafblk; 588b1991ee3SDarrick J. Wong error = xfs_bmap_last_before(rd->sc->tp, dp, &last, XFS_DATA_FORK); 589b1991ee3SDarrick J. Wong if (!error && 590b1991ee3SDarrick J. Wong XFS_FSB_TO_B(mp, last) > geo->blksize && 591b1991ee3SDarrick J. Wong XFS_FSB_TO_B(mp, last) == dp->i_disk_size) { 592b1991ee3SDarrick J. Wong *magic_guess = cpu_to_be32(XFS_DIR3_DATA_MAGIC); 593b1991ee3SDarrick J. Wong return; 594b1991ee3SDarrick J. Wong } 595b1991ee3SDarrick J. Wong } 596b1991ee3SDarrick J. Wong 597b1991ee3SDarrick J. Wong /* Recover directory entries from a specific directory block. */ 598b1991ee3SDarrick J. Wong STATIC int 599b1991ee3SDarrick J. Wong xrep_dir_recover_dirblock( 600b1991ee3SDarrick J. Wong struct xrep_dir *rd, 601b1991ee3SDarrick J. Wong __be32 magic_guess, 602b1991ee3SDarrick J. Wong xfs_dablk_t dabno) 603b1991ee3SDarrick J. Wong { 604b1991ee3SDarrick J. Wong struct xfs_dir2_data_hdr *hdr; 605b1991ee3SDarrick J. Wong struct xfs_buf *bp; 606b1991ee3SDarrick J. Wong __be32 oldmagic; 607b1991ee3SDarrick J. Wong int error; 608b1991ee3SDarrick J. Wong 609b1991ee3SDarrick J. Wong /* 610b1991ee3SDarrick J. Wong * Try to read buffer. We invalidate them in the next step so we don't 611b1991ee3SDarrick J. Wong * bother to set a buffer type or ops. 612b1991ee3SDarrick J. Wong */ 613b1991ee3SDarrick J. Wong error = xfs_da_read_buf(rd->sc->tp, rd->sc->ip, dabno, 614b1991ee3SDarrick J. Wong XFS_DABUF_MAP_HOLE_OK, &bp, XFS_DATA_FORK, NULL); 615b1991ee3SDarrick J. Wong if (error || !bp) 616b1991ee3SDarrick J. Wong return error; 617b1991ee3SDarrick J. Wong 618b1991ee3SDarrick J. Wong hdr = bp->b_addr; 619b1991ee3SDarrick J. Wong oldmagic = hdr->magic; 620b1991ee3SDarrick J. Wong 621b1991ee3SDarrick J. Wong trace_xrep_dir_recover_dirblock(rd->sc->ip, dabno, 622b1991ee3SDarrick J. Wong be32_to_cpu(hdr->magic), be32_to_cpu(magic_guess)); 623b1991ee3SDarrick J. Wong 624b1991ee3SDarrick J. Wong /* 625b1991ee3SDarrick J. Wong * If we're sure of the block's format, proceed with the salvage 626b1991ee3SDarrick J. Wong * operation using the specified magic number. 627b1991ee3SDarrick J. Wong */ 628b1991ee3SDarrick J. Wong if (magic_guess) { 629b1991ee3SDarrick J. Wong hdr->magic = magic_guess; 630b1991ee3SDarrick J. Wong goto recover; 631b1991ee3SDarrick J. Wong } 632b1991ee3SDarrick J. Wong 633b1991ee3SDarrick J. Wong /* 634b1991ee3SDarrick J. Wong * If we couldn't guess what type of directory this is, then we will 635b1991ee3SDarrick J. Wong * only salvage entries from directory blocks that match the magic 636b1991ee3SDarrick J. Wong * number and pass verifiers. 637b1991ee3SDarrick J. Wong */ 638b1991ee3SDarrick J. Wong switch (hdr->magic) { 639b1991ee3SDarrick J. Wong case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC): 640b1991ee3SDarrick J. Wong case cpu_to_be32(XFS_DIR3_BLOCK_MAGIC): 641b1991ee3SDarrick J. Wong if (!xrep_buf_verify_struct(bp, &xfs_dir3_block_buf_ops)) 642b1991ee3SDarrick J. Wong goto out; 643b1991ee3SDarrick J. Wong if (xfs_dir3_block_header_check(bp, rd->sc->ip->i_ino) != NULL) 644b1991ee3SDarrick J. Wong goto out; 645b1991ee3SDarrick J. Wong break; 646b1991ee3SDarrick J. Wong case cpu_to_be32(XFS_DIR2_DATA_MAGIC): 647b1991ee3SDarrick J. Wong case cpu_to_be32(XFS_DIR3_DATA_MAGIC): 648b1991ee3SDarrick J. Wong if (!xrep_buf_verify_struct(bp, &xfs_dir3_data_buf_ops)) 649b1991ee3SDarrick J. Wong goto out; 650b1991ee3SDarrick J. Wong if (xfs_dir3_data_header_check(bp, rd->sc->ip->i_ino) != NULL) 651b1991ee3SDarrick J. Wong goto out; 652b1991ee3SDarrick J. Wong break; 653b1991ee3SDarrick J. Wong default: 654b1991ee3SDarrick J. Wong goto out; 655b1991ee3SDarrick J. Wong } 656b1991ee3SDarrick J. Wong 657b1991ee3SDarrick J. Wong recover: 658b1991ee3SDarrick J. Wong error = xrep_dir_recover_data(rd, bp); 659b1991ee3SDarrick J. Wong 660b1991ee3SDarrick J. Wong out: 661b1991ee3SDarrick J. Wong hdr->magic = oldmagic; 662b1991ee3SDarrick J. Wong xfs_trans_brelse(rd->sc->tp, bp); 663b1991ee3SDarrick J. Wong return error; 664b1991ee3SDarrick J. Wong } 665b1991ee3SDarrick J. Wong 666b1991ee3SDarrick J. Wong static inline void 667b1991ee3SDarrick J. Wong xrep_dir_init_args( 668b1991ee3SDarrick J. Wong struct xrep_dir *rd, 669b1991ee3SDarrick J. Wong struct xfs_inode *dp, 670b1991ee3SDarrick J. Wong const struct xfs_name *name) 671b1991ee3SDarrick J. Wong { 672b1991ee3SDarrick J. Wong memset(&rd->args, 0, sizeof(struct xfs_da_args)); 673b1991ee3SDarrick J. Wong rd->args.geo = rd->sc->mp->m_dir_geo; 674b1991ee3SDarrick J. Wong rd->args.whichfork = XFS_DATA_FORK; 675b1991ee3SDarrick J. Wong rd->args.owner = rd->sc->ip->i_ino; 676b1991ee3SDarrick J. Wong rd->args.trans = rd->sc->tp; 677b1991ee3SDarrick J. Wong rd->args.dp = dp; 678b1991ee3SDarrick J. Wong if (!name) 679b1991ee3SDarrick J. Wong return; 680b1991ee3SDarrick J. Wong rd->args.name = name->name; 681b1991ee3SDarrick J. Wong rd->args.namelen = name->len; 682b1991ee3SDarrick J. Wong rd->args.filetype = name->type; 683b1991ee3SDarrick J. Wong rd->args.hashval = xfs_dir2_hashname(rd->sc->mp, name); 684b1991ee3SDarrick J. Wong } 685b1991ee3SDarrick J. Wong 686b1991ee3SDarrick J. Wong /* Replay a stashed createname into the temporary directory. */ 687b1991ee3SDarrick J. Wong STATIC int 688b1991ee3SDarrick J. Wong xrep_dir_replay_createname( 689b1991ee3SDarrick J. Wong struct xrep_dir *rd, 690b1991ee3SDarrick J. Wong const struct xfs_name *name, 691b1991ee3SDarrick J. Wong xfs_ino_t inum, 692b1991ee3SDarrick J. Wong xfs_extlen_t total) 693b1991ee3SDarrick J. Wong { 694b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 695b1991ee3SDarrick J. Wong struct xfs_inode *dp = rd->sc->tempip; 696b1991ee3SDarrick J. Wong int error; 697b1991ee3SDarrick J. Wong 698b1991ee3SDarrick J. Wong ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 699b1991ee3SDarrick J. Wong 700b1991ee3SDarrick J. Wong error = xfs_dir_ino_validate(sc->mp, inum); 701b1991ee3SDarrick J. Wong if (error) 702b1991ee3SDarrick J. Wong return error; 703b1991ee3SDarrick J. Wong 704b1991ee3SDarrick J. Wong trace_xrep_dir_replay_createname(dp, name, inum); 705b1991ee3SDarrick J. Wong 706b1991ee3SDarrick J. Wong xrep_dir_init_args(rd, dp, name); 707b1991ee3SDarrick J. Wong rd->args.inumber = inum; 708b1991ee3SDarrick J. Wong rd->args.total = total; 709b1991ee3SDarrick J. Wong rd->args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT; 7104d893a40SChristoph Hellwig return xfs_dir_createname_args(&rd->args); 711b1991ee3SDarrick J. Wong } 712b1991ee3SDarrick J. Wong 7138559b21aSDarrick J. Wong /* Replay a stashed removename onto the temporary directory. */ 7148559b21aSDarrick J. Wong STATIC int 7158559b21aSDarrick J. Wong xrep_dir_replay_removename( 7168559b21aSDarrick J. Wong struct xrep_dir *rd, 7178559b21aSDarrick J. Wong const struct xfs_name *name, 7188559b21aSDarrick J. Wong xfs_extlen_t total) 7198559b21aSDarrick J. Wong { 7208559b21aSDarrick J. Wong struct xfs_inode *dp = rd->args.dp; 7218559b21aSDarrick J. Wong 7228559b21aSDarrick J. Wong ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 7238559b21aSDarrick J. Wong 7248559b21aSDarrick J. Wong xrep_dir_init_args(rd, dp, name); 7258559b21aSDarrick J. Wong rd->args.op_flags = 0; 7268559b21aSDarrick J. Wong rd->args.total = total; 7278559b21aSDarrick J. Wong 7288559b21aSDarrick J. Wong trace_xrep_dir_replay_removename(dp, name, 0); 7293866e6e6SChristoph Hellwig return xfs_dir_removename_args(&rd->args); 7308559b21aSDarrick J. Wong } 7318559b21aSDarrick J. Wong 732b1991ee3SDarrick J. Wong /* 733b1991ee3SDarrick J. Wong * Add this stashed incore directory entry to the temporary directory. 734b1991ee3SDarrick J. Wong * The caller must hold the tempdir's IOLOCK, must not hold any ILOCKs, and 735b1991ee3SDarrick J. Wong * must not be in transaction context. 736b1991ee3SDarrick J. Wong */ 737b1991ee3SDarrick J. Wong STATIC int 738b1991ee3SDarrick J. Wong xrep_dir_replay_update( 739b1991ee3SDarrick J. Wong struct xrep_dir *rd, 740b1991ee3SDarrick J. Wong const struct xfs_name *xname, 741b1991ee3SDarrick J. Wong const struct xrep_dirent *dirent) 742b1991ee3SDarrick J. Wong { 743b1991ee3SDarrick J. Wong struct xfs_mount *mp = rd->sc->mp; 744b1991ee3SDarrick J. Wong #ifdef DEBUG 745b1991ee3SDarrick J. Wong xfs_ino_t ino; 746b1991ee3SDarrick J. Wong #endif 747b1991ee3SDarrick J. Wong uint resblks; 748b1991ee3SDarrick J. Wong int error; 749b1991ee3SDarrick J. Wong 750f1097be2SAllison Henderson resblks = xfs_link_space_res(mp, xname->len); 751b1991ee3SDarrick J. Wong error = xchk_trans_alloc(rd->sc, resblks); 752b1991ee3SDarrick J. Wong if (error) 753b1991ee3SDarrick J. Wong return error; 754b1991ee3SDarrick J. Wong 755b1991ee3SDarrick J. Wong /* Lock the temporary directory and join it to the transaction */ 756b1991ee3SDarrick J. Wong xrep_tempfile_ilock(rd->sc); 757b1991ee3SDarrick J. Wong xfs_trans_ijoin(rd->sc->tp, rd->sc->tempip, 0); 758b1991ee3SDarrick J. Wong 7598559b21aSDarrick J. Wong switch (dirent->action) { 7608559b21aSDarrick J. Wong case XREP_DIRENT_ADD: 761b1991ee3SDarrick J. Wong /* 7628559b21aSDarrick J. Wong * Create a replacement dirent in the temporary directory. 7638559b21aSDarrick J. Wong * Note that _createname doesn't check for existing entries. 7648559b21aSDarrick J. Wong * There shouldn't be any in the temporary dir, but we'll 7658559b21aSDarrick J. Wong * verify this in debug mode. 766b1991ee3SDarrick J. Wong */ 767b1991ee3SDarrick J. Wong #ifdef DEBUG 768b1991ee3SDarrick J. Wong error = xchk_dir_lookup(rd->sc, rd->sc->tempip, xname, &ino); 769b1991ee3SDarrick J. Wong if (error != -ENOENT) { 770b1991ee3SDarrick J. Wong ASSERT(error != -ENOENT); 771b1991ee3SDarrick J. Wong goto out_cancel; 772b1991ee3SDarrick J. Wong } 773b1991ee3SDarrick J. Wong #endif 774b1991ee3SDarrick J. Wong 7758559b21aSDarrick J. Wong error = xrep_dir_replay_createname(rd, xname, dirent->ino, 7768559b21aSDarrick J. Wong resblks); 777b1991ee3SDarrick J. Wong if (error) 778b1991ee3SDarrick J. Wong goto out_cancel; 779b1991ee3SDarrick J. Wong 780b1991ee3SDarrick J. Wong if (xname->type == XFS_DIR3_FT_DIR) 781b1991ee3SDarrick J. Wong rd->subdirs++; 782b1991ee3SDarrick J. Wong rd->dirents++; 7838559b21aSDarrick J. Wong break; 7848559b21aSDarrick J. Wong case XREP_DIRENT_REMOVE: 7858559b21aSDarrick J. Wong /* 7868559b21aSDarrick J. Wong * Remove a dirent from the temporary directory. Note that 7878559b21aSDarrick J. Wong * _removename doesn't check the inode target of the exist 7888559b21aSDarrick J. Wong * entry. There should be a perfect match in the temporary 7898559b21aSDarrick J. Wong * dir, but we'll verify this in debug mode. 7908559b21aSDarrick J. Wong */ 7918559b21aSDarrick J. Wong #ifdef DEBUG 7928559b21aSDarrick J. Wong error = xchk_dir_lookup(rd->sc, rd->sc->tempip, xname, &ino); 7938559b21aSDarrick J. Wong if (error) { 7948559b21aSDarrick J. Wong ASSERT(error != 0); 7958559b21aSDarrick J. Wong goto out_cancel; 7968559b21aSDarrick J. Wong } 7978559b21aSDarrick J. Wong if (ino != dirent->ino) { 7988559b21aSDarrick J. Wong ASSERT(ino == dirent->ino); 7998559b21aSDarrick J. Wong error = -EIO; 8008559b21aSDarrick J. Wong goto out_cancel; 8018559b21aSDarrick J. Wong } 8028559b21aSDarrick J. Wong #endif 8038559b21aSDarrick J. Wong 8048559b21aSDarrick J. Wong error = xrep_dir_replay_removename(rd, xname, resblks); 8058559b21aSDarrick J. Wong if (error) 8068559b21aSDarrick J. Wong goto out_cancel; 8078559b21aSDarrick J. Wong 8088559b21aSDarrick J. Wong if (xname->type == XFS_DIR3_FT_DIR) 8098559b21aSDarrick J. Wong rd->subdirs--; 8108559b21aSDarrick J. Wong rd->dirents--; 8118559b21aSDarrick J. Wong break; 8128559b21aSDarrick J. Wong default: 8138559b21aSDarrick J. Wong ASSERT(0); 8148559b21aSDarrick J. Wong error = -EIO; 8158559b21aSDarrick J. Wong goto out_cancel; 8168559b21aSDarrick J. Wong } 817b1991ee3SDarrick J. Wong 818b1991ee3SDarrick J. Wong /* Commit and unlock. */ 819b1991ee3SDarrick J. Wong error = xrep_trans_commit(rd->sc); 820b1991ee3SDarrick J. Wong if (error) 821b1991ee3SDarrick J. Wong return error; 822b1991ee3SDarrick J. Wong 823b1991ee3SDarrick J. Wong xrep_tempfile_iunlock(rd->sc); 824b1991ee3SDarrick J. Wong return 0; 825b1991ee3SDarrick J. Wong out_cancel: 826b1991ee3SDarrick J. Wong xchk_trans_cancel(rd->sc); 827b1991ee3SDarrick J. Wong xrep_tempfile_iunlock(rd->sc); 828b1991ee3SDarrick J. Wong return error; 829b1991ee3SDarrick J. Wong } 830b1991ee3SDarrick J. Wong 831b1991ee3SDarrick J. Wong /* 832b1991ee3SDarrick J. Wong * Flush stashed incore dirent updates that have been recorded by the scanner. 833b1991ee3SDarrick J. Wong * This is done to reduce the memory requirements of the directory rebuild, 834b1991ee3SDarrick J. Wong * since directories can contain up to 32GB of directory data. 835b1991ee3SDarrick J. Wong * 836b1991ee3SDarrick J. Wong * Caller must not hold transactions or ILOCKs. Caller must hold the tempdir 837b1991ee3SDarrick J. Wong * IOLOCK. 838b1991ee3SDarrick J. Wong */ 839b1991ee3SDarrick J. Wong STATIC int 840b1991ee3SDarrick J. Wong xrep_dir_replay_updates( 841b1991ee3SDarrick J. Wong struct xrep_dir *rd) 842b1991ee3SDarrick J. Wong { 843b1991ee3SDarrick J. Wong xfarray_idx_t array_cur; 844b1991ee3SDarrick J. Wong int error; 845b1991ee3SDarrick J. Wong 846b1991ee3SDarrick J. Wong /* Add all the salvaged dirents to the temporary directory. */ 84776fc23b6SDarrick J. Wong mutex_lock(&rd->pscan.lock); 848b1991ee3SDarrick J. Wong foreach_xfarray_idx(rd->dir_entries, array_cur) { 849b1991ee3SDarrick J. Wong struct xrep_dirent dirent; 850b1991ee3SDarrick J. Wong 851b1991ee3SDarrick J. Wong error = xfarray_load(rd->dir_entries, array_cur, &dirent); 852b1991ee3SDarrick J. Wong if (error) 85376fc23b6SDarrick J. Wong goto out_unlock; 854b1991ee3SDarrick J. Wong 855b1991ee3SDarrick J. Wong error = xfblob_loadname(rd->dir_names, dirent.name_cookie, 856b1991ee3SDarrick J. Wong &rd->xname, dirent.namelen); 857b1991ee3SDarrick J. Wong if (error) 85876fc23b6SDarrick J. Wong goto out_unlock; 859b1991ee3SDarrick J. Wong rd->xname.type = dirent.ftype; 86076fc23b6SDarrick J. Wong mutex_unlock(&rd->pscan.lock); 861b1991ee3SDarrick J. Wong 862b1991ee3SDarrick J. Wong error = xrep_dir_replay_update(rd, &rd->xname, &dirent); 863b1991ee3SDarrick J. Wong if (error) 864b1991ee3SDarrick J. Wong return error; 86576fc23b6SDarrick J. Wong mutex_lock(&rd->pscan.lock); 866b1991ee3SDarrick J. Wong } 867b1991ee3SDarrick J. Wong 868b1991ee3SDarrick J. Wong /* Empty out both arrays now that we've added the entries. */ 869b1991ee3SDarrick J. Wong xfarray_truncate(rd->dir_entries); 870b1991ee3SDarrick J. Wong xfblob_truncate(rd->dir_names); 87176fc23b6SDarrick J. Wong mutex_unlock(&rd->pscan.lock); 872b1991ee3SDarrick J. Wong return 0; 87376fc23b6SDarrick J. Wong out_unlock: 87476fc23b6SDarrick J. Wong mutex_unlock(&rd->pscan.lock); 87576fc23b6SDarrick J. Wong return error; 876b1991ee3SDarrick J. Wong } 877b1991ee3SDarrick J. Wong 878b1991ee3SDarrick J. Wong /* 879b1991ee3SDarrick J. Wong * Periodically flush stashed directory entries to the temporary dir. This 880b1991ee3SDarrick J. Wong * is done to reduce the memory requirements of the directory rebuild, since 881b1991ee3SDarrick J. Wong * directories can contain up to 32GB of directory data. 882b1991ee3SDarrick J. Wong */ 883b1991ee3SDarrick J. Wong STATIC int 884b1991ee3SDarrick J. Wong xrep_dir_flush_stashed( 885b1991ee3SDarrick J. Wong struct xrep_dir *rd) 886b1991ee3SDarrick J. Wong { 887b1991ee3SDarrick J. Wong int error; 888b1991ee3SDarrick J. Wong 889b1991ee3SDarrick J. Wong /* 890b1991ee3SDarrick J. Wong * Entering this function, the scrub context has a reference to the 891b1991ee3SDarrick J. Wong * inode being repaired, the temporary file, and a scrub transaction 892b1991ee3SDarrick J. Wong * that we use during dirent salvaging to avoid livelocking if there 893b1991ee3SDarrick J. Wong * are cycles in the directory structures. We hold ILOCK_EXCL on both 894b1991ee3SDarrick J. Wong * the inode being repaired and the temporary file, though they are 895b1991ee3SDarrick J. Wong * not ijoined to the scrub transaction. 896b1991ee3SDarrick J. Wong * 897b1991ee3SDarrick J. Wong * To constrain kernel memory use, we occasionally write salvaged 898b1991ee3SDarrick J. Wong * dirents from the xfarray and xfblob structures into the temporary 899b1991ee3SDarrick J. Wong * directory in preparation for exchanging the directory structures at 900b1991ee3SDarrick J. Wong * the end. Updating the temporary file requires a transaction, so we 901b1991ee3SDarrick J. Wong * commit the scrub transaction and drop the two ILOCKs so that 902b1991ee3SDarrick J. Wong * we can allocate whatever transaction we want. 903b1991ee3SDarrick J. Wong * 904b1991ee3SDarrick J. Wong * We still hold IOLOCK_EXCL on the inode being repaired, which 905b1991ee3SDarrick J. Wong * prevents anyone from accessing the damaged directory data while we 906b1991ee3SDarrick J. Wong * repair it. 907b1991ee3SDarrick J. Wong */ 908b1991ee3SDarrick J. Wong error = xrep_trans_commit(rd->sc); 909b1991ee3SDarrick J. Wong if (error) 910b1991ee3SDarrick J. Wong return error; 911b1991ee3SDarrick J. Wong xchk_iunlock(rd->sc, XFS_ILOCK_EXCL); 912b1991ee3SDarrick J. Wong 913b1991ee3SDarrick J. Wong /* 914b1991ee3SDarrick J. Wong * Take the IOLOCK of the temporary file while we modify dirents. This 915b1991ee3SDarrick J. Wong * isn't strictly required because the temporary file is never revealed 916b1991ee3SDarrick J. Wong * to userspace, but we follow the same locking rules. We still hold 917b1991ee3SDarrick J. Wong * sc->ip's IOLOCK. 918b1991ee3SDarrick J. Wong */ 919b1991ee3SDarrick J. Wong error = xrep_tempfile_iolock_polled(rd->sc); 920b1991ee3SDarrick J. Wong if (error) 921b1991ee3SDarrick J. Wong return error; 922b1991ee3SDarrick J. Wong 923b1991ee3SDarrick J. Wong /* Write to the tempdir all the updates that we've stashed. */ 924b1991ee3SDarrick J. Wong error = xrep_dir_replay_updates(rd); 925b1991ee3SDarrick J. Wong xrep_tempfile_iounlock(rd->sc); 926b1991ee3SDarrick J. Wong if (error) 927b1991ee3SDarrick J. Wong return error; 928b1991ee3SDarrick J. Wong 929b1991ee3SDarrick J. Wong /* 930b1991ee3SDarrick J. Wong * Recreate the salvage transaction and relock the dir we're salvaging. 931b1991ee3SDarrick J. Wong */ 932b1991ee3SDarrick J. Wong error = xchk_trans_alloc(rd->sc, 0); 933b1991ee3SDarrick J. Wong if (error) 934b1991ee3SDarrick J. Wong return error; 935b1991ee3SDarrick J. Wong xchk_ilock(rd->sc, XFS_ILOCK_EXCL); 936b1991ee3SDarrick J. Wong return 0; 937b1991ee3SDarrick J. Wong } 938b1991ee3SDarrick J. Wong 939b1991ee3SDarrick J. Wong /* Decide if we've stashed too much dirent data in memory. */ 940b1991ee3SDarrick J. Wong static inline bool 941b1991ee3SDarrick J. Wong xrep_dir_want_flush_stashed( 942b1991ee3SDarrick J. Wong struct xrep_dir *rd) 943b1991ee3SDarrick J. Wong { 944b1991ee3SDarrick J. Wong unsigned long long bytes; 945b1991ee3SDarrick J. Wong 946b1991ee3SDarrick J. Wong bytes = xfarray_bytes(rd->dir_entries) + xfblob_bytes(rd->dir_names); 947b1991ee3SDarrick J. Wong return bytes > XREP_DIR_MAX_STASH_BYTES; 948b1991ee3SDarrick J. Wong } 949b1991ee3SDarrick J. Wong 950b1991ee3SDarrick J. Wong /* Extract as many directory entries as we can. */ 951b1991ee3SDarrick J. Wong STATIC int 952b1991ee3SDarrick J. Wong xrep_dir_recover( 953b1991ee3SDarrick J. Wong struct xrep_dir *rd) 954b1991ee3SDarrick J. Wong { 955b1991ee3SDarrick J. Wong struct xfs_bmbt_irec got; 956b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 957b1991ee3SDarrick J. Wong struct xfs_da_geometry *geo = sc->mp->m_dir_geo; 958b1991ee3SDarrick J. Wong xfs_fileoff_t offset; 959b1991ee3SDarrick J. Wong xfs_dablk_t dabno; 960b1991ee3SDarrick J. Wong __be32 magic_guess; 961b1991ee3SDarrick J. Wong int nmap; 962b1991ee3SDarrick J. Wong int error; 963b1991ee3SDarrick J. Wong 964b1991ee3SDarrick J. Wong xrep_dir_guess_format(rd, &magic_guess); 965b1991ee3SDarrick J. Wong 966b1991ee3SDarrick J. Wong /* Iterate each directory data block in the data fork. */ 967b1991ee3SDarrick J. Wong for (offset = 0; 968b1991ee3SDarrick J. Wong offset < geo->leafblk; 969b1991ee3SDarrick J. Wong offset = got.br_startoff + got.br_blockcount) { 970b1991ee3SDarrick J. Wong nmap = 1; 971b1991ee3SDarrick J. Wong error = xfs_bmapi_read(sc->ip, offset, geo->leafblk - offset, 972b1991ee3SDarrick J. Wong &got, &nmap, 0); 973b1991ee3SDarrick J. Wong if (error) 974b1991ee3SDarrick J. Wong return error; 975b1991ee3SDarrick J. Wong if (nmap != 1) 976b1991ee3SDarrick J. Wong return -EFSCORRUPTED; 977b1991ee3SDarrick J. Wong if (!xfs_bmap_is_written_extent(&got)) 978b1991ee3SDarrick J. Wong continue; 979b1991ee3SDarrick J. Wong 980b1991ee3SDarrick J. Wong for (dabno = round_up(got.br_startoff, geo->fsbcount); 981b1991ee3SDarrick J. Wong dabno < got.br_startoff + got.br_blockcount; 982b1991ee3SDarrick J. Wong dabno += geo->fsbcount) { 983b1991ee3SDarrick J. Wong if (xchk_should_terminate(rd->sc, &error)) 984b1991ee3SDarrick J. Wong return error; 985b1991ee3SDarrick J. Wong 986b1991ee3SDarrick J. Wong error = xrep_dir_recover_dirblock(rd, 987b1991ee3SDarrick J. Wong magic_guess, dabno); 988b1991ee3SDarrick J. Wong if (error) 989b1991ee3SDarrick J. Wong return error; 990b1991ee3SDarrick J. Wong 991b1991ee3SDarrick J. Wong /* Flush dirents to constrain memory usage. */ 992b1991ee3SDarrick J. Wong if (xrep_dir_want_flush_stashed(rd)) { 993b1991ee3SDarrick J. Wong error = xrep_dir_flush_stashed(rd); 994b1991ee3SDarrick J. Wong if (error) 995b1991ee3SDarrick J. Wong return error; 996b1991ee3SDarrick J. Wong } 997b1991ee3SDarrick J. Wong } 998b1991ee3SDarrick J. Wong } 999b1991ee3SDarrick J. Wong 1000b1991ee3SDarrick J. Wong return 0; 1001b1991ee3SDarrick J. Wong } 1002b1991ee3SDarrick J. Wong 1003b1991ee3SDarrick J. Wong /* 1004b1991ee3SDarrick J. Wong * Find all the directory entries for this inode by scraping them out of the 1005b1991ee3SDarrick J. Wong * directory leaf blocks by hand, and flushing them into the temp dir. 1006b1991ee3SDarrick J. Wong */ 1007b1991ee3SDarrick J. Wong STATIC int 1008b1991ee3SDarrick J. Wong xrep_dir_find_entries( 1009b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1010b1991ee3SDarrick J. Wong { 1011b1991ee3SDarrick J. Wong struct xfs_inode *dp = rd->sc->ip; 1012b1991ee3SDarrick J. Wong int error; 1013b1991ee3SDarrick J. Wong 1014b1991ee3SDarrick J. Wong /* 1015b1991ee3SDarrick J. Wong * Salvage directory entries from the old directory, and write them to 1016b1991ee3SDarrick J. Wong * the temporary directory. 1017b1991ee3SDarrick J. Wong */ 1018b1991ee3SDarrick J. Wong if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { 1019b1991ee3SDarrick J. Wong error = xrep_dir_recover_sf(rd); 1020b1991ee3SDarrick J. Wong } else { 1021b1991ee3SDarrick J. Wong error = xfs_iread_extents(rd->sc->tp, dp, XFS_DATA_FORK); 1022b1991ee3SDarrick J. Wong if (error) 1023b1991ee3SDarrick J. Wong return error; 1024b1991ee3SDarrick J. Wong 1025b1991ee3SDarrick J. Wong error = xrep_dir_recover(rd); 1026b1991ee3SDarrick J. Wong } 1027b1991ee3SDarrick J. Wong if (error) 1028b1991ee3SDarrick J. Wong return error; 1029b1991ee3SDarrick J. Wong 1030b1991ee3SDarrick J. Wong return xrep_dir_flush_stashed(rd); 1031b1991ee3SDarrick J. Wong } 1032b1991ee3SDarrick J. Wong 1033b1991ee3SDarrick J. Wong /* Scan all files in the filesystem for dirents. */ 1034b1991ee3SDarrick J. Wong STATIC int 1035b1991ee3SDarrick J. Wong xrep_dir_salvage_entries( 1036b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1037b1991ee3SDarrick J. Wong { 1038b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1039b1991ee3SDarrick J. Wong int error; 1040b1991ee3SDarrick J. Wong 1041b1991ee3SDarrick J. Wong /* 1042b1991ee3SDarrick J. Wong * Drop the ILOCK on this directory so that we can scan for this 1043b1991ee3SDarrick J. Wong * directory's parent. Figure out who is going to be the parent of 1044b1991ee3SDarrick J. Wong * this directory, then retake the ILOCK so that we can salvage 1045b1991ee3SDarrick J. Wong * directory entries. 1046b1991ee3SDarrick J. Wong */ 1047b1991ee3SDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL); 1048b1991ee3SDarrick J. Wong error = xrep_dir_find_parent(rd); 1049b1991ee3SDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL); 1050b1991ee3SDarrick J. Wong if (error) 1051b1991ee3SDarrick J. Wong return error; 1052b1991ee3SDarrick J. Wong 1053b1991ee3SDarrick J. Wong /* 1054b1991ee3SDarrick J. Wong * Collect directory entries by parsing raw leaf blocks to salvage 1055b1991ee3SDarrick J. Wong * whatever we can. When we're done, free the staging memory before 1056b1991ee3SDarrick J. Wong * exchanging the directories to reduce memory usage. 1057b1991ee3SDarrick J. Wong */ 1058b1991ee3SDarrick J. Wong error = xrep_dir_find_entries(rd); 1059b1991ee3SDarrick J. Wong if (error) 1060b1991ee3SDarrick J. Wong return error; 1061b1991ee3SDarrick J. Wong 1062b1991ee3SDarrick J. Wong /* 1063b1991ee3SDarrick J. Wong * Cancel the repair transaction and drop the ILOCK so that we can 1064b1991ee3SDarrick J. Wong * (later) use the atomic mapping exchange functions to compute the 1065b1991ee3SDarrick J. Wong * correct block reservations and re-lock the inodes. 1066b1991ee3SDarrick J. Wong * 1067b1991ee3SDarrick J. Wong * We still hold IOLOCK_EXCL (aka i_rwsem) which will prevent directory 1068b1991ee3SDarrick J. Wong * modifications, but there's nothing to prevent userspace from reading 1069b1991ee3SDarrick J. Wong * the directory until we're ready for the exchange operation. Reads 1070b1991ee3SDarrick J. Wong * will return -EIO without shutting down the fs, so we're ok with 1071b1991ee3SDarrick J. Wong * that. 1072a07b4557SDarrick J. Wong * 1073a07b4557SDarrick J. Wong * The VFS can change dotdot on us, but the findparent scan will keep 1074a07b4557SDarrick J. Wong * our incore parent inode up to date. See the note on locking issues 1075a07b4557SDarrick J. Wong * for more details. 1076b1991ee3SDarrick J. Wong */ 1077b1991ee3SDarrick J. Wong error = xrep_trans_commit(sc); 1078b1991ee3SDarrick J. Wong if (error) 1079b1991ee3SDarrick J. Wong return error; 1080b1991ee3SDarrick J. Wong 1081b1991ee3SDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL); 1082b1991ee3SDarrick J. Wong return 0; 1083b1991ee3SDarrick J. Wong } 1084b1991ee3SDarrick J. Wong 1085b1991ee3SDarrick J. Wong 1086b1991ee3SDarrick J. Wong /* 108776fc23b6SDarrick J. Wong * Examine a parent pointer of a file. If it leads us back to the directory 108876fc23b6SDarrick J. Wong * that we're rebuilding, create an incore dirent from the parent pointer and 108976fc23b6SDarrick J. Wong * stash it. 109076fc23b6SDarrick J. Wong */ 109176fc23b6SDarrick J. Wong STATIC int 109276fc23b6SDarrick J. Wong xrep_dir_scan_pptr( 109376fc23b6SDarrick J. Wong struct xfs_scrub *sc, 109476fc23b6SDarrick J. Wong struct xfs_inode *ip, 109576fc23b6SDarrick J. Wong unsigned int attr_flags, 109676fc23b6SDarrick J. Wong const unsigned char *name, 109776fc23b6SDarrick J. Wong unsigned int namelen, 109876fc23b6SDarrick J. Wong const void *value, 109976fc23b6SDarrick J. Wong unsigned int valuelen, 110076fc23b6SDarrick J. Wong void *priv) 110176fc23b6SDarrick J. Wong { 110276fc23b6SDarrick J. Wong struct xfs_name xname = { 110376fc23b6SDarrick J. Wong .name = name, 110476fc23b6SDarrick J. Wong .len = namelen, 110576fc23b6SDarrick J. Wong .type = xfs_mode_to_ftype(VFS_I(ip)->i_mode), 110676fc23b6SDarrick J. Wong }; 110776fc23b6SDarrick J. Wong xfs_ino_t parent_ino; 110876fc23b6SDarrick J. Wong uint32_t parent_gen; 110976fc23b6SDarrick J. Wong struct xrep_dir *rd = priv; 111076fc23b6SDarrick J. Wong int error; 111176fc23b6SDarrick J. Wong 111276fc23b6SDarrick J. Wong if (!(attr_flags & XFS_ATTR_PARENT)) 111376fc23b6SDarrick J. Wong return 0; 111476fc23b6SDarrick J. Wong 111576fc23b6SDarrick J. Wong /* 111676fc23b6SDarrick J. Wong * Ignore parent pointers that point back to a different dir, list the 111776fc23b6SDarrick J. Wong * wrong generation number, or are invalid. 111876fc23b6SDarrick J. Wong */ 111976fc23b6SDarrick J. Wong error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value, 112076fc23b6SDarrick J. Wong valuelen, &parent_ino, &parent_gen); 112176fc23b6SDarrick J. Wong if (error) 112276fc23b6SDarrick J. Wong return error; 112376fc23b6SDarrick J. Wong 112476fc23b6SDarrick J. Wong if (parent_ino != sc->ip->i_ino || 112576fc23b6SDarrick J. Wong parent_gen != VFS_I(sc->ip)->i_generation) 112676fc23b6SDarrick J. Wong return 0; 112776fc23b6SDarrick J. Wong 112876fc23b6SDarrick J. Wong mutex_lock(&rd->pscan.lock); 112976fc23b6SDarrick J. Wong error = xrep_dir_stash_createname(rd, &xname, ip->i_ino); 113076fc23b6SDarrick J. Wong mutex_unlock(&rd->pscan.lock); 113176fc23b6SDarrick J. Wong return error; 113276fc23b6SDarrick J. Wong } 113376fc23b6SDarrick J. Wong 113476fc23b6SDarrick J. Wong /* 113576fc23b6SDarrick J. Wong * If this child dirent points to the directory being repaired, remember that 113676fc23b6SDarrick J. Wong * fact so that we can reset the dotdot entry if necessary. 113776fc23b6SDarrick J. Wong */ 113876fc23b6SDarrick J. Wong STATIC int 113976fc23b6SDarrick J. Wong xrep_dir_scan_dirent( 114076fc23b6SDarrick J. Wong struct xfs_scrub *sc, 114176fc23b6SDarrick J. Wong struct xfs_inode *dp, 114276fc23b6SDarrick J. Wong xfs_dir2_dataptr_t dapos, 114376fc23b6SDarrick J. Wong const struct xfs_name *name, 114476fc23b6SDarrick J. Wong xfs_ino_t ino, 114576fc23b6SDarrick J. Wong void *priv) 114676fc23b6SDarrick J. Wong { 114776fc23b6SDarrick J. Wong struct xrep_dir *rd = priv; 114876fc23b6SDarrick J. Wong 114976fc23b6SDarrick J. Wong /* Dirent doesn't point to this directory. */ 115076fc23b6SDarrick J. Wong if (ino != rd->sc->ip->i_ino) 115176fc23b6SDarrick J. Wong return 0; 115276fc23b6SDarrick J. Wong 115376fc23b6SDarrick J. Wong /* Ignore garbage inum. */ 115476fc23b6SDarrick J. Wong if (!xfs_verify_dir_ino(rd->sc->mp, ino)) 115576fc23b6SDarrick J. Wong return 0; 115676fc23b6SDarrick J. Wong 115776fc23b6SDarrick J. Wong /* No weird looking names. */ 115876fc23b6SDarrick J. Wong if (name->len >= MAXNAMELEN || name->len <= 0) 115976fc23b6SDarrick J. Wong return 0; 116076fc23b6SDarrick J. Wong 116176fc23b6SDarrick J. Wong /* Don't pick up dot or dotdot entries; we only want child dirents. */ 116276fc23b6SDarrick J. Wong if (xfs_dir2_samename(name, &xfs_name_dotdot) || 116376fc23b6SDarrick J. Wong xfs_dir2_samename(name, &xfs_name_dot)) 116476fc23b6SDarrick J. Wong return 0; 116576fc23b6SDarrick J. Wong 116676fc23b6SDarrick J. Wong trace_xrep_dir_stash_createname(sc->tempip, &xfs_name_dotdot, 116776fc23b6SDarrick J. Wong dp->i_ino); 116876fc23b6SDarrick J. Wong 116976fc23b6SDarrick J. Wong xrep_findparent_scan_found(&rd->pscan, dp->i_ino); 117076fc23b6SDarrick J. Wong return 0; 117176fc23b6SDarrick J. Wong } 117276fc23b6SDarrick J. Wong 117376fc23b6SDarrick J. Wong /* 117476fc23b6SDarrick J. Wong * Decide if we want to look for child dirents or parent pointers in this file. 117576fc23b6SDarrick J. Wong * Skip the dir being repaired and any files being used to stage repairs. 117676fc23b6SDarrick J. Wong */ 117776fc23b6SDarrick J. Wong static inline bool 117876fc23b6SDarrick J. Wong xrep_dir_want_scan( 117976fc23b6SDarrick J. Wong struct xrep_dir *rd, 118076fc23b6SDarrick J. Wong const struct xfs_inode *ip) 118176fc23b6SDarrick J. Wong { 118276fc23b6SDarrick J. Wong return ip != rd->sc->ip && !xrep_is_tempfile(ip); 118376fc23b6SDarrick J. Wong } 118476fc23b6SDarrick J. Wong 118576fc23b6SDarrick J. Wong /* 118676fc23b6SDarrick J. Wong * Take ILOCK on a file that we want to scan. 118776fc23b6SDarrick J. Wong * 118876fc23b6SDarrick J. Wong * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt or 118976fc23b6SDarrick J. Wong * has an unloaded attr bmbt. Otherwise, take ILOCK_SHARED. 119076fc23b6SDarrick J. Wong */ 119176fc23b6SDarrick J. Wong static inline unsigned int 119276fc23b6SDarrick J. Wong xrep_dir_scan_ilock( 119376fc23b6SDarrick J. Wong struct xrep_dir *rd, 119476fc23b6SDarrick J. Wong struct xfs_inode *ip) 119576fc23b6SDarrick J. Wong { 119676fc23b6SDarrick J. Wong uint lock_mode = XFS_ILOCK_SHARED; 119776fc23b6SDarrick J. Wong 119876fc23b6SDarrick J. Wong /* Need to take the shared ILOCK to advance the iscan cursor. */ 119976fc23b6SDarrick J. Wong if (!xrep_dir_want_scan(rd, ip)) 120076fc23b6SDarrick J. Wong goto lock; 120176fc23b6SDarrick J. Wong 120276fc23b6SDarrick J. Wong if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) { 120376fc23b6SDarrick J. Wong lock_mode = XFS_ILOCK_EXCL; 120476fc23b6SDarrick J. Wong goto lock; 120576fc23b6SDarrick J. Wong } 120676fc23b6SDarrick J. Wong 120776fc23b6SDarrick J. Wong if (xfs_inode_has_attr_fork(ip) && xfs_need_iread_extents(&ip->i_af)) 120876fc23b6SDarrick J. Wong lock_mode = XFS_ILOCK_EXCL; 120976fc23b6SDarrick J. Wong 121076fc23b6SDarrick J. Wong lock: 121176fc23b6SDarrick J. Wong xfs_ilock(ip, lock_mode); 121276fc23b6SDarrick J. Wong return lock_mode; 121376fc23b6SDarrick J. Wong } 121476fc23b6SDarrick J. Wong 121576fc23b6SDarrick J. Wong /* 121676fc23b6SDarrick J. Wong * Scan this file for relevant child dirents or parent pointers that point to 121776fc23b6SDarrick J. Wong * the directory we're rebuilding. 121876fc23b6SDarrick J. Wong */ 121976fc23b6SDarrick J. Wong STATIC int 122076fc23b6SDarrick J. Wong xrep_dir_scan_file( 122176fc23b6SDarrick J. Wong struct xrep_dir *rd, 122276fc23b6SDarrick J. Wong struct xfs_inode *ip) 122376fc23b6SDarrick J. Wong { 122476fc23b6SDarrick J. Wong unsigned int lock_mode; 122576fc23b6SDarrick J. Wong int error = 0; 122676fc23b6SDarrick J. Wong 122776fc23b6SDarrick J. Wong lock_mode = xrep_dir_scan_ilock(rd, ip); 122876fc23b6SDarrick J. Wong 122976fc23b6SDarrick J. Wong if (!xrep_dir_want_scan(rd, ip)) 123076fc23b6SDarrick J. Wong goto scan_done; 123176fc23b6SDarrick J. Wong 123276fc23b6SDarrick J. Wong /* 123376fc23b6SDarrick J. Wong * If the extended attributes look as though they has been zapped by 123476fc23b6SDarrick J. Wong * the inode record repair code, we cannot scan for parent pointers. 123576fc23b6SDarrick J. Wong */ 123676fc23b6SDarrick J. Wong if (xchk_pptr_looks_zapped(ip)) { 123776fc23b6SDarrick J. Wong error = -EBUSY; 123876fc23b6SDarrick J. Wong goto scan_done; 123976fc23b6SDarrick J. Wong } 124076fc23b6SDarrick J. Wong 12416efbbdebSDarrick J. Wong error = xchk_xattr_walk(rd->sc, ip, xrep_dir_scan_pptr, NULL, rd); 124276fc23b6SDarrick J. Wong if (error) 124376fc23b6SDarrick J. Wong goto scan_done; 124476fc23b6SDarrick J. Wong 124576fc23b6SDarrick J. Wong if (S_ISDIR(VFS_I(ip)->i_mode)) { 124676fc23b6SDarrick J. Wong /* 124776fc23b6SDarrick J. Wong * If the directory looks as though it has been zapped by the 124876fc23b6SDarrick J. Wong * inode record repair code, we cannot scan for child dirents. 124976fc23b6SDarrick J. Wong */ 125076fc23b6SDarrick J. Wong if (xchk_dir_looks_zapped(ip)) { 125176fc23b6SDarrick J. Wong error = -EBUSY; 125276fc23b6SDarrick J. Wong goto scan_done; 125376fc23b6SDarrick J. Wong } 125476fc23b6SDarrick J. Wong 125576fc23b6SDarrick J. Wong error = xchk_dir_walk(rd->sc, ip, xrep_dir_scan_dirent, rd); 125676fc23b6SDarrick J. Wong if (error) 125776fc23b6SDarrick J. Wong goto scan_done; 125876fc23b6SDarrick J. Wong } 125976fc23b6SDarrick J. Wong 126076fc23b6SDarrick J. Wong scan_done: 126176fc23b6SDarrick J. Wong xchk_iscan_mark_visited(&rd->pscan.iscan, ip); 126276fc23b6SDarrick J. Wong xfs_iunlock(ip, lock_mode); 126376fc23b6SDarrick J. Wong return error; 126476fc23b6SDarrick J. Wong } 126576fc23b6SDarrick J. Wong 126676fc23b6SDarrick J. Wong /* 126776fc23b6SDarrick J. Wong * Scan all files in the filesystem for parent pointers that we can turn into 126876fc23b6SDarrick J. Wong * replacement dirents, and a dirent that we can use to set the dotdot pointer. 126976fc23b6SDarrick J. Wong */ 127076fc23b6SDarrick J. Wong STATIC int 127176fc23b6SDarrick J. Wong xrep_dir_scan_dirtree( 127276fc23b6SDarrick J. Wong struct xrep_dir *rd) 127376fc23b6SDarrick J. Wong { 127476fc23b6SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 127576fc23b6SDarrick J. Wong struct xfs_inode *ip; 127676fc23b6SDarrick J. Wong int error; 127776fc23b6SDarrick J. Wong 127876fc23b6SDarrick J. Wong /* Roots of directory trees are their own parents. */ 1279679b098bSDarrick J. Wong if (xchk_inode_is_dirtree_root(sc->ip)) 128076fc23b6SDarrick J. Wong xrep_findparent_scan_found(&rd->pscan, sc->ip->i_ino); 128176fc23b6SDarrick J. Wong 128276fc23b6SDarrick J. Wong /* 128376fc23b6SDarrick J. Wong * Filesystem scans are time consuming. Drop the directory ILOCK and 128476fc23b6SDarrick J. Wong * all other resources for the duration of the scan and hope for the 128576fc23b6SDarrick J. Wong * best. The live update hooks will keep our scan information up to 128676fc23b6SDarrick J. Wong * date even though we've dropped the locks. 128776fc23b6SDarrick J. Wong */ 128876fc23b6SDarrick J. Wong xchk_trans_cancel(sc); 128976fc23b6SDarrick J. Wong if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) 129076fc23b6SDarrick J. Wong xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED | 129176fc23b6SDarrick J. Wong XFS_ILOCK_EXCL)); 129276fc23b6SDarrick J. Wong error = xchk_trans_alloc_empty(sc); 129376fc23b6SDarrick J. Wong if (error) 129476fc23b6SDarrick J. Wong return error; 129576fc23b6SDarrick J. Wong 129676fc23b6SDarrick J. Wong while ((error = xchk_iscan_iter(&rd->pscan.iscan, &ip)) == 1) { 129776fc23b6SDarrick J. Wong bool flush; 129876fc23b6SDarrick J. Wong 129976fc23b6SDarrick J. Wong error = xrep_dir_scan_file(rd, ip); 130076fc23b6SDarrick J. Wong xchk_irele(sc, ip); 130176fc23b6SDarrick J. Wong if (error) 130276fc23b6SDarrick J. Wong break; 130376fc23b6SDarrick J. Wong 130476fc23b6SDarrick J. Wong /* Flush stashed dirent updates to constrain memory usage. */ 130576fc23b6SDarrick J. Wong mutex_lock(&rd->pscan.lock); 130676fc23b6SDarrick J. Wong flush = xrep_dir_want_flush_stashed(rd); 130776fc23b6SDarrick J. Wong mutex_unlock(&rd->pscan.lock); 130876fc23b6SDarrick J. Wong if (flush) { 130976fc23b6SDarrick J. Wong xchk_trans_cancel(sc); 131076fc23b6SDarrick J. Wong 131176fc23b6SDarrick J. Wong error = xrep_tempfile_iolock_polled(sc); 131276fc23b6SDarrick J. Wong if (error) 131376fc23b6SDarrick J. Wong break; 131476fc23b6SDarrick J. Wong 131576fc23b6SDarrick J. Wong error = xrep_dir_replay_updates(rd); 131676fc23b6SDarrick J. Wong xrep_tempfile_iounlock(sc); 131776fc23b6SDarrick J. Wong if (error) 131876fc23b6SDarrick J. Wong break; 131976fc23b6SDarrick J. Wong 132076fc23b6SDarrick J. Wong error = xchk_trans_alloc_empty(sc); 132176fc23b6SDarrick J. Wong if (error) 132276fc23b6SDarrick J. Wong break; 132376fc23b6SDarrick J. Wong } 132476fc23b6SDarrick J. Wong 132576fc23b6SDarrick J. Wong if (xchk_should_terminate(sc, &error)) 132676fc23b6SDarrick J. Wong break; 132776fc23b6SDarrick J. Wong } 132876fc23b6SDarrick J. Wong xchk_iscan_iter_finish(&rd->pscan.iscan); 132976fc23b6SDarrick J. Wong if (error) { 133076fc23b6SDarrick J. Wong /* 133176fc23b6SDarrick J. Wong * If we couldn't grab an inode that was busy with a state 133276fc23b6SDarrick J. Wong * change, change the error code so that we exit to userspace 133376fc23b6SDarrick J. Wong * as quickly as possible. 133476fc23b6SDarrick J. Wong */ 133576fc23b6SDarrick J. Wong if (error == -EBUSY) 133676fc23b6SDarrick J. Wong return -ECANCELED; 133776fc23b6SDarrick J. Wong return error; 133876fc23b6SDarrick J. Wong } 133976fc23b6SDarrick J. Wong 134076fc23b6SDarrick J. Wong /* 134176fc23b6SDarrick J. Wong * Cancel the empty transaction so that we can (later) use the atomic 134276fc23b6SDarrick J. Wong * file mapping exchange functions to lock files and commit the new 134376fc23b6SDarrick J. Wong * directory. 134476fc23b6SDarrick J. Wong */ 134576fc23b6SDarrick J. Wong xchk_trans_cancel(rd->sc); 134676fc23b6SDarrick J. Wong return 0; 134776fc23b6SDarrick J. Wong } 134876fc23b6SDarrick J. Wong 134976fc23b6SDarrick J. Wong /* 13508559b21aSDarrick J. Wong * Capture dirent updates being made by other threads which are relevant to the 13518559b21aSDarrick J. Wong * directory being repaired. 13528559b21aSDarrick J. Wong */ 13538559b21aSDarrick J. Wong STATIC int 13548559b21aSDarrick J. Wong xrep_dir_live_update( 13558559b21aSDarrick J. Wong struct notifier_block *nb, 13568559b21aSDarrick J. Wong unsigned long action, 13578559b21aSDarrick J. Wong void *data) 13588559b21aSDarrick J. Wong { 13598559b21aSDarrick J. Wong struct xfs_dir_update_params *p = data; 13608559b21aSDarrick J. Wong struct xrep_dir *rd; 13618559b21aSDarrick J. Wong struct xfs_scrub *sc; 13628559b21aSDarrick J. Wong int error = 0; 13638559b21aSDarrick J. Wong 13648559b21aSDarrick J. Wong rd = container_of(nb, struct xrep_dir, pscan.dhook.dirent_hook.nb); 13658559b21aSDarrick J. Wong sc = rd->sc; 13668559b21aSDarrick J. Wong 13678559b21aSDarrick J. Wong /* 13688559b21aSDarrick J. Wong * This thread updated a child dirent in the directory that we're 13698559b21aSDarrick J. Wong * rebuilding. Stash the update for replay against the temporary 13708559b21aSDarrick J. Wong * directory. 13718559b21aSDarrick J. Wong */ 13728559b21aSDarrick J. Wong if (p->dp->i_ino == sc->ip->i_ino && 13738559b21aSDarrick J. Wong xchk_iscan_want_live_update(&rd->pscan.iscan, p->ip->i_ino)) { 13748559b21aSDarrick J. Wong mutex_lock(&rd->pscan.lock); 13758559b21aSDarrick J. Wong if (p->delta > 0) 13768559b21aSDarrick J. Wong error = xrep_dir_stash_createname(rd, p->name, 13778559b21aSDarrick J. Wong p->ip->i_ino); 13788559b21aSDarrick J. Wong else 13798559b21aSDarrick J. Wong error = xrep_dir_stash_removename(rd, p->name, 13808559b21aSDarrick J. Wong p->ip->i_ino); 13818559b21aSDarrick J. Wong mutex_unlock(&rd->pscan.lock); 13828559b21aSDarrick J. Wong if (error) 13838559b21aSDarrick J. Wong goto out_abort; 13848559b21aSDarrick J. Wong } 13858559b21aSDarrick J. Wong 13868559b21aSDarrick J. Wong /* 13878559b21aSDarrick J. Wong * This thread updated another directory's child dirent that points to 13888559b21aSDarrick J. Wong * the directory that we're rebuilding, so remember the new dotdot 13898559b21aSDarrick J. Wong * target. 13908559b21aSDarrick J. Wong */ 13918559b21aSDarrick J. Wong if (p->ip->i_ino == sc->ip->i_ino && 13928559b21aSDarrick J. Wong xchk_iscan_want_live_update(&rd->pscan.iscan, p->dp->i_ino)) { 13938559b21aSDarrick J. Wong if (p->delta > 0) { 13948559b21aSDarrick J. Wong trace_xrep_dir_stash_createname(sc->tempip, 13958559b21aSDarrick J. Wong &xfs_name_dotdot, 13968559b21aSDarrick J. Wong p->dp->i_ino); 13978559b21aSDarrick J. Wong 13988559b21aSDarrick J. Wong xrep_findparent_scan_found(&rd->pscan, p->dp->i_ino); 13998559b21aSDarrick J. Wong } else { 14008559b21aSDarrick J. Wong trace_xrep_dir_stash_removename(sc->tempip, 14018559b21aSDarrick J. Wong &xfs_name_dotdot, 14028559b21aSDarrick J. Wong rd->pscan.parent_ino); 14038559b21aSDarrick J. Wong 14048559b21aSDarrick J. Wong xrep_findparent_scan_found(&rd->pscan, NULLFSINO); 14058559b21aSDarrick J. Wong } 14068559b21aSDarrick J. Wong } 14078559b21aSDarrick J. Wong 14088559b21aSDarrick J. Wong return NOTIFY_DONE; 14098559b21aSDarrick J. Wong out_abort: 14108559b21aSDarrick J. Wong xchk_iscan_abort(&rd->pscan.iscan); 14118559b21aSDarrick J. Wong return NOTIFY_DONE; 14128559b21aSDarrick J. Wong } 14138559b21aSDarrick J. Wong 14148559b21aSDarrick J. Wong /* 1415b1991ee3SDarrick J. Wong * Free all the directory blocks and reset the data fork. The caller must 1416b1991ee3SDarrick J. Wong * join the inode to the transaction. This function returns with the inode 1417b1991ee3SDarrick J. Wong * joined to a clean scrub transaction. 1418b1991ee3SDarrick J. Wong */ 1419b1991ee3SDarrick J. Wong STATIC int 1420b1991ee3SDarrick J. Wong xrep_dir_reset_fork( 1421b1991ee3SDarrick J. Wong struct xrep_dir *rd, 1422b1991ee3SDarrick J. Wong xfs_ino_t parent_ino) 1423b1991ee3SDarrick J. Wong { 1424b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1425b1991ee3SDarrick J. Wong struct xfs_ifork *ifp = xfs_ifork_ptr(sc->tempip, XFS_DATA_FORK); 1426b1991ee3SDarrick J. Wong int error; 1427b1991ee3SDarrick J. Wong 1428b1991ee3SDarrick J. Wong /* Unmap all the directory buffers. */ 1429b1991ee3SDarrick J. Wong if (xfs_ifork_has_extents(ifp)) { 1430b1991ee3SDarrick J. Wong error = xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK); 1431b1991ee3SDarrick J. Wong if (error) 1432b1991ee3SDarrick J. Wong return error; 1433b1991ee3SDarrick J. Wong } 1434b1991ee3SDarrick J. Wong 1435b1991ee3SDarrick J. Wong trace_xrep_dir_reset_fork(sc->tempip, parent_ino); 1436b1991ee3SDarrick J. Wong 1437b1991ee3SDarrick J. Wong /* Reset the data fork to an empty data fork. */ 1438b1991ee3SDarrick J. Wong xfs_idestroy_fork(ifp); 1439b1991ee3SDarrick J. Wong ifp->if_bytes = 0; 1440b1991ee3SDarrick J. Wong sc->tempip->i_disk_size = 0; 1441b1991ee3SDarrick J. Wong 1442b1991ee3SDarrick J. Wong /* Reinitialize the short form directory. */ 1443b1991ee3SDarrick J. Wong xrep_dir_init_args(rd, sc->tempip, NULL); 1444b1991ee3SDarrick J. Wong return xfs_dir2_sf_create(&rd->args, parent_ino); 1445b1991ee3SDarrick J. Wong } 1446b1991ee3SDarrick J. Wong 1447b1991ee3SDarrick J. Wong /* 1448b1991ee3SDarrick J. Wong * Prepare both inodes' directory forks for exchanging mappings. Promote the 1449b1991ee3SDarrick J. Wong * tempfile from short format to leaf format, and if the file being repaired 1450b1991ee3SDarrick J. Wong * has a short format data fork, turn it into an empty extent list. 1451b1991ee3SDarrick J. Wong */ 1452b1991ee3SDarrick J. Wong STATIC int 1453b1991ee3SDarrick J. Wong xrep_dir_swap_prep( 1454b1991ee3SDarrick J. Wong struct xfs_scrub *sc, 1455b1991ee3SDarrick J. Wong bool temp_local, 1456b1991ee3SDarrick J. Wong bool ip_local) 1457b1991ee3SDarrick J. Wong { 1458b1991ee3SDarrick J. Wong int error; 1459b1991ee3SDarrick J. Wong 1460b1991ee3SDarrick J. Wong /* 1461b1991ee3SDarrick J. Wong * If the tempfile's directory is in shortform format, convert that to 1462b1991ee3SDarrick J. Wong * a single leaf extent so that we can use the atomic mapping exchange. 1463b1991ee3SDarrick J. Wong */ 1464b1991ee3SDarrick J. Wong if (temp_local) { 1465b1991ee3SDarrick J. Wong struct xfs_da_args args = { 1466b1991ee3SDarrick J. Wong .dp = sc->tempip, 1467b1991ee3SDarrick J. Wong .geo = sc->mp->m_dir_geo, 1468b1991ee3SDarrick J. Wong .whichfork = XFS_DATA_FORK, 1469b1991ee3SDarrick J. Wong .trans = sc->tp, 1470b1991ee3SDarrick J. Wong .total = 1, 1471b1991ee3SDarrick J. Wong .owner = sc->ip->i_ino, 1472b1991ee3SDarrick J. Wong }; 1473b1991ee3SDarrick J. Wong 1474b1991ee3SDarrick J. Wong error = xfs_dir2_sf_to_block(&args); 1475b1991ee3SDarrick J. Wong if (error) 1476b1991ee3SDarrick J. Wong return error; 1477b1991ee3SDarrick J. Wong 1478b1991ee3SDarrick J. Wong /* 1479b1991ee3SDarrick J. Wong * Roll the deferred log items to get us back to a clean 1480b1991ee3SDarrick J. Wong * transaction. 1481b1991ee3SDarrick J. Wong */ 1482b1991ee3SDarrick J. Wong error = xfs_defer_finish(&sc->tp); 1483b1991ee3SDarrick J. Wong if (error) 1484b1991ee3SDarrick J. Wong return error; 1485b1991ee3SDarrick J. Wong } 1486b1991ee3SDarrick J. Wong 1487b1991ee3SDarrick J. Wong /* 1488b1991ee3SDarrick J. Wong * If the file being repaired had a shortform data fork, convert that 1489b1991ee3SDarrick J. Wong * to an empty extent list in preparation for the atomic mapping 1490b1991ee3SDarrick J. Wong * exchange. 1491b1991ee3SDarrick J. Wong */ 1492b1991ee3SDarrick J. Wong if (ip_local) { 1493b1991ee3SDarrick J. Wong struct xfs_ifork *ifp; 1494b1991ee3SDarrick J. Wong 1495b1991ee3SDarrick J. Wong ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); 1496b1991ee3SDarrick J. Wong xfs_idestroy_fork(ifp); 1497b1991ee3SDarrick J. Wong ifp->if_format = XFS_DINODE_FMT_EXTENTS; 1498b1991ee3SDarrick J. Wong ifp->if_nextents = 0; 1499b1991ee3SDarrick J. Wong ifp->if_bytes = 0; 1500b1991ee3SDarrick J. Wong ifp->if_data = NULL; 1501b1991ee3SDarrick J. Wong ifp->if_height = 0; 1502b1991ee3SDarrick J. Wong 1503b1991ee3SDarrick J. Wong xfs_trans_log_inode(sc->tp, sc->ip, 1504b1991ee3SDarrick J. Wong XFS_ILOG_CORE | XFS_ILOG_DDATA); 1505b1991ee3SDarrick J. Wong } 1506b1991ee3SDarrick J. Wong 1507b1991ee3SDarrick J. Wong return 0; 1508b1991ee3SDarrick J. Wong } 1509b1991ee3SDarrick J. Wong 1510b1991ee3SDarrick J. Wong /* 1511b1991ee3SDarrick J. Wong * Replace the inode number of a directory entry. 1512b1991ee3SDarrick J. Wong */ 1513b1991ee3SDarrick J. Wong static int 1514b1991ee3SDarrick J. Wong xrep_dir_replace( 1515b1991ee3SDarrick J. Wong struct xrep_dir *rd, 1516b1991ee3SDarrick J. Wong struct xfs_inode *dp, 1517b1991ee3SDarrick J. Wong const struct xfs_name *name, 1518b1991ee3SDarrick J. Wong xfs_ino_t inum, 1519b1991ee3SDarrick J. Wong xfs_extlen_t total) 1520b1991ee3SDarrick J. Wong { 1521b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1522b1991ee3SDarrick J. Wong int error; 1523b1991ee3SDarrick J. Wong 1524b1991ee3SDarrick J. Wong ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 1525b1991ee3SDarrick J. Wong 1526b1991ee3SDarrick J. Wong error = xfs_dir_ino_validate(sc->mp, inum); 1527b1991ee3SDarrick J. Wong if (error) 1528b1991ee3SDarrick J. Wong return error; 1529b1991ee3SDarrick J. Wong 1530b1991ee3SDarrick J. Wong xrep_dir_init_args(rd, dp, name); 1531b1991ee3SDarrick J. Wong rd->args.inumber = inum; 1532b1991ee3SDarrick J. Wong rd->args.total = total; 1533dfe5febeSChristoph Hellwig return xfs_dir_replace_args(&rd->args); 1534b1991ee3SDarrick J. Wong } 1535b1991ee3SDarrick J. Wong 1536b1991ee3SDarrick J. Wong /* 1537b1991ee3SDarrick J. Wong * Reset the link count of this directory and adjust the unlinked list pointers 1538b1991ee3SDarrick J. Wong * as needed. 1539b1991ee3SDarrick J. Wong */ 1540b1991ee3SDarrick J. Wong STATIC int 1541b1991ee3SDarrick J. Wong xrep_dir_set_nlink( 1542b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1543b1991ee3SDarrick J. Wong { 1544b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1545b1991ee3SDarrick J. Wong struct xfs_inode *dp = sc->ip; 1546b1991ee3SDarrick J. Wong struct xfs_perag *pag; 15475f204051SDarrick J. Wong unsigned int new_nlink = min_t(unsigned long long, 15485f204051SDarrick J. Wong rd->subdirs + 2, 15495f204051SDarrick J. Wong XFS_NLINK_PINNED); 1550b1991ee3SDarrick J. Wong int error; 1551b1991ee3SDarrick J. Wong 1552b1991ee3SDarrick J. Wong /* 1553b1991ee3SDarrick J. Wong * The directory is not on the incore unlinked list, which means that 1554b1991ee3SDarrick J. Wong * it needs to be reachable via the directory tree. Update the nlink 15551e58a8ccSDarrick J. Wong * with our observed link count. If the directory has no parent, it 15561e58a8ccSDarrick J. Wong * will be moved to the orphanage. 1557b1991ee3SDarrick J. Wong */ 1558b1991ee3SDarrick J. Wong if (!xfs_inode_on_unlinked_list(dp)) 1559b1991ee3SDarrick J. Wong goto reset_nlink; 1560b1991ee3SDarrick J. Wong 1561b1991ee3SDarrick J. Wong /* 1562b1991ee3SDarrick J. Wong * The directory is on the unlinked list and we did not find any 1563b1991ee3SDarrick J. Wong * dirents. Set the link count to zero and let the directory 1564b1991ee3SDarrick J. Wong * inactivate when the last reference drops. 1565b1991ee3SDarrick J. Wong */ 1566b1991ee3SDarrick J. Wong if (rd->dirents == 0) { 15671e58a8ccSDarrick J. Wong rd->needs_adoption = false; 1568b1991ee3SDarrick J. Wong new_nlink = 0; 1569b1991ee3SDarrick J. Wong goto reset_nlink; 1570b1991ee3SDarrick J. Wong } 1571b1991ee3SDarrick J. Wong 1572b1991ee3SDarrick J. Wong /* 1573b1991ee3SDarrick J. Wong * The directory is on the unlinked list and we found dirents. This 1574b1991ee3SDarrick J. Wong * directory needs to be reachable via the directory tree. Remove the 1575b1991ee3SDarrick J. Wong * dir from the unlinked list and update nlink with the observed link 15761e58a8ccSDarrick J. Wong * count. If the directory has no parent, it will be moved to the 15771e58a8ccSDarrick J. Wong * orphanage. 1578b1991ee3SDarrick J. Wong */ 1579b1991ee3SDarrick J. Wong pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, dp->i_ino)); 1580b1991ee3SDarrick J. Wong if (!pag) { 1581b1991ee3SDarrick J. Wong ASSERT(0); 1582b1991ee3SDarrick J. Wong return -EFSCORRUPTED; 1583b1991ee3SDarrick J. Wong } 1584b1991ee3SDarrick J. Wong 1585b1991ee3SDarrick J. Wong error = xfs_iunlink_remove(sc->tp, pag, dp); 1586b1991ee3SDarrick J. Wong xfs_perag_put(pag); 1587b1991ee3SDarrick J. Wong if (error) 1588b1991ee3SDarrick J. Wong return error; 1589b1991ee3SDarrick J. Wong 1590b1991ee3SDarrick J. Wong reset_nlink: 1591b1991ee3SDarrick J. Wong if (VFS_I(dp)->i_nlink != new_nlink) 1592b1991ee3SDarrick J. Wong set_nlink(VFS_I(dp), new_nlink); 1593b1991ee3SDarrick J. Wong return 0; 1594b1991ee3SDarrick J. Wong } 1595b1991ee3SDarrick J. Wong 159676fc23b6SDarrick J. Wong /* 159776fc23b6SDarrick J. Wong * Finish replaying stashed dirent updates, allocate a transaction for 159876fc23b6SDarrick J. Wong * exchanging data fork mappings, and take the ILOCKs of both directories 159976fc23b6SDarrick J. Wong * before we commit the new directory structure. 160076fc23b6SDarrick J. Wong */ 160176fc23b6SDarrick J. Wong STATIC int 160276fc23b6SDarrick J. Wong xrep_dir_finalize_tempdir( 160376fc23b6SDarrick J. Wong struct xrep_dir *rd) 160476fc23b6SDarrick J. Wong { 160576fc23b6SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 160676fc23b6SDarrick J. Wong int error; 160776fc23b6SDarrick J. Wong 160876fc23b6SDarrick J. Wong if (!xfs_has_parent(sc->mp)) 160976fc23b6SDarrick J. Wong return xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, &rd->tx); 161076fc23b6SDarrick J. Wong 161176fc23b6SDarrick J. Wong /* 161276fc23b6SDarrick J. Wong * Repair relies on the ILOCK to quiesce all possible dirent updates. 161376fc23b6SDarrick J. Wong * Replay all queued dirent updates into the tempdir before exchanging 161476fc23b6SDarrick J. Wong * the contents, even if that means dropping the ILOCKs and the 161576fc23b6SDarrick J. Wong * transaction. 161676fc23b6SDarrick J. Wong */ 161776fc23b6SDarrick J. Wong do { 161876fc23b6SDarrick J. Wong error = xrep_dir_replay_updates(rd); 161976fc23b6SDarrick J. Wong if (error) 162076fc23b6SDarrick J. Wong return error; 162176fc23b6SDarrick J. Wong 162276fc23b6SDarrick J. Wong error = xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, &rd->tx); 162376fc23b6SDarrick J. Wong if (error) 162476fc23b6SDarrick J. Wong return error; 162576fc23b6SDarrick J. Wong 162676fc23b6SDarrick J. Wong if (xfarray_length(rd->dir_entries) == 0) 162776fc23b6SDarrick J. Wong break; 162876fc23b6SDarrick J. Wong 162976fc23b6SDarrick J. Wong xchk_trans_cancel(sc); 163076fc23b6SDarrick J. Wong xrep_tempfile_iunlock_both(sc); 163176fc23b6SDarrick J. Wong } while (!xchk_should_terminate(sc, &error)); 163276fc23b6SDarrick J. Wong return error; 163376fc23b6SDarrick J. Wong } 163476fc23b6SDarrick J. Wong 1635b1991ee3SDarrick J. Wong /* Exchange the temporary directory's data fork with the one being repaired. */ 1636b1991ee3SDarrick J. Wong STATIC int 1637b1991ee3SDarrick J. Wong xrep_dir_swap( 1638b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1639b1991ee3SDarrick J. Wong { 1640b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1641*87b7c205SDarrick J. Wong xfs_ino_t ino; 1642b1991ee3SDarrick J. Wong bool ip_local, temp_local; 1643b1991ee3SDarrick J. Wong int error = 0; 1644b1991ee3SDarrick J. Wong 1645b1991ee3SDarrick J. Wong /* 16461e58a8ccSDarrick J. Wong * If we never found the parent for this directory, temporarily assign 16471e58a8ccSDarrick J. Wong * the root dir as the parent; we'll move this to the orphanage after 16481e58a8ccSDarrick J. Wong * exchanging the dir contents. We hold the ILOCK of the dir being 16491e58a8ccSDarrick J. Wong * repaired, so we're not worried about racy updates of dotdot. 1650a07b4557SDarrick J. Wong */ 1651a07b4557SDarrick J. Wong ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL); 16521e58a8ccSDarrick J. Wong if (rd->pscan.parent_ino == NULLFSINO) { 16531e58a8ccSDarrick J. Wong rd->needs_adoption = true; 16541e58a8ccSDarrick J. Wong rd->pscan.parent_ino = rd->sc->mp->m_sb.sb_rootino; 16551e58a8ccSDarrick J. Wong } 1656a07b4557SDarrick J. Wong 1657a07b4557SDarrick J. Wong /* 1658b1991ee3SDarrick J. Wong * Reset the temporary directory's '..' entry to point to the parent 1659*87b7c205SDarrick J. Wong * that we found. The dirent replace code asserts if the dirent 1660*87b7c205SDarrick J. Wong * already points at the new inumber, so we look it up here. 1661b1991ee3SDarrick J. Wong * 1662b1991ee3SDarrick J. Wong * It's also possible that this replacement could also expand a sf 1663b1991ee3SDarrick J. Wong * tempdir into block format. 1664b1991ee3SDarrick J. Wong */ 1665*87b7c205SDarrick J. Wong error = xchk_dir_lookup(sc, rd->sc->tempip, &xfs_name_dotdot, &ino); 1666*87b7c205SDarrick J. Wong if (error) 1667*87b7c205SDarrick J. Wong return error; 1668*87b7c205SDarrick J. Wong 1669*87b7c205SDarrick J. Wong if (rd->pscan.parent_ino != ino) { 1670b1991ee3SDarrick J. Wong error = xrep_dir_replace(rd, rd->sc->tempip, &xfs_name_dotdot, 1671a07b4557SDarrick J. Wong rd->pscan.parent_ino, rd->tx.req.resblks); 1672b1991ee3SDarrick J. Wong if (error) 1673b1991ee3SDarrick J. Wong return error; 1674b1991ee3SDarrick J. Wong } 1675b1991ee3SDarrick J. Wong 1676b1991ee3SDarrick J. Wong /* 1677b1991ee3SDarrick J. Wong * Changing the dot and dotdot entries could have changed the shape of 1678b1991ee3SDarrick J. Wong * the directory, so we recompute these. 1679b1991ee3SDarrick J. Wong */ 1680b1991ee3SDarrick J. Wong ip_local = sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL; 1681b1991ee3SDarrick J. Wong temp_local = sc->tempip->i_df.if_format == XFS_DINODE_FMT_LOCAL; 1682b1991ee3SDarrick J. Wong 1683b1991ee3SDarrick J. Wong /* 1684b1991ee3SDarrick J. Wong * If the both files have a local format data fork and the rebuilt 1685b1991ee3SDarrick J. Wong * directory data would fit in the repaired file's data fork, copy 1686b1991ee3SDarrick J. Wong * the contents from the tempfile and update the directory link count. 1687b1991ee3SDarrick J. Wong * We're done now. 1688b1991ee3SDarrick J. Wong */ 1689b1991ee3SDarrick J. Wong if (ip_local && temp_local && 1690b1991ee3SDarrick J. Wong sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip)) { 1691b1991ee3SDarrick J. Wong xrep_tempfile_copyout_local(sc, XFS_DATA_FORK); 1692b1991ee3SDarrick J. Wong return xrep_dir_set_nlink(rd); 1693b1991ee3SDarrick J. Wong } 1694b1991ee3SDarrick J. Wong 1695b1991ee3SDarrick J. Wong /* 1696b1991ee3SDarrick J. Wong * Clean the transaction before we start working on exchanging 1697b1991ee3SDarrick J. Wong * directory contents. 1698b1991ee3SDarrick J. Wong */ 1699b1991ee3SDarrick J. Wong error = xrep_tempfile_roll_trans(rd->sc); 1700b1991ee3SDarrick J. Wong if (error) 1701b1991ee3SDarrick J. Wong return error; 1702b1991ee3SDarrick J. Wong 1703b1991ee3SDarrick J. Wong /* Otherwise, make sure both data forks are in block-mapping mode. */ 1704b1991ee3SDarrick J. Wong error = xrep_dir_swap_prep(sc, temp_local, ip_local); 1705b1991ee3SDarrick J. Wong if (error) 1706b1991ee3SDarrick J. Wong return error; 1707b1991ee3SDarrick J. Wong 1708b1991ee3SDarrick J. Wong /* 1709b1991ee3SDarrick J. Wong * Set nlink of the directory in the same transaction sequence that 1710b1991ee3SDarrick J. Wong * (atomically) commits the new directory data. 1711b1991ee3SDarrick J. Wong */ 1712b1991ee3SDarrick J. Wong error = xrep_dir_set_nlink(rd); 1713b1991ee3SDarrick J. Wong if (error) 1714b1991ee3SDarrick J. Wong return error; 1715b1991ee3SDarrick J. Wong 1716b1991ee3SDarrick J. Wong return xrep_tempexch_contents(sc, &rd->tx); 1717b1991ee3SDarrick J. Wong } 1718b1991ee3SDarrick J. Wong 1719b1991ee3SDarrick J. Wong /* 1720b1991ee3SDarrick J. Wong * Exchange the new directory contents (which we created in the tempfile) with 1721b1991ee3SDarrick J. Wong * the directory being repaired. 1722b1991ee3SDarrick J. Wong */ 1723b1991ee3SDarrick J. Wong STATIC int 1724b1991ee3SDarrick J. Wong xrep_dir_rebuild_tree( 1725b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1726b1991ee3SDarrick J. Wong { 1727b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1728b1991ee3SDarrick J. Wong int error; 1729b1991ee3SDarrick J. Wong 1730a07b4557SDarrick J. Wong trace_xrep_dir_rebuild_tree(sc->ip, rd->pscan.parent_ino); 1731b1991ee3SDarrick J. Wong 1732b1991ee3SDarrick J. Wong /* 1733b1991ee3SDarrick J. Wong * Take the IOLOCK on the temporary file so that we can run dir 1734b1991ee3SDarrick J. Wong * operations with the same locks held as we would for a normal file. 1735b1991ee3SDarrick J. Wong * We still hold sc->ip's IOLOCK. 1736b1991ee3SDarrick J. Wong */ 1737b1991ee3SDarrick J. Wong error = xrep_tempfile_iolock_polled(rd->sc); 1738b1991ee3SDarrick J. Wong if (error) 1739b1991ee3SDarrick J. Wong return error; 1740b1991ee3SDarrick J. Wong 174176fc23b6SDarrick J. Wong /* 174276fc23b6SDarrick J. Wong * Allocate transaction, lock inodes, and make sure that we've replayed 174376fc23b6SDarrick J. Wong * all the stashed dirent updates to the tempdir. After this point, 174476fc23b6SDarrick J. Wong * we're ready to exchange data fork mappings. 174576fc23b6SDarrick J. Wong */ 174676fc23b6SDarrick J. Wong error = xrep_dir_finalize_tempdir(rd); 1747b1991ee3SDarrick J. Wong if (error) 1748b1991ee3SDarrick J. Wong return error; 1749b1991ee3SDarrick J. Wong 17508559b21aSDarrick J. Wong if (xchk_iscan_aborted(&rd->pscan.iscan)) 17518559b21aSDarrick J. Wong return -ECANCELED; 17528559b21aSDarrick J. Wong 1753b1991ee3SDarrick J. Wong /* 1754b1991ee3SDarrick J. Wong * Exchange the tempdir's data fork with the file being repaired. This 1755b1991ee3SDarrick J. Wong * recreates the transaction and re-takes the ILOCK in the scrub 1756b1991ee3SDarrick J. Wong * context. 1757b1991ee3SDarrick J. Wong */ 1758b1991ee3SDarrick J. Wong error = xrep_dir_swap(rd); 1759b1991ee3SDarrick J. Wong if (error) 1760b1991ee3SDarrick J. Wong return error; 1761b1991ee3SDarrick J. Wong 1762b1991ee3SDarrick J. Wong /* 1763b1991ee3SDarrick J. Wong * Release the old directory blocks and reset the data fork of the temp 1764b1991ee3SDarrick J. Wong * directory to an empty shortform directory because inactivation does 1765b1991ee3SDarrick J. Wong * nothing for directories. 1766b1991ee3SDarrick J. Wong */ 1767b1991ee3SDarrick J. Wong error = xrep_dir_reset_fork(rd, sc->mp->m_rootip->i_ino); 1768b1991ee3SDarrick J. Wong if (error) 1769b1991ee3SDarrick J. Wong return error; 1770b1991ee3SDarrick J. Wong 1771b1991ee3SDarrick J. Wong /* 1772b1991ee3SDarrick J. Wong * Roll to get a transaction without any inodes joined to it. Then we 1773b1991ee3SDarrick J. Wong * can drop the tempfile's ILOCK and IOLOCK before doing more work on 1774b1991ee3SDarrick J. Wong * the scrub target directory. 1775b1991ee3SDarrick J. Wong */ 1776b1991ee3SDarrick J. Wong error = xfs_trans_roll(&sc->tp); 1777b1991ee3SDarrick J. Wong if (error) 1778b1991ee3SDarrick J. Wong return error; 1779b1991ee3SDarrick J. Wong 1780b1991ee3SDarrick J. Wong xrep_tempfile_iunlock(sc); 1781b1991ee3SDarrick J. Wong xrep_tempfile_iounlock(sc); 1782b1991ee3SDarrick J. Wong return 0; 1783b1991ee3SDarrick J. Wong } 1784b1991ee3SDarrick J. Wong 1785b1991ee3SDarrick J. Wong /* Set up the filesystem scan so we can regenerate directory entries. */ 1786b1991ee3SDarrick J. Wong STATIC int 1787b1991ee3SDarrick J. Wong xrep_dir_setup_scan( 1788b1991ee3SDarrick J. Wong struct xrep_dir *rd) 1789b1991ee3SDarrick J. Wong { 1790b1991ee3SDarrick J. Wong struct xfs_scrub *sc = rd->sc; 1791b1991ee3SDarrick J. Wong char *descr; 1792b1991ee3SDarrick J. Wong int error; 1793b1991ee3SDarrick J. Wong 1794b1991ee3SDarrick J. Wong /* Set up some staging memory for salvaging dirents. */ 1795b1991ee3SDarrick J. Wong descr = xchk_xfile_ino_descr(sc, "directory entries"); 1796b1991ee3SDarrick J. Wong error = xfarray_create(descr, 0, sizeof(struct xrep_dirent), 1797b1991ee3SDarrick J. Wong &rd->dir_entries); 1798b1991ee3SDarrick J. Wong kfree(descr); 1799b1991ee3SDarrick J. Wong if (error) 1800b1991ee3SDarrick J. Wong return error; 1801b1991ee3SDarrick J. Wong 1802b1991ee3SDarrick J. Wong descr = xchk_xfile_ino_descr(sc, "directory entry names"); 1803b1991ee3SDarrick J. Wong error = xfblob_create(descr, &rd->dir_names); 1804b1991ee3SDarrick J. Wong kfree(descr); 1805b1991ee3SDarrick J. Wong if (error) 1806b1991ee3SDarrick J. Wong goto out_xfarray; 1807b1991ee3SDarrick J. Wong 18088559b21aSDarrick J. Wong if (xfs_has_parent(sc->mp)) 18098559b21aSDarrick J. Wong error = __xrep_findparent_scan_start(sc, &rd->pscan, 18108559b21aSDarrick J. Wong xrep_dir_live_update); 18118559b21aSDarrick J. Wong else 1812a07b4557SDarrick J. Wong error = xrep_findparent_scan_start(sc, &rd->pscan); 1813a07b4557SDarrick J. Wong if (error) 1814a07b4557SDarrick J. Wong goto out_xfblob; 1815a07b4557SDarrick J. Wong 1816b1991ee3SDarrick J. Wong return 0; 1817b1991ee3SDarrick J. Wong 1818a07b4557SDarrick J. Wong out_xfblob: 1819a07b4557SDarrick J. Wong xfblob_destroy(rd->dir_names); 1820a07b4557SDarrick J. Wong rd->dir_names = NULL; 1821b1991ee3SDarrick J. Wong out_xfarray: 1822b1991ee3SDarrick J. Wong xfarray_destroy(rd->dir_entries); 1823b1991ee3SDarrick J. Wong rd->dir_entries = NULL; 1824b1991ee3SDarrick J. Wong return error; 1825b1991ee3SDarrick J. Wong } 1826b1991ee3SDarrick J. Wong 1827b1991ee3SDarrick J. Wong /* 18281e58a8ccSDarrick J. Wong * Move the current file to the orphanage. 18291e58a8ccSDarrick J. Wong * 18301e58a8ccSDarrick J. Wong * Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks. Upon 18311e58a8ccSDarrick J. Wong * successful return, the scrub transaction will have enough extra reservation 18321e58a8ccSDarrick J. Wong * to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the 18331e58a8ccSDarrick J. Wong * orphanage; and both inodes will be ijoined. 18341e58a8ccSDarrick J. Wong */ 18351e58a8ccSDarrick J. Wong STATIC int 18361e58a8ccSDarrick J. Wong xrep_dir_move_to_orphanage( 18371e58a8ccSDarrick J. Wong struct xrep_dir *rd) 18381e58a8ccSDarrick J. Wong { 18391e58a8ccSDarrick J. Wong struct xfs_scrub *sc = rd->sc; 18401e58a8ccSDarrick J. Wong xfs_ino_t orig_parent, new_parent; 18411e58a8ccSDarrick J. Wong int error; 18421e58a8ccSDarrick J. Wong 18431e58a8ccSDarrick J. Wong /* 18441e58a8ccSDarrick J. Wong * We are about to drop the ILOCK on sc->ip to lock the orphanage and 18451e58a8ccSDarrick J. Wong * prepare for the adoption. Therefore, look up the old dotdot entry 18461e58a8ccSDarrick J. Wong * for sc->ip so that we can compare it after we re-lock sc->ip. 18471e58a8ccSDarrick J. Wong */ 18481e58a8ccSDarrick J. Wong error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &orig_parent); 18491e58a8ccSDarrick J. Wong if (error) 18501e58a8ccSDarrick J. Wong return error; 18511e58a8ccSDarrick J. Wong 18521e58a8ccSDarrick J. Wong /* 18531e58a8ccSDarrick J. Wong * Drop the ILOCK on the scrub target and commit the transaction. 18541e58a8ccSDarrick J. Wong * Adoption computes its own resource requirements and gathers the 18551e58a8ccSDarrick J. Wong * necessary components. 18561e58a8ccSDarrick J. Wong */ 18571e58a8ccSDarrick J. Wong error = xrep_trans_commit(sc); 18581e58a8ccSDarrick J. Wong if (error) 18591e58a8ccSDarrick J. Wong return error; 18601e58a8ccSDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL); 18611e58a8ccSDarrick J. Wong 18621e58a8ccSDarrick J. Wong /* If we can take the orphanage's iolock then we're ready to move. */ 18631e58a8ccSDarrick J. Wong if (!xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) { 18641e58a8ccSDarrick J. Wong xchk_iunlock(sc, sc->ilock_flags); 18651e58a8ccSDarrick J. Wong error = xrep_orphanage_iolock_two(sc); 18661e58a8ccSDarrick J. Wong if (error) 18671e58a8ccSDarrick J. Wong return error; 18681e58a8ccSDarrick J. Wong } 18691e58a8ccSDarrick J. Wong 18701e58a8ccSDarrick J. Wong /* Grab transaction and ILOCK the two files. */ 18711e58a8ccSDarrick J. Wong error = xrep_adoption_trans_alloc(sc, &rd->adoption); 18721e58a8ccSDarrick J. Wong if (error) 18731e58a8ccSDarrick J. Wong return error; 18741e58a8ccSDarrick J. Wong 18751e58a8ccSDarrick J. Wong error = xrep_adoption_compute_name(&rd->adoption, &rd->xname); 18761e58a8ccSDarrick J. Wong if (error) 18771e58a8ccSDarrick J. Wong return error; 18781e58a8ccSDarrick J. Wong 18791e58a8ccSDarrick J. Wong /* 18801e58a8ccSDarrick J. Wong * Now that we've reacquired the ILOCK on sc->ip, look up the dotdot 18811e58a8ccSDarrick J. Wong * entry again. If the parent changed or the child was unlinked while 18821e58a8ccSDarrick J. Wong * the child directory was unlocked, we don't need to move the child to 18831e58a8ccSDarrick J. Wong * the orphanage after all. 18841e58a8ccSDarrick J. Wong */ 18851e58a8ccSDarrick J. Wong error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &new_parent); 18861e58a8ccSDarrick J. Wong if (error) 18871e58a8ccSDarrick J. Wong return error; 18881e58a8ccSDarrick J. Wong 18891e58a8ccSDarrick J. Wong /* 18901e58a8ccSDarrick J. Wong * Attach to the orphanage if we still have a linked directory and it 18911e58a8ccSDarrick J. Wong * hasn't been moved. 18921e58a8ccSDarrick J. Wong */ 18931e58a8ccSDarrick J. Wong if (orig_parent == new_parent && VFS_I(sc->ip)->i_nlink > 0) { 18941e58a8ccSDarrick J. Wong error = xrep_adoption_move(&rd->adoption); 18951e58a8ccSDarrick J. Wong if (error) 18961e58a8ccSDarrick J. Wong return error; 18971e58a8ccSDarrick J. Wong } 18981e58a8ccSDarrick J. Wong 18991e58a8ccSDarrick J. Wong /* 19001e58a8ccSDarrick J. Wong * Launder the scrub transaction so we can drop the orphanage ILOCK 19011e58a8ccSDarrick J. Wong * and IOLOCK. Return holding the scrub target's ILOCK and IOLOCK. 19021e58a8ccSDarrick J. Wong */ 19031e58a8ccSDarrick J. Wong error = xrep_adoption_trans_roll(&rd->adoption); 19041e58a8ccSDarrick J. Wong if (error) 19051e58a8ccSDarrick J. Wong return error; 19061e58a8ccSDarrick J. Wong 19071e58a8ccSDarrick J. Wong xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL); 19081e58a8ccSDarrick J. Wong xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL); 19091e58a8ccSDarrick J. Wong return 0; 19101e58a8ccSDarrick J. Wong } 19111e58a8ccSDarrick J. Wong 19121e58a8ccSDarrick J. Wong /* 1913b1991ee3SDarrick J. Wong * Repair the directory metadata. 1914b1991ee3SDarrick J. Wong * 1915b1991ee3SDarrick J. Wong * XXX: Directory entry buffers can be multiple fsblocks in size. The buffer 1916b1991ee3SDarrick J. Wong * cache in XFS can't handle aliased multiblock buffers, so this might 1917b1991ee3SDarrick J. Wong * misbehave if the directory blocks are crosslinked with other filesystem 1918b1991ee3SDarrick J. Wong * metadata. 1919b1991ee3SDarrick J. Wong * 1920b1991ee3SDarrick J. Wong * XXX: Is it necessary to check the dcache for this directory to make sure 1921b1991ee3SDarrick J. Wong * that we always recreate every cached entry? 1922b1991ee3SDarrick J. Wong */ 1923b1991ee3SDarrick J. Wong int 1924b1991ee3SDarrick J. Wong xrep_directory( 1925b1991ee3SDarrick J. Wong struct xfs_scrub *sc) 1926b1991ee3SDarrick J. Wong { 1927b1991ee3SDarrick J. Wong struct xrep_dir *rd = sc->buf; 1928b1991ee3SDarrick J. Wong int error; 1929b1991ee3SDarrick J. Wong 1930b1991ee3SDarrick J. Wong /* The rmapbt is required to reap the old data fork. */ 1931b1991ee3SDarrick J. Wong if (!xfs_has_rmapbt(sc->mp)) 1932b1991ee3SDarrick J. Wong return -EOPNOTSUPP; 19336d335233SDarrick J. Wong /* We require atomic file exchange range to rebuild anything. */ 19346d335233SDarrick J. Wong if (!xfs_has_exchange_range(sc->mp)) 19356d335233SDarrick J. Wong return -EOPNOTSUPP; 1936b1991ee3SDarrick J. Wong 1937b1991ee3SDarrick J. Wong error = xrep_dir_setup_scan(rd); 1938b1991ee3SDarrick J. Wong if (error) 1939b1991ee3SDarrick J. Wong return error; 1940b1991ee3SDarrick J. Wong 194176fc23b6SDarrick J. Wong if (xfs_has_parent(sc->mp)) 194276fc23b6SDarrick J. Wong error = xrep_dir_scan_dirtree(rd); 194376fc23b6SDarrick J. Wong else 1944b1991ee3SDarrick J. Wong error = xrep_dir_salvage_entries(rd); 1945b1991ee3SDarrick J. Wong if (error) 1946b1991ee3SDarrick J. Wong goto out_teardown; 1947b1991ee3SDarrick J. Wong 1948b1991ee3SDarrick J. Wong /* Last chance to abort before we start committing fixes. */ 1949b1991ee3SDarrick J. Wong if (xchk_should_terminate(sc, &error)) 1950b1991ee3SDarrick J. Wong goto out_teardown; 1951b1991ee3SDarrick J. Wong 1952b1991ee3SDarrick J. Wong error = xrep_dir_rebuild_tree(rd); 1953b1991ee3SDarrick J. Wong if (error) 1954b1991ee3SDarrick J. Wong goto out_teardown; 1955b1991ee3SDarrick J. Wong 19561e58a8ccSDarrick J. Wong if (rd->needs_adoption) { 19571e58a8ccSDarrick J. Wong if (!xrep_orphanage_can_adopt(rd->sc)) 19581e58a8ccSDarrick J. Wong error = -EFSCORRUPTED; 19591e58a8ccSDarrick J. Wong else 19601e58a8ccSDarrick J. Wong error = xrep_dir_move_to_orphanage(rd); 19611e58a8ccSDarrick J. Wong if (error) 19621e58a8ccSDarrick J. Wong goto out_teardown; 19631e58a8ccSDarrick J. Wong } 19641e58a8ccSDarrick J. Wong 1965b1991ee3SDarrick J. Wong out_teardown: 1966b1991ee3SDarrick J. Wong xrep_dir_teardown(sc); 1967b1991ee3SDarrick J. Wong return error; 1968b1991ee3SDarrick J. Wong } 1969