/* * If we are using the local fork to store a symlink body we need to * zero-terminate it so that we can pass it back to the VFS directly. * Overallocate the in-memory fork by one for that and add a zero * to terminate it below.
*/
zero_terminate = S_ISLNK(VFS_I(ip)->i_mode); if (zero_terminate)
mem_size++;
/* * The file is in-lined in the on-disk inode.
*/ STATICint
xfs_iformat_local( struct xfs_inode *ip, struct xfs_dinode *dip, int whichfork, int size)
{ /* * If the size is unreasonable, then something * is wrong and we just bail out rather than crash in * kmalloc() or memcpy() below.
*/ if (unlikely(size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
xfs_warn(ip->i_mount, "corrupt inode %llu (bad size %d for local fork, size = %zd).",
(unsignedlonglong) ip->i_ino, size,
XFS_DFORK_SIZE(dip, ip->i_mount, whichfork));
xfs_inode_verifier_error(ip, -EFSCORRUPTED, "xfs_iformat_local", dip, sizeof(*dip),
__this_address);
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED;
}
/* * The file consists of a set of extents all of which fit into the on-disk * inode.
*/ STATICint
xfs_iformat_extents( struct xfs_inode *ip, struct xfs_dinode *dip, int whichfork)
{ struct xfs_mount *mp = ip->i_mount; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); int state = xfs_bmap_fork_to_state(whichfork);
xfs_extnum_t nex = xfs_dfork_nextents(dip, whichfork); int size = nex * sizeof(xfs_bmbt_rec_t); struct xfs_iext_cursor icur; struct xfs_bmbt_rec *dp; struct xfs_bmbt_irec new; int i;
/* * If the number of extents is unreasonable, then something is wrong and * we just bail out rather than crash in kmalloc() or memcpy() below.
*/ if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
xfs_warn(ip->i_mount, "corrupt inode %llu ((a)extents = %llu).",
ip->i_ino, nex);
xfs_inode_verifier_error(ip, -EFSCORRUPTED, "xfs_iformat_extents(1)", dip, sizeof(*dip),
__this_address);
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED;
}
/* * The file has too many extents to fit into * the inode, so they are in B-tree format. * Allocate a buffer for the root of the B-tree * and copy the root into it. The i_extents * field will remain NULL until all of the * extents are read in (when they are needed).
*/ STATICint
xfs_iformat_btree( struct xfs_inode *ip, struct xfs_dinode *dip, int whichfork)
{ struct xfs_mount *mp = ip->i_mount;
xfs_bmdr_block_t *dfp; struct xfs_ifork *ifp; struct xfs_btree_block *broot; int nrecs; int size; int level;
/* * blow out if -- fork has less extents than can fit in * fork (fork shouldn't be a btree format), root btree * block has more records than can fit into the fork, * or the number of extents is greater than the number of * blocks.
*/ if (unlikely(ifp->if_nextents <= XFS_IFORK_MAXEXT(ip, whichfork) ||
nrecs == 0 ||
xfs_bmdr_space_calc(nrecs) >
XFS_DFORK_SIZE(dip, mp, whichfork) ||
ifp->if_nextents > ip->i_nblocks) ||
level == 0 || level > XFS_BM_MAXLEVELS(mp, whichfork)) {
xfs_warn(mp, "corrupt inode %llu (btree).",
(unsignedlonglong) ip->i_ino);
xfs_inode_verifier_error(ip, -EFSCORRUPTED, "xfs_iformat_btree", dfp, size,
__this_address);
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED;
}
broot = xfs_broot_alloc(ifp, size); /* * Copy and convert from the on-disk structure * to the in-memory structure.
*/
xfs_bmdr_to_bmbt(ip, dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
broot, size);
int
xfs_iformat_data_fork( struct xfs_inode *ip, struct xfs_dinode *dip)
{ struct inode *inode = VFS_I(ip); int error;
/* * Initialize the extent count early, as the per-format routines may * depend on it. Use release semantics to set needextents /after/ we * set the format. This ensures that we can use acquire semantics on * needextents in xfs_need_iread_extents() and be guaranteed to see a * valid format value after that load.
*/
ip->i_df.if_format = dip->di_format;
ip->i_df.if_nextents = xfs_dfork_data_extents(dip);
smp_store_release(&ip->i_df.if_needextents,
ip->i_df.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0);
switch (inode->i_mode & S_IFMT) { case S_IFIFO: case S_IFCHR: case S_IFBLK: case S_IFSOCK:
ip->i_disk_size = 0;
inode->i_rdev = xfs_to_linux_dev_t(xfs_dinode_get_rdev(dip)); return 0; case S_IFREG: case S_IFLNK: case S_IFDIR: switch (ip->i_df.if_format) { case XFS_DINODE_FMT_LOCAL:
error = xfs_iformat_local(ip, dip, XFS_DATA_FORK,
be64_to_cpu(dip->di_size)); if (!error)
error = xfs_ifork_verify_local_data(ip); return error; case XFS_DINODE_FMT_EXTENTS: return xfs_iformat_extents(ip, dip, XFS_DATA_FORK); case XFS_DINODE_FMT_BTREE: return xfs_iformat_btree(ip, dip, XFS_DATA_FORK); case XFS_DINODE_FMT_META_BTREE: switch (ip->i_metatype) { case XFS_METAFILE_RTRMAP: return xfs_iformat_rtrmap(ip, dip); case XFS_METAFILE_RTREFCOUNT: return xfs_iformat_rtrefcount(ip, dip); default: break;
}
fallthrough; default:
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__,
dip, sizeof(*dip), __this_address);
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED;
} break; default:
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, sizeof(*dip), __this_address);
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED;
}
}
void
xfs_ifork_init_attr( struct xfs_inode *ip, enum xfs_dinode_fmt format,
xfs_extnum_t nextents)
{ /* * Initialize the extent count early, as the per-format routines may * depend on it. Use release semantics to set needextents /after/ we * set the format. This ensures that we can use acquire semantics on * needextents in xfs_need_iread_extents() and be guaranteed to see a * valid format value after that load.
*/
ip->i_af.if_format = format;
ip->i_af.if_nextents = nextents;
smp_store_release(&ip->i_af.if_needextents,
ip->i_af.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0);
}
if (error)
xfs_ifork_zap_attr(ip); return error;
}
/* * Allocate the if_broot component of an inode fork so that it is @new_size * bytes in size, using __GFP_NOLOCKDEP like all the other code that * initializes a broot during inode load. Returns if_broot.
*/ struct xfs_btree_block *
xfs_broot_alloc( struct xfs_ifork *ifp,
size_t new_size)
{
ASSERT(ifp->if_broot == NULL);
/* * Reallocate the if_broot component of an inode fork so that it is @new_size * bytes in size. Returns if_broot.
*/ struct xfs_btree_block *
xfs_broot_realloc( struct xfs_ifork *ifp,
size_t new_size)
{ /* No size change? No action needed. */ if (new_size == ifp->if_broot_bytes) return ifp->if_broot;
/* New size is zero, free it. */ if (new_size == 0) {
ifp->if_broot_bytes = 0;
kfree(ifp->if_broot);
ifp->if_broot = NULL; return NULL;
}
/* * Shrinking the iroot means we allocate a new smaller object and copy * it. We don't trust krealloc not to nop on realloc-down.
*/ if (ifp->if_broot_bytes > 0 && ifp->if_broot_bytes > new_size) { struct xfs_btree_block *old_broot = ifp->if_broot;
/* * Growing the iroot means we can krealloc. This may get us the same * object.
*/
ifp->if_broot = krealloc(ifp->if_broot, new_size,
GFP_KERNEL | __GFP_NOFAIL);
ifp->if_broot_bytes = new_size; return ifp->if_broot;
}
/* * This is called when the amount of space needed for if_data * is increased or decreased. The change in size is indicated by * the number of bytes that need to be added or deleted in the * byte_diff parameter. * * If the amount of space needed has decreased below the size of the * inline buffer, then switch to using the inline buffer. Otherwise, * use krealloc() or kmalloc() to adjust the size of the buffer * to what is needed. * * ip -- the inode whose if_data area is changing * byte_diff -- the change in the number of bytes, positive or negative, * requested for the if_data array.
*/ void *
xfs_idata_realloc( struct xfs_inode *ip,
int64_t byte_diff, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
int64_t new_size = ifp->if_bytes + byte_diff;
/* Free all memory and reset a fork back to its initial state. */ void
xfs_idestroy_fork( struct xfs_ifork *ifp)
{ if (ifp->if_broot != NULL) {
kfree(ifp->if_broot);
ifp->if_broot = NULL;
}
switch (ifp->if_format) { case XFS_DINODE_FMT_LOCAL:
kfree(ifp->if_data);
ifp->if_data = NULL; break; case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE: if (ifp->if_height)
xfs_iext_destroy(ifp); break;
}
}
/* * Convert in-core extents to on-disk form * * In the case of the data fork, the in-core and on-disk fork sizes can be * different due to delayed allocation extents. We only copy on-disk extents * here, so callers must always use the physical fork size to determine the * size of the buffer passed to this routine. We will return the size actually * used.
*/ int
xfs_iextents_copy( struct xfs_inode *ip, struct xfs_bmbt_rec *dp, int whichfork)
{ int state = xfs_bmap_fork_to_state(whichfork); struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_iext_cursor icur; struct xfs_bmbt_irec rec;
int64_t copied = 0;
/* * Each of the following cases stores data into the same region * of the on-disk inode, so only one of them can be valid at * any given time. While it is possible to have conflicting formats * and log flags, e.g. having XFS_ILOG_?DATA set when the fork is * in EXTENTS format, this can only happen when the fork has * changed formats after being modified but before being flushed. * In these cases, the format always takes precedence, because the * format indicates the current state of the fork.
*/ void
xfs_iflush_fork( struct xfs_inode *ip, struct xfs_dinode *dip, struct xfs_inode_log_item *iip, int whichfork)
{ char *cp; struct xfs_ifork *ifp;
xfs_mount_t *mp; staticconstshort brootflag[2] =
{ XFS_ILOG_DBROOT, XFS_ILOG_ABROOT }; staticconstshort dataflag[2] =
{ XFS_ILOG_DDATA, XFS_ILOG_ADATA }; staticconstshort extflag[2] =
{ XFS_ILOG_DEXT, XFS_ILOG_AEXT };
if (!iip) return;
ifp = xfs_ifork_ptr(ip, whichfork); /* * This can happen if we gave up in iformat in an error path, * for the attribute fork.
*/ if (!ifp) {
ASSERT(whichfork == XFS_ATTR_FORK); return;
}
cp = XFS_DFORK_PTR(dip, whichfork);
mp = ip->i_mount; switch (ifp->if_format) { case XFS_DINODE_FMT_LOCAL: if ((iip->ili_fields & dataflag[whichfork]) &&
(ifp->if_bytes > 0)) {
ASSERT(ifp->if_data != NULL);
ASSERT(ifp->if_bytes <= xfs_inode_fork_size(ip, whichfork));
memcpy(cp, ifp->if_data, ifp->if_bytes);
} break;
case XFS_DINODE_FMT_EXTENTS: if ((iip->ili_fields & extflag[whichfork]) &&
(ifp->if_bytes > 0)) {
ASSERT(ifp->if_nextents > 0);
(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
whichfork);
} break;
/* Verify the inline contents of the attr fork of an inode. */ int
xfs_ifork_verify_local_attr( struct xfs_inode *ip)
{ struct xfs_ifork *ifp = &ip->i_af;
xfs_failaddr_t fa;
if (!xfs_inode_has_attr_fork(ip)) {
fa = __this_address;
} else { struct xfs_ifork *ifp = &ip->i_af;
/* * Check if the inode fork supports adding nr_to_add more extents. * * If it doesn't but we can upgrade it to large extent counters, do the upgrade. * If we can't upgrade or are already using big counters but still can't fit the * additional extents, return -EFBIG.
*/ int
xfs_iext_count_extend( struct xfs_trans *tp, struct xfs_inode *ip, int whichfork,
uint nr_to_add)
{ struct xfs_mount *mp = ip->i_mount; bool has_large =
xfs_inode_has_large_extent_counts(ip); struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
uint64_t nr_exts;
ASSERT(nr_to_add <= XFS_MAX_EXTCNT_UPGRADE_NR);
if (whichfork == XFS_COW_FORK) return 0;
/* no point in upgrading if if_nextents overflows */
nr_exts = ifp->if_nextents + nr_to_add; if (nr_exts < ifp->if_nextents) return -EFBIG;
if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_REDUCE_MAX_IEXTENTS) &&
nr_exts > 10) return -EFBIG;
/* Decide if a file mapping is on the realtime device or not. */ bool
xfs_ifork_is_realtime( struct xfs_inode *ip, int whichfork)
{ return XFS_IS_REALTIME_INODE(ip) && whichfork != XFS_ATTR_FORK;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.