^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2017 Oracle. All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Author: Darrick J. Wong <darrick.wong@oracle.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include "xfs.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include "xfs_fs.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include "xfs_shared.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include "xfs_format.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include "xfs_trans_resv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include "xfs_mount.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include "xfs_log_format.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include "xfs_trans.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include "xfs_inode.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include "xfs_icache.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include "xfs_dir2.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include "xfs_dir2_priv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "scrub/scrub.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include "scrub/common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include "scrub/dabtree.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) /* Set us up to scrub directories. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) xchk_setup_directory(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) struct xfs_inode *ip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) return xchk_setup_inode_contents(sc, ip, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* Directories */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /* Scrub a directory entry. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct xchk_dir_ctx {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /* VFS fill-directory iterator */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct dir_context dir_iter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct xfs_scrub *sc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) /* Check that an inode's mode matches a given DT_ type. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) xchk_dir_check_ftype(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) struct xchk_dir_ctx *sdc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) xfs_fileoff_t offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) xfs_ino_t inum,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) int dtype)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) struct xfs_mount *mp = sdc->sc->mp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct xfs_inode *ip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) int ino_dtype;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) int error = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) if (!xfs_sb_version_hasftype(&mp->m_sb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (dtype != DT_UNKNOWN && dtype != DT_DIR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * Grab the inode pointed to by the dirent. We release the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * inode before we cancel the scrub transaction. Since we're
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * don't know a priori that releasing the inode won't trigger
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * eofblocks cleanup (which allocates what would be a nested
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * transaction), we can't use DONTCACHE here because DONTCACHE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * inodes can trigger immediate inactive cleanup of the inode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) error = xfs_iget(mp, sdc->sc->tp, inum, 0, 0, &ip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (!xchk_fblock_xref_process_error(sdc->sc, XFS_DATA_FORK, offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) /* Convert mode to the DT_* values that dir_emit uses. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) ino_dtype = xfs_dir3_get_dtype(mp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) xfs_mode_to_ftype(VFS_I(ip)->i_mode));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (ino_dtype != dtype)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) xfs_irele(ip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * Scrub a single directory entry.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * We use the VFS directory iterator (i.e. readdir) to call this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * function for every directory entry in a directory. Once we're here,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * we check the inode number to make sure it's sane, then we check that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * we can look up this filename. Finally, we check the ftype.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) xchk_dir_actor(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct dir_context *dir_iter,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) const char *name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) int namelen,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) loff_t pos,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) u64 ino,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) unsigned type)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) struct xfs_mount *mp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct xfs_inode *ip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct xchk_dir_ctx *sdc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) struct xfs_name xname;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) xfs_ino_t lookup_ino;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) xfs_dablk_t offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) int error = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) sdc = container_of(dir_iter, struct xchk_dir_ctx, dir_iter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) ip = sdc->sc->ip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) mp = ip->i_mount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) offset = xfs_dir2_db_to_da(mp->m_dir_geo,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) xfs_dir2_dataptr_to_db(mp->m_dir_geo, pos));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (xchk_should_terminate(sdc->sc, &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* Does this inode number make sense? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) if (!xfs_verify_dir_ino(mp, ino)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) /* Does this name make sense? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (!xfs_dir2_namecheck(name, namelen)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (!strncmp(".", name, namelen)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* If this is "." then check that the inum matches the dir. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (xfs_sb_version_hasftype(&mp->m_sb) && type != DT_DIR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (ino != ip->i_ino)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) } else if (!strncmp("..", name, namelen)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) * If this is ".." in the root inode, check that the inum
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) * matches this dir.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (xfs_sb_version_hasftype(&mp->m_sb) && type != DT_DIR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) if (ip->i_ino == mp->m_sb.sb_rootino && ino != ip->i_ino)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) /* Verify that we can look up this name by hash. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) xname.name = name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) xname.len = namelen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) xname.type = XFS_DIR3_FT_UNKNOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) error = xfs_dir_lookup(sdc->sc->tp, ip, &xname, &lookup_ino, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) /* ENOENT means the hash lookup failed and the dir is corrupt */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (error == -ENOENT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) error = -EFSCORRUPTED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) if (!xchk_fblock_process_error(sdc->sc, XFS_DATA_FORK, offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (lookup_ino != ino) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) /* Verify the file type. This function absorbs error codes. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) error = xchk_dir_check_ftype(sdc, offset, lookup_ino, type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) * A negative error code returned here is supposed to cause the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) * dir_emit caller (xfs_readdir) to abort the directory iteration
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * and return zero to xchk_directory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if (error == 0 && sdc->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) return -EFSCORRUPTED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) /* Scrub a directory btree record. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) xchk_dir_rec(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) struct xchk_da_btree *ds,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) int level)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) struct xfs_da_state_blk *blk = &ds->state->path.blk[level];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) struct xfs_mount *mp = ds->state->mp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) struct xfs_inode *dp = ds->dargs.dp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) struct xfs_da_geometry *geo = mp->m_dir_geo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) struct xfs_dir2_data_entry *dent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) struct xfs_buf *bp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) struct xfs_dir2_leaf_entry *ent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) unsigned int end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) unsigned int iter_off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) xfs_ino_t ino;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) xfs_dablk_t rec_bno;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) xfs_dir2_db_t db;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) xfs_dir2_data_aoff_t off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) xfs_dir2_dataptr_t ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) xfs_dahash_t calc_hash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) xfs_dahash_t hash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) struct xfs_dir3_icleaf_hdr hdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) unsigned int tag;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) blk->magic == XFS_DIR2_LEAFN_MAGIC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) ent = hdr.ents + blk->index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) /* Check the hash of the entry. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) error = xchk_da_btree_hash(ds, level, &ent->hashval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) /* Valid hash pointer? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) ptr = be32_to_cpu(ent->address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if (ptr == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) /* Find the directory entry's location. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) db = xfs_dir2_dataptr_to_db(geo, ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) off = xfs_dir2_dataptr_to_off(geo, ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) rec_bno = xfs_dir2_db_to_da(geo, db);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) if (rec_bno >= geo->leafblk) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) xchk_da_set_corrupt(ds, level);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) error = xfs_dir3_data_read(ds->dargs.trans, dp, rec_bno,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) XFS_DABUF_MAP_HOLE_OK, &bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) if (!bp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) xchk_buffer_recheck(ds->sc, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) goto out_relse;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) dent = bp->b_addr + off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) /* Make sure we got a real directory entry. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) iter_off = geo->data_entry_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) end = xfs_dir3_data_end_offset(geo, bp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) if (!end) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) goto out_relse;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) struct xfs_dir2_data_entry *dep = bp->b_addr + iter_off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) struct xfs_dir2_data_unused *dup = bp->b_addr + iter_off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) if (iter_off >= end) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) goto out_relse;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) iter_off += be16_to_cpu(dup->length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (dep == dent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) iter_off += xfs_dir2_data_entsize(mp, dep->namelen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) /* Retrieve the entry, sanity check it, and compare hashes. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) ino = be64_to_cpu(dent->inumber);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) hash = be32_to_cpu(ent->hashval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (!xfs_verify_dir_ino(mp, ino) || tag != off)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) if (dent->namelen == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) goto out_relse;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) calc_hash = xfs_da_hashname(dent->name, dent->namelen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) if (calc_hash != hash)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) out_relse:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) xfs_trans_brelse(ds->dargs.trans, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) * Is this unused entry either in the bestfree or smaller than all of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) * them? We've already checked that the bestfrees are sorted longest to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) * shortest, and that there aren't any bogus entries.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) STATIC void
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) xchk_directory_check_free_entry(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) xfs_dablk_t lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) struct xfs_dir2_data_free *bf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) struct xfs_dir2_data_unused *dup)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) struct xfs_dir2_data_free *dfp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) unsigned int dup_length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) dup_length = be16_to_cpu(dup->length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) /* Unused entry is shorter than any of the bestfrees */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) if (dup_length == be16_to_cpu(dfp->length))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) /* Unused entry should be in the bestfrees but wasn't found. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) /* Check free space info in a directory data block. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) xchk_directory_data_bestfree(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) xfs_dablk_t lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) bool is_block)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) struct xfs_dir2_data_unused *dup;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) struct xfs_dir2_data_free *dfp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) struct xfs_buf *bp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) struct xfs_dir2_data_free *bf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) struct xfs_mount *mp = sc->mp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) u16 tag;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) unsigned int nr_bestfrees = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) unsigned int nr_frees = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) unsigned int smallest_bestfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) int newlen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) unsigned int offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) unsigned int end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) if (is_block) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) /* dir block format */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) error = xfs_dir3_block_read(sc->tp, sc->ip, &bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) /* dir data format */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, 0, &bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) xchk_buffer_recheck(sc, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) /* Do the bestfrees correspond to actual free space? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) smallest_bestfree = UINT_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) offset = be16_to_cpu(dfp->offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) if (offset == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) if (offset >= mp->m_dir_geo->blksize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) dup = bp->b_addr + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) /* bestfree doesn't match the entry it points at? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) tag != offset) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) /* bestfree records should be ordered largest to smallest */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) if (smallest_bestfree < be16_to_cpu(dfp->length)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) smallest_bestfree = be16_to_cpu(dfp->length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) nr_bestfrees++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) /* Make sure the bestfrees are actually the best free spaces. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) offset = mp->m_dir_geo->data_entry_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) /* Iterate the entries, stopping when we hit or go past the end. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) while (offset < end) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) dup = bp->b_addr + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) /* Skip real entries */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) newlen = xfs_dir2_data_entsize(mp, dep->namelen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) if (newlen <= 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) offset += newlen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) /* Spot check this free entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) if (tag != offset) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) * Either this entry is a bestfree or it's smaller than
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) * any of the bestfrees.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) xchk_directory_check_free_entry(sc, lblk, bf, dup);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) /* Move on. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) newlen = be16_to_cpu(dup->length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) if (newlen <= 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) goto out_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) offset += newlen;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) if (offset <= end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) nr_frees++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) /* We're required to fill all the space. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) if (offset != end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) /* Did we see at least as many free slots as there are bestfrees? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) if (nr_frees < nr_bestfrees)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) out_buf:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) xfs_trans_brelse(sc->tp, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) * Does the free space length in the free space index block ($len) match
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) * the longest length in the directory data block's bestfree array?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) * Assume that we've already checked that the data block's bestfree
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) * array is in order.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) STATIC void
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) xchk_directory_check_freesp(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) xfs_dablk_t lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) struct xfs_buf *dbp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) unsigned int len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) struct xfs_dir2_data_free *dfp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) if (len != be16_to_cpu(dfp->length))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) if (len > 0 && be16_to_cpu(dfp->offset) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) /* Check free space info in a directory leaf1 block. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) xchk_directory_leaf1_bestfree(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) struct xfs_da_args *args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) xfs_dablk_t lblk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) struct xfs_dir3_icleaf_hdr leafhdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) struct xfs_dir2_leaf_tail *ltp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) struct xfs_dir2_leaf *leaf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) struct xfs_buf *dbp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) struct xfs_buf *bp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) struct xfs_da_geometry *geo = sc->mp->m_dir_geo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) __be16 *bestp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) __u16 best;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) __u32 hash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) __u32 lasthash = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) __u32 bestcount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) unsigned int stale = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) /* Read the free space block. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, &bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) xchk_buffer_recheck(sc, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) leaf = bp->b_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) ltp = xfs_dir2_leaf_tail_p(geo, leaf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) bestcount = be32_to_cpu(ltp->bestcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) bestp = xfs_dir2_leaf_bests_p(ltp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) if (xfs_sb_version_hascrc(&sc->mp->m_sb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (hdr3->pad != cpu_to_be32(0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) * There should be as many bestfree slots as there are dir data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) * blocks that can fit under i_size.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) if (bestcount != xfs_dir2_byte_to_db(geo, sc->ip->i_d.di_size)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) /* Is the leaf count even remotely sane? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) if (leafhdr.count > geo->leaf_max_ents) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) /* Leaves and bests don't overlap in leaf format. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) /* Check hash value order, count stale entries. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) for (i = 0; i < leafhdr.count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) hash = be32_to_cpu(leafhdr.ents[i].hashval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) if (i > 0 && lasthash > hash)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) lasthash = hash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) if (leafhdr.ents[i].address ==
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) stale++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) if (leafhdr.stale != stale)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) /* Check all the bestfree entries. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) for (i = 0; i < bestcount; i++, bestp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) best = be16_to_cpu(*bestp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) error = xfs_dir3_data_read(sc->tp, sc->ip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) xfs_dir2_db_to_da(args->geo, i),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) XFS_DABUF_MAP_HOLE_OK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) &dbp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) if (!dbp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) if (best != NULLDATAOFF) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) if (best == NULLDATAOFF)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) xchk_directory_check_freesp(sc, lblk, dbp, best);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) xfs_trans_brelse(sc->tp, dbp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) xfs_trans_brelse(sc->tp, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) /* Check free space info in a directory freespace block. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) xchk_directory_free_bestfree(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) struct xfs_scrub *sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) struct xfs_da_args *args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) xfs_dablk_t lblk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) struct xfs_dir3_icfree_hdr freehdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) struct xfs_buf *dbp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) struct xfs_buf *bp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) __u16 best;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) unsigned int stale = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) /* Read the free space block */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) xchk_buffer_recheck(sc, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) if (xfs_sb_version_hascrc(&sc->mp->m_sb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) struct xfs_dir3_free_hdr *hdr3 = bp->b_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) if (hdr3->pad != cpu_to_be32(0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) /* Check all the entries. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) for (i = 0; i < freehdr.nvalid; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) best = be16_to_cpu(freehdr.bests[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) if (best == NULLDATAOFF) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) stale++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) error = xfs_dir3_data_read(sc->tp, sc->ip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) (freehdr.firstdb + i) * args->geo->fsbcount,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) 0, &dbp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) xchk_directory_check_freesp(sc, lblk, dbp, best);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) xfs_trans_brelse(sc->tp, dbp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) if (freehdr.nused + stale != freehdr.nvalid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) xfs_trans_brelse(sc->tp, bp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) /* Check free space information in directories. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) STATIC int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) xchk_directory_blocks(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) struct xfs_scrub *sc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) struct xfs_bmbt_irec got;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) struct xfs_da_args args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) struct xfs_mount *mp = sc->mp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) xfs_fileoff_t leaf_lblk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) xfs_fileoff_t free_lblk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) xfs_fileoff_t lblk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) struct xfs_iext_cursor icur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) xfs_dablk_t dabno;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) bool found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) int is_block = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) /* Ignore local format directories. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) if (ifp->if_format != XFS_DINODE_FMT_EXTENTS &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) ifp->if_format != XFS_DINODE_FMT_BTREE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) /* Is this a block dir? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) args.dp = sc->ip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) args.geo = mp->m_dir_geo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) args.trans = sc->tp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) error = xfs_dir2_isblock(&args, &is_block);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) /* Iterate all the data extents in the directory... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) /* Block directories only have a single block at offset 0. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) if (is_block &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) (got.br_startoff > 0 ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) got.br_blockcount != args.geo->fsbcount)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) got.br_startoff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) /* No more data blocks... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) if (got.br_startoff >= leaf_lblk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) * Check each data block's bestfree data.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) * Iterate all the fsbcount-aligned block offsets in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) * this directory. The directory block reading code is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) * smart enough to do its own bmap lookups to handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) * discontiguous directory blocks. When we're done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) * with the extent record, re-query the bmap at the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) * next fsbcount-aligned offset to avoid redundant
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) * block checks.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) for (lblk = roundup((xfs_dablk_t)got.br_startoff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) args.geo->fsbcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) lblk < got.br_startoff + got.br_blockcount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) lblk += args.geo->fsbcount) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) error = xchk_directory_data_bestfree(sc, lblk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) is_block);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) dabno = got.br_startoff + got.br_blockcount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) lblk = roundup(dabno, args.geo->fsbcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) /* Look for a leaf1 block, which has free info. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) got.br_startoff == leaf_lblk &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) got.br_blockcount == args.geo->fsbcount &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) !xfs_iext_next_extent(ifp, &icur, &got)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) if (is_block) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) error = xchk_directory_leaf1_bestfree(sc, &args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) leaf_lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) /* Scan for free blocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) lblk = free_lblk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) * Dirs can't have blocks mapped above 2^32.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) * Single-block dirs shouldn't even be here.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) lblk = got.br_startoff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) if (lblk & ~0xFFFFFFFFULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) if (is_block) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) * Check each dir free block's bestfree data.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) * Iterate all the fsbcount-aligned block offsets in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) * this directory. The directory block reading code is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) * smart enough to do its own bmap lookups to handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) * discontiguous directory blocks. When we're done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) * with the extent record, re-query the bmap at the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) * next fsbcount-aligned offset to avoid redundant
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) * block checks.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) for (lblk = roundup((xfs_dablk_t)got.br_startoff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) args.geo->fsbcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) lblk < got.br_startoff + got.br_blockcount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) lblk += args.geo->fsbcount) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) error = xchk_directory_free_bestfree(sc, &args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) lblk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) dabno = got.br_startoff + got.br_blockcount;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) lblk = roundup(dabno, args.geo->fsbcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) /* Scrub a whole directory. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) xchk_directory(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) struct xfs_scrub *sc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) struct xchk_dir_ctx sdc = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) .dir_iter.actor = xchk_dir_actor,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) .dir_iter.pos = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) .sc = sc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) size_t bufsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) loff_t oldpos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) int error = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) /* Plausible size? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) if (sc->ip->i_d.di_size < xfs_dir2_sf_hdr_size(0)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) xchk_ino_set_corrupt(sc, sc->ip->i_ino);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) /* Check directory tree structure */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) /* Check the freespace. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) error = xchk_directory_blocks(sc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) if (error)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) * Check that every dirent we see can also be looked up by hash.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) * Userspace usually asks for a 32k buffer, so we will too.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) sc->ip->i_d.di_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) * Look up every name in this directory by hash.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) * Use the xfs_readdir function to call xchk_dir_actor on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) * every directory entry in this directory. In _actor, we check
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) * the name, inode number, and ftype (if applicable) of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) * entry. xfs_readdir uses the VFS filldir functions to provide
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840) * iteration context.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) * The VFS grabs a read or write lock via i_rwsem before it reads
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) * or writes to a directory. If we've gotten this far we've
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) * already obtained IOLOCK_EXCL, which (since 4.10) is the same as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) * getting a write lock on i_rwsem. Therefore, it is safe for us
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) * to drop the ILOCK here in order to reuse the _readdir and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) * _dir_lookup routines, which do their own ILOCK locking.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) oldpos = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) sc->ilock_flags &= ~XFS_ILOCK_EXCL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) while (true) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) error = xfs_readdir(sc->tp, sc->ip, &sdc.dir_iter, bufsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) &error))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) if (oldpos == sdc.dir_iter.pos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) oldpos = sdc.dir_iter.pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) }