/* * Add the new entry the "easy" way. * This is copying the old directory and adding the new entry at the end. * Since it's sorted by "offset" we need room after the last offset * that's already there, and then room to convert to a block directory. * This is already checked by the pick routine. */ static void xfs_dir2_sf_addname_easy( xfs_da_args_t *args, /* operation arguments */ xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */ xfs_dir2_data_aoff_t offset, /* offset to use for new ent */ int new_isize) /* new directory size */ { int byteoff; /* byte offset in sf dir */ xfs_inode_t *dp; /* incore directory inode */ xfs_dir2_sf_t *sfp; /* shortform structure */ dp = args->dp; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; byteoff = (int)((char *)sfep - (char *)sfp); /* * Grow the in-inode space. */ xfs_idata_realloc(dp, XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen), XFS_DATA_FORK); /* * Need to set up again due to realloc of the inode data. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff); /* * Fill in the new entry. */ sfep->namelen = args->namelen; XFS_DIR2_SF_PUT_OFFSET(sfep, offset); memcpy(sfep->name, args->name, sfep->namelen); XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, XFS_DIR2_SF_INUMBERP(sfep)); /* * Update the header and inode. */ sfp->hdr.count++; #if XFS_BIG_INUMS if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) sfp->hdr.i8count++; #endif dp->i_d.di_size = new_isize; xfs_dir2_sf_check(args); }
/* * Check consistency of shortform directory, assert if bad. */ static void xfs_dir2_sf_check( xfs_da_args_t *args) /* operation arguments */ { xfs_inode_t *dp; /* incore directory inode */ int i; /* entry number */ int i8count; /* number of big inode#s */ xfs_ino_t ino; /* entry inode number */ int offset; /* data offset */ xfs_dir2_sf_entry_t *sfep; /* shortform dir entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ dp = args->dp; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; offset = XFS_DIR2_DATA_FIRST_OFFSET; ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); i8count = ino > XFS_DIR2_MAX_SHORT_INUM; for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { ASSERT(XFS_DIR2_SF_GET_OFFSET(sfep) >= offset); ino = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); i8count += ino > XFS_DIR2_MAX_SHORT_INUM; offset = XFS_DIR2_SF_GET_OFFSET(sfep) + XFS_DIR2_DATA_ENTSIZE(sfep->namelen); } ASSERT(i8count == sfp->hdr.i8count); ASSERT(XFS_BIG_INUMS || i8count == 0); ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size); ASSERT(offset + (sfp->hdr.count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + (uint)sizeof(xfs_dir2_block_tail_t) <= dp->i_mount->m_dirblksize); }
xfs_dir2_inou_t * xfs_dir2_sf_inumberp(xfs_dir2_sf_entry_t *sfep) { return XFS_DIR2_SF_INUMBERP(sfep); }
/* * Remove an entry from a shortform directory. */ int /* error */ xfs_dir2_sf_removename( xfs_da_args_t *args) { int byteoff; /* offset of removed entry */ xfs_inode_t *dp; /* incore directory inode */ int entsize; /* this entry's size */ int i; /* shortform entry index */ int newsize; /* new inode size */ int oldsize; /* old inode size */ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_dir2_trace_args("sf_removename", args); dp = args->dp; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); oldsize = (int)dp->i_d.di_size; /* * Bail out if the directory is way too short. */ if (oldsize < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); return XFS_ERROR(EIO); } ASSERT(dp->i_df.if_bytes == oldsize); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(oldsize >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); /* * Loop over the old directory entries. * Find the one we're deleting. */ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(sfep->name, args->name, args->namelen) == 0) { ASSERT(XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)) == args->inumber); break; } } /* * Didn't find it. */ if (i == sfp->hdr.count) { return XFS_ERROR(ENOENT); } /* * Calculate sizes. */ byteoff = (int)((char *)sfep - (char *)sfp); entsize = XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen); newsize = oldsize - entsize; /* * Copy the part if any after the removed entry, sliding it down. */ if (byteoff + entsize < oldsize) memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize, oldsize - (byteoff + entsize)); /* * Fix up the header and file size. */ sfp->hdr.count--; dp->i_d.di_size = newsize; /* * Reallocate, making it smaller. */ xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; #if XFS_BIG_INUMS /* * Are we changing inode number size? */ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) { if (sfp->hdr.i8count == 1) xfs_dir2_sf_toino4(args); else sfp->hdr.i8count--; } #endif xfs_dir2_sf_check(args); xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); return 0; }
/* * Lookup an entry in a shortform directory. * Returns EEXIST if found, ENOENT if not found. */ int /* error */ xfs_dir2_sf_lookup( xfs_da_args_t *args) /* operation arguments */ { xfs_inode_t *dp; /* incore directory inode */ int i; /* entry index */ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_dir2_trace_args("sf_lookup", args); xfs_dir2_sf_check(args); dp = args->dp; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); /* * Bail out if the directory is way too short. */ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); return XFS_ERROR(EIO); } ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); /* * Special case for . */ if (args->namelen == 1 && args->name[0] == '.') { args->inumber = dp->i_ino; return XFS_ERROR(EEXIST); } /* * Special case for .. */ if (args->namelen == 2 && args->name[0] == '.' && args->name[1] == '.') { args->inumber = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); return XFS_ERROR(EEXIST); } /* * Loop over all the entries trying to match ours. */ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(args->name, sfep->name, args->namelen) == 0) { args->inumber = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); return XFS_ERROR(EEXIST); } } /* * Didn't find it. */ ASSERT(args->oknoent); return XFS_ERROR(ENOENT); }
int /* error */ xfs_dir2_sf_getdents( xfs_inode_t *dp, /* incore directory inode */ uio_t *uio, /* caller's buffer control */ int *eofp, /* eof reached? (out) */ xfs_dirent_t *dbp, /* caller's buffer */ xfs_dir2_put_t put) /* abi's formatting function */ { int error; /* error return value */ int i; /* shortform entry number */ xfs_mount_t *mp; /* filesystem mount point */ xfs_dir2_dataptr_t off; /* current entry's offset */ xfs_dir2_put_args_t p; /* arg package for put rtn */ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_off_t dir_offset; mp = dp->i_mount; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); /* * Give up if the directory is way too short. */ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); return XFS_ERROR(EIO); } dir_offset = uio->uio_offset; ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); /* * If the block number in the offset is out of range, we're done. */ if (XFS_DIR2_DATAPTR_TO_DB(mp, dir_offset) > mp->m_dirdatablk) { *eofp = 1; return 0; } /* * Set up putargs structure. */ p.dbp = dbp; p.put = put; p.uio = uio; /* * Put . entry unless we're starting past it. */ if (dir_offset <= XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOT_OFFSET)) { p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, 0, XFS_DIR2_DATA_DOTDOT_OFFSET); p.ino = dp->i_ino; #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif p.name = "."; p.namelen = 1; error = p.put(&p); if (!p.done) { uio->uio_offset = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOT_OFFSET); return error; } } /* * Put .. entry unless we're starting past it. */ if (dir_offset <= XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOTDOT_OFFSET)) { p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_DATA_FIRST_OFFSET); p.ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif p.name = ".."; p.namelen = 2; error = p.put(&p); if (!p.done) { uio->uio_offset = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOTDOT_OFFSET); return error; } } /* * Loop while there are more entries and put'ing works. */ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { off = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_SF_GET_OFFSET(sfep)); if (dir_offset > off) continue; p.namelen = sfep->namelen; p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, XFS_DIR2_SF_GET_OFFSET(sfep) + XFS_DIR2_DATA_ENTSIZE(p.namelen)); p.ino = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif p.name = (char *)sfep->name; error = p.put(&p); if (!p.done) { uio->uio_offset = off; return error; } } /* * They all fit. */ *eofp = 1; uio->uio_offset = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk + 1, 0); return 0; }
/* ARGSUSED */ static void xfs_dir2_sf_addname_hard( xfs_da_args_t *args, /* operation arguments */ int objchange, /* changing inode number size */ int new_isize) /* new directory size */ { int add_datasize; /* data size need for new ent */ char *buf; /* buffer for old */ xfs_inode_t *dp; /* incore directory inode */ int eof; /* reached end of old dir */ int nbytes; /* temp for byte copies */ xfs_dir2_data_aoff_t new_offset; /* next offset value */ xfs_dir2_data_aoff_t offset; /* current offset value */ int old_isize; /* previous di_size */ xfs_dir2_sf_entry_t *oldsfep; /* entry in original dir */ xfs_dir2_sf_t *oldsfp; /* original shortform dir */ xfs_dir2_sf_entry_t *sfep; /* entry in new dir */ xfs_dir2_sf_t *sfp; /* new shortform dir */ /* * Copy the old directory to the stack buffer. */ dp = args->dp; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; old_isize = (int)dp->i_d.di_size; buf = kmem_alloc(old_isize, KM_SLEEP); oldsfp = (xfs_dir2_sf_t *)buf; memcpy(oldsfp, sfp, old_isize); /* * Loop over the old directory finding the place we're going * to insert the new entry. * If it's going to end up at the end then oldsfep will point there. */ for (offset = XFS_DIR2_DATA_FIRST_OFFSET, oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp), add_datasize = XFS_DIR2_DATA_ENTSIZE(args->namelen), eof = (char *)oldsfep == &buf[old_isize]; !eof; offset = new_offset + XFS_DIR2_DATA_ENTSIZE(oldsfep->namelen), oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep), eof = (char *)oldsfep == &buf[old_isize]) { new_offset = XFS_DIR2_SF_GET_OFFSET(oldsfep); if (offset + add_datasize <= new_offset) break; } /* * Get rid of the old directory, then allocate space for * the new one. We do this so xfs_idata_realloc won't copy * the data. */ xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK); xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK); /* * Reset the pointer since the buffer was reallocated. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; /* * Copy the first part of the directory, including the header. */ nbytes = (int)((char *)oldsfep - (char *)oldsfp); memcpy(sfp, oldsfp, nbytes); sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes); /* * Fill in the new entry, and update the header counts. */ sfep->namelen = args->namelen; XFS_DIR2_SF_PUT_OFFSET(sfep, offset); memcpy(sfep->name, args->name, sfep->namelen); XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, XFS_DIR2_SF_INUMBERP(sfep)); sfp->hdr.count++; #if XFS_BIG_INUMS if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange) sfp->hdr.i8count++; #endif /* * If there's more left to copy, do that. */ if (!eof) { sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); memcpy(sfep, oldsfep, old_isize - nbytes); } kmem_free(buf, old_isize); dp->i_d.di_size = new_isize; xfs_dir2_sf_check(args); }
/* * Convert a block format directory to shortform. * Caller has already checked that it will fit, and built us a header. */ int /* error */ xfs_dir2_block_to_sf( xfs_da_args_t *args, /* operation arguments */ xfs_dabuf_t *bp, /* block buffer */ int size, /* shortform directory size */ xfs_dir2_sf_hdr_t *sfhp) /* shortform directory hdr */ { xfs_dir2_block_t *block; /* block structure */ xfs_dir2_block_tail_t *btp; /* block tail pointer */ xfs_dir2_data_entry_t *dep; /* data entry pointer */ xfs_inode_t *dp; /* incore directory inode */ xfs_dir2_data_unused_t *dup; /* unused data pointer */ char *endptr; /* end of data entries */ int error; /* error return value */ int logflags; /* inode logging flags */ xfs_mount_t *mp; /* filesystem mount point */ char *ptr; /* current data pointer */ xfs_dir2_sf_entry_t *sfep; /* shortform entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_ino_t temp; xfs_dir2_trace_args_sb("block_to_sf", args, size, bp); dp = args->dp; mp = dp->i_mount; /* * Make a copy of the block data, so we can shrink the inode * and add local data. */ block = kmem_alloc(mp->m_dirblksize, KM_SLEEP); memcpy(block, bp->data, mp->m_dirblksize); logflags = XFS_ILOG_CORE; if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) { ASSERT(error != ENOSPC); goto out; } /* * The buffer is now unconditionally gone, whether * xfs_dir2_shrink_inode worked or not. * * Convert the inode to local format. */ dp->i_df.if_flags &= ~XFS_IFEXTENTS; dp->i_df.if_flags |= XFS_IFINLINE; dp->i_d.di_format = XFS_DINODE_FMT_LOCAL; ASSERT(dp->i_df.if_bytes == 0); xfs_idata_realloc(dp, size, XFS_DATA_FORK); logflags |= XFS_ILOG_DDATA; /* * Copy the header into the newly allocate local space. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; memcpy(sfp, sfhp, XFS_DIR2_SF_HDR_SIZE(sfhp->i8count)); dp->i_d.di_size = size; /* * Set up to loop over the block's entries. */ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); ptr = (char *)block->u; endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp); sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); /* * Loop over the active and unused entries. * Stop when we reach the leaf/tail portion of the block. */ while (ptr < endptr) { /* * If it's unused, just skip over it. */ dup = (xfs_dir2_data_unused_t *)ptr; if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) { ptr += INT_GET(dup->length, ARCH_CONVERT); continue; } dep = (xfs_dir2_data_entry_t *)ptr; /* * Skip . */ if (dep->namelen == 1 && dep->name[0] == '.') ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == dp->i_ino); /* * Skip .., but make sure the inode number is right. */ else if (dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.') ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); /* * Normal entry, copy it into shortform. */ else { sfep->namelen = dep->namelen; XFS_DIR2_SF_PUT_OFFSET(sfep, (xfs_dir2_data_aoff_t) ((char *)dep - (char *)block)); memcpy(sfep->name, dep->name, dep->namelen); temp=INT_GET(dep->inumber, ARCH_CONVERT); XFS_DIR2_SF_PUT_INUMBER(sfp, &temp, XFS_DIR2_SF_INUMBERP(sfep)); sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); } ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen); } ASSERT((char *)sfep - (char *)sfp == size); xfs_dir2_sf_check(args); out: xfs_trans_log_inode(args->trans, dp, logflags); kmem_free(block, mp->m_dirblksize); return error; }
/* * Convert from 4-byte inode numbers to 8-byte inode numbers. * The new 8-byte inode number is not there yet, we leave with the * count 1 but no corresponding entry. */ static void xfs_dir2_sf_toino8( xfs_da_args_t *args) /* operation arguments */ { char *buf; /* old dir's buffer */ xfs_inode_t *dp; /* incore directory inode */ int i; /* entry index */ xfs_ino_t ino; /* entry inode number */ int newsize; /* new inode size */ xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ xfs_dir2_sf_t *oldsfp; /* old sf directory */ int oldsize; /* old inode size */ xfs_dir2_sf_entry_t *sfep; /* new sf entry */ xfs_dir2_sf_t *sfp; /* new sf directory */ xfs_dir2_trace_args("sf_toino8", args); dp = args->dp; /* * Copy the old directory to the buffer. * Then nuke it from the inode, and add the new buffer to the inode. * Don't want xfs_idata_realloc copying the data here. */ oldsize = dp->i_df.if_bytes; buf = kmem_alloc(oldsize, KM_SLEEP); oldsfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(oldsfp->hdr.i8count == 0); memcpy(buf, oldsfp, oldsize); /* * Compute the new inode size. */ newsize = oldsize + (oldsfp->hdr.count + 1) * ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)); xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); /* * Reset our pointers, the data has moved. */ oldsfp = (xfs_dir2_sf_t *)buf; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; /* * Fill in the new header. */ sfp->hdr.count = oldsfp->hdr.count; sfp->hdr.i8count = 1; ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, &oldsfp->hdr.parent); XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, &sfp->hdr.parent); /* * Copy the entries field by field. */ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp), oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep), oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep)) { sfep->namelen = oldsfep->namelen; sfep->offset = oldsfep->offset; memcpy(sfep->name, oldsfep->name, sfep->namelen); ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, XFS_DIR2_SF_INUMBERP(oldsfep)); XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, XFS_DIR2_SF_INUMBERP(sfep)); } /* * Clean up the inode. */ kmem_free(buf, oldsize); dp->i_d.di_size = newsize; xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); }
/* * Replace the inode number of an entry in a shortform directory. */ int /* error */ xfs_dir2_sf_replace( xfs_da_args_t *args) /* operation arguments */ { xfs_inode_t *dp; /* incore directory inode */ int i; /* entry index */ #if XFS_BIG_INUMS || defined(DEBUG) xfs_ino_t ino=0; /* entry old inode number */ #endif #if XFS_BIG_INUMS int i8elevated; /* sf_toino8 set i8count=1 */ #endif xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_dir2_trace_args("sf_replace", args); dp = args->dp; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); /* * Bail out if the shortform directory is way too small. */ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); return XFS_ERROR(EIO); } ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); #if XFS_BIG_INUMS /* * New inode number is large, and need to convert to 8-byte inodes. */ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->hdr.i8count == 0) { int error; /* error return value */ int newsize; /* new inode size */ newsize = dp->i_df.if_bytes + (sfp->hdr.count + 1) * ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)); /* * Won't fit as shortform, convert to block then do replace. */ if (newsize > XFS_IFORK_DSIZE(dp)) { error = xfs_dir2_sf_to_block(args); if (error) { return error; } return xfs_dir2_block_replace(args); } /* * Still fits, convert to 8-byte now. */ xfs_dir2_sf_toino8(args); i8elevated = 1; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; } else i8elevated = 0; #endif ASSERT(args->namelen != 1 || args->name[0] != '.'); /* * Replace ..'s entry. */ if (args->namelen == 2 && args->name[0] == '.' && args->name[1] == '.') { #if XFS_BIG_INUMS || defined(DEBUG) ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); ASSERT(args->inumber != ino); #endif XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, &sfp->hdr.parent); } /* * Normal entry, look for the name. */ else { for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); i < sfp->hdr.count; i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(args->name, sfep->name, args->namelen) == 0) { #if XFS_BIG_INUMS || defined(DEBUG) ino = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); ASSERT(args->inumber != ino); #endif XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, XFS_DIR2_SF_INUMBERP(sfep)); break; } } /* * Didn't find it. */ if (i == sfp->hdr.count) { ASSERT(args->oknoent); #if XFS_BIG_INUMS if (i8elevated) xfs_dir2_sf_toino4(args); #endif return XFS_ERROR(ENOENT); } } #if XFS_BIG_INUMS /* * See if the old number was large, the new number is small. */ if (ino > XFS_DIR2_MAX_SHORT_INUM && args->inumber <= XFS_DIR2_MAX_SHORT_INUM) { /* * And the old count was one, so need to convert to small. */ if (sfp->hdr.i8count == 1) xfs_dir2_sf_toino4(args); else sfp->hdr.i8count--; } /* * See if the old number was small, the new number is large. */ if (ino <= XFS_DIR2_MAX_SHORT_INUM && args->inumber > XFS_DIR2_MAX_SHORT_INUM) { /* * add to the i8count unless we just converted to 8-byte * inodes (which does an implied i8count = 1) */ ASSERT(sfp->hdr.i8count != 0); if (!i8elevated) sfp->hdr.i8count++; } #endif xfs_dir2_sf_check(args); xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); return 0; }