162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2022-2023 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_dir2.h" 1562306a36Sopenharmony_ci#include "xfs_dir2_priv.h" 1662306a36Sopenharmony_ci#include "xfs_trace.h" 1762306a36Sopenharmony_ci#include "xfs_bmap.h" 1862306a36Sopenharmony_ci#include "xfs_trans.h" 1962306a36Sopenharmony_ci#include "xfs_error.h" 2062306a36Sopenharmony_ci#include "scrub/scrub.h" 2162306a36Sopenharmony_ci#include "scrub/readdir.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Call a function for every entry in a shortform directory. */ 2462306a36Sopenharmony_ciSTATIC int 2562306a36Sopenharmony_cixchk_dir_walk_sf( 2662306a36Sopenharmony_ci struct xfs_scrub *sc, 2762306a36Sopenharmony_ci struct xfs_inode *dp, 2862306a36Sopenharmony_ci xchk_dirent_fn dirent_fn, 2962306a36Sopenharmony_ci void *priv) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct xfs_name name = { 3262306a36Sopenharmony_ci .name = ".", 3362306a36Sopenharmony_ci .len = 1, 3462306a36Sopenharmony_ci .type = XFS_DIR3_FT_DIR, 3562306a36Sopenharmony_ci }; 3662306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 3762306a36Sopenharmony_ci struct xfs_da_geometry *geo = mp->m_dir_geo; 3862306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep; 3962306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; 4062306a36Sopenharmony_ci xfs_ino_t ino; 4162306a36Sopenharmony_ci xfs_dir2_dataptr_t dapos; 4262306a36Sopenharmony_ci unsigned int i; 4362306a36Sopenharmony_ci int error; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_disk_size); 4662306a36Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* dot entry */ 5162306a36Sopenharmony_ci dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 5262306a36Sopenharmony_ci geo->data_entry_offset); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv); 5562306a36Sopenharmony_ci if (error) 5662306a36Sopenharmony_ci return error; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* dotdot entry */ 5962306a36Sopenharmony_ci dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 6062306a36Sopenharmony_ci geo->data_entry_offset + 6162306a36Sopenharmony_ci xfs_dir2_data_entsize(mp, sizeof(".") - 1)); 6262306a36Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 6362306a36Sopenharmony_ci name.name = ".."; 6462306a36Sopenharmony_ci name.len = 2; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci error = dirent_fn(sc, dp, dapos, &name, ino, priv); 6762306a36Sopenharmony_ci if (error) 6862306a36Sopenharmony_ci return error; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* iterate everything else */ 7162306a36Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 7262306a36Sopenharmony_ci for (i = 0; i < sfp->count; i++) { 7362306a36Sopenharmony_ci dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, 7462306a36Sopenharmony_ci xfs_dir2_sf_get_offset(sfep)); 7562306a36Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 7662306a36Sopenharmony_ci name.name = sfep->name; 7762306a36Sopenharmony_ci name.len = sfep->namelen; 7862306a36Sopenharmony_ci name.type = xfs_dir2_sf_get_ftype(mp, sfep); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci error = dirent_fn(sc, dp, dapos, &name, ino, priv); 8162306a36Sopenharmony_ci if (error) 8262306a36Sopenharmony_ci return error; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Call a function for every entry in a block directory. */ 9162306a36Sopenharmony_ciSTATIC int 9262306a36Sopenharmony_cixchk_dir_walk_block( 9362306a36Sopenharmony_ci struct xfs_scrub *sc, 9462306a36Sopenharmony_ci struct xfs_inode *dp, 9562306a36Sopenharmony_ci xchk_dirent_fn dirent_fn, 9662306a36Sopenharmony_ci void *priv) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 9962306a36Sopenharmony_ci struct xfs_da_geometry *geo = mp->m_dir_geo; 10062306a36Sopenharmony_ci struct xfs_buf *bp; 10162306a36Sopenharmony_ci unsigned int off, next_off, end; 10262306a36Sopenharmony_ci int error; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci error = xfs_dir3_block_read(sc->tp, dp, &bp); 10562306a36Sopenharmony_ci if (error) 10662306a36Sopenharmony_ci return error; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Walk each directory entry. */ 10962306a36Sopenharmony_ci end = xfs_dir3_data_end_offset(geo, bp->b_addr); 11062306a36Sopenharmony_ci for (off = geo->data_entry_offset; off < end; off = next_off) { 11162306a36Sopenharmony_ci struct xfs_name name = { }; 11262306a36Sopenharmony_ci struct xfs_dir2_data_unused *dup = bp->b_addr + off; 11362306a36Sopenharmony_ci struct xfs_dir2_data_entry *dep = bp->b_addr + off; 11462306a36Sopenharmony_ci xfs_ino_t ino; 11562306a36Sopenharmony_ci xfs_dir2_dataptr_t dapos; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Skip an empty entry. */ 11862306a36Sopenharmony_ci if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 11962306a36Sopenharmony_ci next_off = off + be16_to_cpu(dup->length); 12062306a36Sopenharmony_ci continue; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Otherwise, find the next entry and report it. */ 12462306a36Sopenharmony_ci next_off = off + xfs_dir2_data_entsize(mp, dep->namelen); 12562306a36Sopenharmony_ci if (next_off > end) 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off); 12962306a36Sopenharmony_ci ino = be64_to_cpu(dep->inumber); 13062306a36Sopenharmony_ci name.name = dep->name; 13162306a36Sopenharmony_ci name.len = dep->namelen; 13262306a36Sopenharmony_ci name.type = xfs_dir2_data_get_ftype(mp, dep); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci error = dirent_fn(sc, dp, dapos, &name, ino, priv); 13562306a36Sopenharmony_ci if (error) 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci xfs_trans_brelse(sc->tp, bp); 14062306a36Sopenharmony_ci return error; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Read a leaf-format directory buffer. */ 14462306a36Sopenharmony_ciSTATIC int 14562306a36Sopenharmony_cixchk_read_leaf_dir_buf( 14662306a36Sopenharmony_ci struct xfs_trans *tp, 14762306a36Sopenharmony_ci struct xfs_inode *dp, 14862306a36Sopenharmony_ci struct xfs_da_geometry *geo, 14962306a36Sopenharmony_ci xfs_dir2_off_t *curoff, 15062306a36Sopenharmony_ci struct xfs_buf **bpp) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct xfs_iext_cursor icur; 15362306a36Sopenharmony_ci struct xfs_bmbt_irec map; 15462306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK); 15562306a36Sopenharmony_ci xfs_dablk_t last_da; 15662306a36Sopenharmony_ci xfs_dablk_t map_off; 15762306a36Sopenharmony_ci xfs_dir2_off_t new_off; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *bpp = NULL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * Look for mapped directory blocks at or above the current offset. 16362306a36Sopenharmony_ci * Truncate down to the nearest directory block to start the scanning 16462306a36Sopenharmony_ci * operation. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET); 16762306a36Sopenharmony_ci map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff)); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map)) 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci if (map.br_startoff >= last_da) 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci xfs_trim_extent(&map, map_off, last_da - map_off); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Read the directory block of that first mapping. */ 17662306a36Sopenharmony_ci new_off = xfs_dir2_da_to_byte(geo, map.br_startoff); 17762306a36Sopenharmony_ci if (new_off > *curoff) 17862306a36Sopenharmony_ci *curoff = new_off; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* Call a function for every entry in a leaf directory. */ 18462306a36Sopenharmony_ciSTATIC int 18562306a36Sopenharmony_cixchk_dir_walk_leaf( 18662306a36Sopenharmony_ci struct xfs_scrub *sc, 18762306a36Sopenharmony_ci struct xfs_inode *dp, 18862306a36Sopenharmony_ci xchk_dirent_fn dirent_fn, 18962306a36Sopenharmony_ci void *priv) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 19262306a36Sopenharmony_ci struct xfs_da_geometry *geo = mp->m_dir_geo; 19362306a36Sopenharmony_ci struct xfs_buf *bp = NULL; 19462306a36Sopenharmony_ci xfs_dir2_off_t curoff = 0; 19562306a36Sopenharmony_ci unsigned int offset = 0; 19662306a36Sopenharmony_ci int error; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Iterate every directory offset in this directory. */ 19962306a36Sopenharmony_ci while (curoff < XFS_DIR2_LEAF_OFFSET) { 20062306a36Sopenharmony_ci struct xfs_name name = { }; 20162306a36Sopenharmony_ci struct xfs_dir2_data_unused *dup; 20262306a36Sopenharmony_ci struct xfs_dir2_data_entry *dep; 20362306a36Sopenharmony_ci xfs_ino_t ino; 20462306a36Sopenharmony_ci unsigned int length; 20562306a36Sopenharmony_ci xfs_dir2_dataptr_t dapos; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * If we have no buffer, or we're off the end of the 20962306a36Sopenharmony_ci * current buffer, need to get another one. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci if (!bp || offset >= geo->blksize) { 21262306a36Sopenharmony_ci if (bp) { 21362306a36Sopenharmony_ci xfs_trans_brelse(sc->tp, bp); 21462306a36Sopenharmony_ci bp = NULL; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff, 21862306a36Sopenharmony_ci &bp); 21962306a36Sopenharmony_ci if (error || !bp) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Find our position in the block. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci offset = geo->data_entry_offset; 22662306a36Sopenharmony_ci curoff += geo->data_entry_offset; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Skip an empty entry. */ 23062306a36Sopenharmony_ci dup = bp->b_addr + offset; 23162306a36Sopenharmony_ci if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 23262306a36Sopenharmony_ci length = be16_to_cpu(dup->length); 23362306a36Sopenharmony_ci offset += length; 23462306a36Sopenharmony_ci curoff += length; 23562306a36Sopenharmony_ci continue; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Otherwise, find the next entry and report it. */ 23962306a36Sopenharmony_ci dep = bp->b_addr + offset; 24062306a36Sopenharmony_ci length = xfs_dir2_data_entsize(mp, dep->namelen); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff; 24362306a36Sopenharmony_ci ino = be64_to_cpu(dep->inumber); 24462306a36Sopenharmony_ci name.name = dep->name; 24562306a36Sopenharmony_ci name.len = dep->namelen; 24662306a36Sopenharmony_ci name.type = xfs_dir2_data_get_ftype(mp, dep); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci error = dirent_fn(sc, dp, dapos, &name, ino, priv); 24962306a36Sopenharmony_ci if (error) 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Advance to the next entry. */ 25362306a36Sopenharmony_ci offset += length; 25462306a36Sopenharmony_ci curoff += length; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (bp) 25862306a36Sopenharmony_ci xfs_trans_brelse(sc->tp, bp); 25962306a36Sopenharmony_ci return error; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Call a function for every entry in a directory. 26462306a36Sopenharmony_ci * 26562306a36Sopenharmony_ci * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ciint 26862306a36Sopenharmony_cixchk_dir_walk( 26962306a36Sopenharmony_ci struct xfs_scrub *sc, 27062306a36Sopenharmony_ci struct xfs_inode *dp, 27162306a36Sopenharmony_ci xchk_dirent_fn dirent_fn, 27262306a36Sopenharmony_ci void *priv) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct xfs_da_args args = { 27562306a36Sopenharmony_ci .dp = dp, 27662306a36Sopenharmony_ci .geo = dp->i_mount->m_dir_geo, 27762306a36Sopenharmony_ci .trans = sc->tp, 27862306a36Sopenharmony_ci }; 27962306a36Sopenharmony_ci bool isblock; 28062306a36Sopenharmony_ci int error; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (xfs_is_shutdown(dp->i_mount)) 28362306a36Sopenharmony_ci return -EIO; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 28662306a36Sopenharmony_ci ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) 28962306a36Sopenharmony_ci return xchk_dir_walk_sf(sc, dp, dirent_fn, priv); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* dir2 functions require that the data fork is loaded */ 29262306a36Sopenharmony_ci error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK); 29362306a36Sopenharmony_ci if (error) 29462306a36Sopenharmony_ci return error; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci error = xfs_dir2_isblock(&args, &isblock); 29762306a36Sopenharmony_ci if (error) 29862306a36Sopenharmony_ci return error; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (isblock) 30162306a36Sopenharmony_ci return xchk_dir_walk_block(sc, dp, dirent_fn, priv); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * Look up the inode number for an exact name in a directory. 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not 31062306a36Sopenharmony_ci * checked for correctness. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ciint 31362306a36Sopenharmony_cixchk_dir_lookup( 31462306a36Sopenharmony_ci struct xfs_scrub *sc, 31562306a36Sopenharmony_ci struct xfs_inode *dp, 31662306a36Sopenharmony_ci const struct xfs_name *name, 31762306a36Sopenharmony_ci xfs_ino_t *ino) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct xfs_da_args args = { 32062306a36Sopenharmony_ci .dp = dp, 32162306a36Sopenharmony_ci .geo = dp->i_mount->m_dir_geo, 32262306a36Sopenharmony_ci .trans = sc->tp, 32362306a36Sopenharmony_ci .name = name->name, 32462306a36Sopenharmony_ci .namelen = name->len, 32562306a36Sopenharmony_ci .filetype = name->type, 32662306a36Sopenharmony_ci .hashval = xfs_dir2_hashname(dp->i_mount, name), 32762306a36Sopenharmony_ci .whichfork = XFS_DATA_FORK, 32862306a36Sopenharmony_ci .op_flags = XFS_DA_OP_OKNOENT, 32962306a36Sopenharmony_ci }; 33062306a36Sopenharmony_ci bool isblock, isleaf; 33162306a36Sopenharmony_ci int error; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (xfs_is_shutdown(dp->i_mount)) 33462306a36Sopenharmony_ci return -EIO; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 33762306a36Sopenharmony_ci ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { 34062306a36Sopenharmony_ci error = xfs_dir2_sf_lookup(&args); 34162306a36Sopenharmony_ci goto out_check_rval; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* dir2 functions require that the data fork is loaded */ 34562306a36Sopenharmony_ci error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK); 34662306a36Sopenharmony_ci if (error) 34762306a36Sopenharmony_ci return error; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci error = xfs_dir2_isblock(&args, &isblock); 35062306a36Sopenharmony_ci if (error) 35162306a36Sopenharmony_ci return error; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (isblock) { 35462306a36Sopenharmony_ci error = xfs_dir2_block_lookup(&args); 35562306a36Sopenharmony_ci goto out_check_rval; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci error = xfs_dir2_isleaf(&args, &isleaf); 35962306a36Sopenharmony_ci if (error) 36062306a36Sopenharmony_ci return error; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (isleaf) { 36362306a36Sopenharmony_ci error = xfs_dir2_leaf_lookup(&args); 36462306a36Sopenharmony_ci goto out_check_rval; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci error = xfs_dir2_node_lookup(&args); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciout_check_rval: 37062306a36Sopenharmony_ci if (error == -EEXIST) 37162306a36Sopenharmony_ci error = 0; 37262306a36Sopenharmony_ci if (!error) 37362306a36Sopenharmony_ci *ino = args.inumber; 37462306a36Sopenharmony_ci return error; 37562306a36Sopenharmony_ci} 376