18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Oracle. All Rights Reserved. 48c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "xfs.h" 78c2ecf20Sopenharmony_ci#include "xfs_fs.h" 88c2ecf20Sopenharmony_ci#include "xfs_shared.h" 98c2ecf20Sopenharmony_ci#include "xfs_format.h" 108c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h" 118c2ecf20Sopenharmony_ci#include "xfs_mount.h" 128c2ecf20Sopenharmony_ci#include "xfs_log_format.h" 138c2ecf20Sopenharmony_ci#include "xfs_inode.h" 148c2ecf20Sopenharmony_ci#include "xfs_icache.h" 158c2ecf20Sopenharmony_ci#include "xfs_dir2.h" 168c2ecf20Sopenharmony_ci#include "xfs_dir2_priv.h" 178c2ecf20Sopenharmony_ci#include "scrub/scrub.h" 188c2ecf20Sopenharmony_ci#include "scrub/common.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Set us up to scrub parents. */ 218c2ecf20Sopenharmony_ciint 228c2ecf20Sopenharmony_cixchk_setup_parent( 238c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 248c2ecf20Sopenharmony_ci struct xfs_inode *ip) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci return xchk_setup_inode_contents(sc, ip, 0); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Parent pointers */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Look for an entry in a parent pointing to this inode. */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct xchk_parent_ctx { 348c2ecf20Sopenharmony_ci struct dir_context dc; 358c2ecf20Sopenharmony_ci struct xfs_scrub *sc; 368c2ecf20Sopenharmony_ci xfs_ino_t ino; 378c2ecf20Sopenharmony_ci xfs_nlink_t nlink; 388c2ecf20Sopenharmony_ci bool cancelled; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Look for a single entry in a directory pointing to an inode. */ 428c2ecf20Sopenharmony_ciSTATIC int 438c2ecf20Sopenharmony_cixchk_parent_actor( 448c2ecf20Sopenharmony_ci struct dir_context *dc, 458c2ecf20Sopenharmony_ci const char *name, 468c2ecf20Sopenharmony_ci int namelen, 478c2ecf20Sopenharmony_ci loff_t pos, 488c2ecf20Sopenharmony_ci u64 ino, 498c2ecf20Sopenharmony_ci unsigned type) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct xchk_parent_ctx *spc; 528c2ecf20Sopenharmony_ci int error = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci spc = container_of(dc, struct xchk_parent_ctx, dc); 558c2ecf20Sopenharmony_ci if (spc->ino == ino) 568c2ecf20Sopenharmony_ci spc->nlink++; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * If we're facing a fatal signal, bail out. Store the cancellation 608c2ecf20Sopenharmony_ci * status separately because the VFS readdir code squashes error codes 618c2ecf20Sopenharmony_ci * into short directory reads. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci if (xchk_should_terminate(spc->sc, &error)) 648c2ecf20Sopenharmony_ci spc->cancelled = true; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return error; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Count the number of dentries in the parent dir that point to this inode. */ 708c2ecf20Sopenharmony_ciSTATIC int 718c2ecf20Sopenharmony_cixchk_parent_count_parent_dentries( 728c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 738c2ecf20Sopenharmony_ci struct xfs_inode *parent, 748c2ecf20Sopenharmony_ci xfs_nlink_t *nlink) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct xchk_parent_ctx spc = { 778c2ecf20Sopenharmony_ci .dc.actor = xchk_parent_actor, 788c2ecf20Sopenharmony_ci .ino = sc->ip->i_ino, 798c2ecf20Sopenharmony_ci .sc = sc, 808c2ecf20Sopenharmony_ci }; 818c2ecf20Sopenharmony_ci size_t bufsize; 828c2ecf20Sopenharmony_ci loff_t oldpos; 838c2ecf20Sopenharmony_ci uint lock_mode; 848c2ecf20Sopenharmony_ci int error = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * If there are any blocks, read-ahead block 0 as we're almost 888c2ecf20Sopenharmony_ci * certain to have the next operation be a read there. This is 898c2ecf20Sopenharmony_ci * how we guarantee that the parent's extent map has been loaded, 908c2ecf20Sopenharmony_ci * if there is one. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci lock_mode = xfs_ilock_data_map_shared(parent); 938c2ecf20Sopenharmony_ci if (parent->i_df.if_nextents > 0) 948c2ecf20Sopenharmony_ci error = xfs_dir3_data_readahead(parent, 0, 0); 958c2ecf20Sopenharmony_ci xfs_iunlock(parent, lock_mode); 968c2ecf20Sopenharmony_ci if (error) 978c2ecf20Sopenharmony_ci return error; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Iterate the parent dir to confirm that there is 1018c2ecf20Sopenharmony_ci * exactly one entry pointing back to the inode being 1028c2ecf20Sopenharmony_ci * scanned. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci bufsize = (size_t)min_t(loff_t, XFS_READDIR_BUFSIZE, 1058c2ecf20Sopenharmony_ci parent->i_d.di_size); 1068c2ecf20Sopenharmony_ci oldpos = 0; 1078c2ecf20Sopenharmony_ci while (true) { 1088c2ecf20Sopenharmony_ci error = xfs_readdir(sc->tp, parent, &spc.dc, bufsize); 1098c2ecf20Sopenharmony_ci if (error) 1108c2ecf20Sopenharmony_ci goto out; 1118c2ecf20Sopenharmony_ci if (spc.cancelled) { 1128c2ecf20Sopenharmony_ci error = -EAGAIN; 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci if (oldpos == spc.dc.pos) 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci oldpos = spc.dc.pos; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci *nlink = spc.nlink; 1208c2ecf20Sopenharmony_ciout: 1218c2ecf20Sopenharmony_ci return error; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * Given the inode number of the alleged parent of the inode being 1268c2ecf20Sopenharmony_ci * scrubbed, try to validate that the parent has exactly one directory 1278c2ecf20Sopenharmony_ci * entry pointing back to the inode being scrubbed. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ciSTATIC int 1308c2ecf20Sopenharmony_cixchk_parent_validate( 1318c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 1328c2ecf20Sopenharmony_ci xfs_ino_t dnum, 1338c2ecf20Sopenharmony_ci bool *try_again) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct xfs_mount *mp = sc->mp; 1368c2ecf20Sopenharmony_ci struct xfs_inode *dp = NULL; 1378c2ecf20Sopenharmony_ci xfs_nlink_t expected_nlink; 1388c2ecf20Sopenharmony_ci xfs_nlink_t nlink; 1398c2ecf20Sopenharmony_ci int error = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci *try_again = false; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1448c2ecf20Sopenharmony_ci goto out; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* '..' must not point to ourselves. */ 1478c2ecf20Sopenharmony_ci if (sc->ip->i_ino == dnum) { 1488c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * If we're an unlinked directory, the parent /won't/ have a link 1548c2ecf20Sopenharmony_ci * to us. Otherwise, it should have one link. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Grab this parent inode. We release the inode before we 1608c2ecf20Sopenharmony_ci * cancel the scrub transaction. Since we're don't know a 1618c2ecf20Sopenharmony_ci * priori that releasing the inode won't trigger eofblocks 1628c2ecf20Sopenharmony_ci * cleanup (which allocates what would be a nested transaction) 1638c2ecf20Sopenharmony_ci * if the parent pointer erroneously points to a file, we 1648c2ecf20Sopenharmony_ci * can't use DONTCACHE here because DONTCACHE inodes can trigger 1658c2ecf20Sopenharmony_ci * immediate inactive cleanup of the inode. 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * If _iget returns -EINVAL then the parent inode number is garbage 1688c2ecf20Sopenharmony_ci * and the directory is corrupt. If the _iget returns -EFSCORRUPTED 1698c2ecf20Sopenharmony_ci * or -EFSBADCRC then the parent is corrupt which is a cross 1708c2ecf20Sopenharmony_ci * referencing error. Any other error is an operational error. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp); 1738c2ecf20Sopenharmony_ci if (error == -EINVAL) { 1748c2ecf20Sopenharmony_ci error = -EFSCORRUPTED; 1758c2ecf20Sopenharmony_ci xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error); 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 1798c2ecf20Sopenharmony_ci goto out; 1808c2ecf20Sopenharmony_ci if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) { 1818c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 1828c2ecf20Sopenharmony_ci goto out_rele; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * We prefer to keep the inode locked while we lock and search 1878c2ecf20Sopenharmony_ci * its alleged parent for a forward reference. If we can grab 1888c2ecf20Sopenharmony_ci * the iolock, validate the pointers and we're done. We must 1898c2ecf20Sopenharmony_ci * use nowait here to avoid an ABBA deadlock on the parent and 1908c2ecf20Sopenharmony_ci * the child inodes. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) { 1938c2ecf20Sopenharmony_ci error = xchk_parent_count_parent_dentries(sc, dp, &nlink); 1948c2ecf20Sopenharmony_ci if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, 1958c2ecf20Sopenharmony_ci &error)) 1968c2ecf20Sopenharmony_ci goto out_unlock; 1978c2ecf20Sopenharmony_ci if (nlink != expected_nlink) 1988c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 1998c2ecf20Sopenharmony_ci goto out_unlock; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * The game changes if we get here. We failed to lock the parent, 2048c2ecf20Sopenharmony_ci * so we're going to try to verify both pointers while only holding 2058c2ecf20Sopenharmony_ci * one lock so as to avoid deadlocking with something that's actually 2068c2ecf20Sopenharmony_ci * trying to traverse down the directory tree. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci xfs_iunlock(sc->ip, sc->ilock_flags); 2098c2ecf20Sopenharmony_ci sc->ilock_flags = 0; 2108c2ecf20Sopenharmony_ci error = xchk_ilock_inverted(dp, XFS_IOLOCK_SHARED); 2118c2ecf20Sopenharmony_ci if (error) 2128c2ecf20Sopenharmony_ci goto out_rele; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Go looking for our dentry. */ 2158c2ecf20Sopenharmony_ci error = xchk_parent_count_parent_dentries(sc, dp, &nlink); 2168c2ecf20Sopenharmony_ci if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 2178c2ecf20Sopenharmony_ci goto out_unlock; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Drop the parent lock, relock this inode. */ 2208c2ecf20Sopenharmony_ci xfs_iunlock(dp, XFS_IOLOCK_SHARED); 2218c2ecf20Sopenharmony_ci error = xchk_ilock_inverted(sc->ip, XFS_IOLOCK_EXCL); 2228c2ecf20Sopenharmony_ci if (error) 2238c2ecf20Sopenharmony_ci goto out_rele; 2248c2ecf20Sopenharmony_ci sc->ilock_flags = XFS_IOLOCK_EXCL; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * If we're an unlinked directory, the parent /won't/ have a link 2288c2ecf20Sopenharmony_ci * to us. Otherwise, it should have one link. We have to re-set 2298c2ecf20Sopenharmony_ci * it here because we dropped the lock on sc->ip. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Look up '..' to see if the inode changed. */ 2348c2ecf20Sopenharmony_ci error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL); 2358c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error)) 2368c2ecf20Sopenharmony_ci goto out_rele; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Drat, parent changed. Try again! */ 2398c2ecf20Sopenharmony_ci if (dnum != dp->i_ino) { 2408c2ecf20Sopenharmony_ci xfs_irele(dp); 2418c2ecf20Sopenharmony_ci *try_again = true; 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci xfs_irele(dp); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * '..' didn't change, so check that there was only one entry 2488c2ecf20Sopenharmony_ci * for us in the parent. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci if (nlink != expected_nlink) 2518c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 2528c2ecf20Sopenharmony_ci return error; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciout_unlock: 2558c2ecf20Sopenharmony_ci xfs_iunlock(dp, XFS_IOLOCK_SHARED); 2568c2ecf20Sopenharmony_ciout_rele: 2578c2ecf20Sopenharmony_ci xfs_irele(dp); 2588c2ecf20Sopenharmony_ciout: 2598c2ecf20Sopenharmony_ci return error; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Scrub a parent pointer. */ 2638c2ecf20Sopenharmony_ciint 2648c2ecf20Sopenharmony_cixchk_parent( 2658c2ecf20Sopenharmony_ci struct xfs_scrub *sc) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct xfs_mount *mp = sc->mp; 2688c2ecf20Sopenharmony_ci xfs_ino_t dnum; 2698c2ecf20Sopenharmony_ci bool try_again; 2708c2ecf20Sopenharmony_ci int tries = 0; 2718c2ecf20Sopenharmony_ci int error = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * If we're a directory, check that the '..' link points up to 2758c2ecf20Sopenharmony_ci * a directory that has one entry pointing to us. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) 2788c2ecf20Sopenharmony_ci return -ENOENT; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* We're not a special inode, are we? */ 2818c2ecf20Sopenharmony_ci if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) { 2828c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 2838c2ecf20Sopenharmony_ci goto out; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * The VFS grabs a read or write lock via i_rwsem before it reads 2888c2ecf20Sopenharmony_ci * or writes to a directory. If we've gotten this far we've 2898c2ecf20Sopenharmony_ci * already obtained IOLOCK_EXCL, which (since 4.10) is the same as 2908c2ecf20Sopenharmony_ci * getting a write lock on i_rwsem. Therefore, it is safe for us 2918c2ecf20Sopenharmony_ci * to drop the ILOCK here in order to do directory lookups. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci sc->ilock_flags &= ~(XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL); 2948c2ecf20Sopenharmony_ci xfs_iunlock(sc->ip, XFS_ILOCK_EXCL | XFS_MMAPLOCK_EXCL); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Look up '..' */ 2978c2ecf20Sopenharmony_ci error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot, &dnum, NULL); 2988c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error)) 2998c2ecf20Sopenharmony_ci goto out; 3008c2ecf20Sopenharmony_ci if (!xfs_verify_dir_ino(mp, dnum)) { 3018c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 3028c2ecf20Sopenharmony_ci goto out; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Is this the root dir? Then '..' must point to itself. */ 3068c2ecf20Sopenharmony_ci if (sc->ip == mp->m_rootip) { 3078c2ecf20Sopenharmony_ci if (sc->ip->i_ino != mp->m_sb.sb_rootino || 3088c2ecf20Sopenharmony_ci sc->ip->i_ino != dnum) 3098c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci do { 3148c2ecf20Sopenharmony_ci error = xchk_parent_validate(sc, dnum, &try_again); 3158c2ecf20Sopenharmony_ci if (error) 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci } while (try_again && ++tries < 20); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * We gave it our best shot but failed, so mark this scrub 3218c2ecf20Sopenharmony_ci * incomplete. Userspace can decide if it wants to try again. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci if (try_again && tries == 20) 3248c2ecf20Sopenharmony_ci xchk_set_incomplete(sc); 3258c2ecf20Sopenharmony_ciout: 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * If we failed to lock the parent inode even after a retry, just mark 3288c2ecf20Sopenharmony_ci * this scrub incomplete and return. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci if ((sc->flags & XCHK_TRY_HARDER) && error == -EDEADLOCK) { 3318c2ecf20Sopenharmony_ci error = 0; 3328c2ecf20Sopenharmony_ci xchk_set_incomplete(sc); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci return error; 3358c2ecf20Sopenharmony_ci} 336