/* Assign to the root project by default. */ return 0;
}
/* Propagate di_flags from a parent inode to a child inode. */ staticinlinevoid
xfs_inode_inherit_flags( struct xfs_inode *ip, conststruct xfs_inode *pip)
{ unsignedint di_flags = 0;
xfs_failaddr_t failaddr;
umode_t mode = VFS_I(ip)->i_mode;
if (S_ISDIR(mode)) { if (pip->i_diflags & XFS_DIFLAG_RTINHERIT)
di_flags |= XFS_DIFLAG_RTINHERIT; if (pip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
di_flags |= XFS_DIFLAG_EXTSZINHERIT;
ip->i_extsize = pip->i_extsize;
} if (pip->i_diflags & XFS_DIFLAG_PROJINHERIT)
di_flags |= XFS_DIFLAG_PROJINHERIT;
} elseif (S_ISREG(mode)) { if ((pip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
xfs_has_realtime(ip->i_mount))
di_flags |= XFS_DIFLAG_REALTIME; if (pip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
di_flags |= XFS_DIFLAG_EXTSIZE;
ip->i_extsize = pip->i_extsize;
}
} if ((pip->i_diflags & XFS_DIFLAG_NOATIME) &&
xfs_inherit_noatime)
di_flags |= XFS_DIFLAG_NOATIME; if ((pip->i_diflags & XFS_DIFLAG_NODUMP) &&
xfs_inherit_nodump)
di_flags |= XFS_DIFLAG_NODUMP; if ((pip->i_diflags & XFS_DIFLAG_SYNC) &&
xfs_inherit_sync)
di_flags |= XFS_DIFLAG_SYNC; if ((pip->i_diflags & XFS_DIFLAG_NOSYMLINKS) &&
xfs_inherit_nosymlinks)
di_flags |= XFS_DIFLAG_NOSYMLINKS; if ((pip->i_diflags & XFS_DIFLAG_NODEFRAG) &&
xfs_inherit_nodefrag)
di_flags |= XFS_DIFLAG_NODEFRAG; if (pip->i_diflags & XFS_DIFLAG_FILESTREAM)
di_flags |= XFS_DIFLAG_FILESTREAM;
ip->i_diflags |= di_flags;
/* * Inode verifiers on older kernels only check that the extent size * hint is an integer multiple of the rt extent size on realtime files. * They did not check the hint alignment on a directory with both * rtinherit and extszinherit flags set. If the misaligned hint is * propagated from a directory into a new realtime file, new file * allocations will fail due to math errors in the rt allocator and/or * trip the verifiers. Validate the hint settings in the new file so * that we don't let broken hints propagate.
*/
failaddr = xfs_inode_validate_extsize(ip->i_mount, ip->i_extsize,
VFS_I(ip)->i_mode, ip->i_diflags); if (failaddr) {
ip->i_diflags &= ~(XFS_DIFLAG_EXTSIZE |
XFS_DIFLAG_EXTSZINHERIT);
ip->i_extsize = 0;
}
}
/* Propagate di_flags2 from a parent inode to a child inode. */ staticinlinevoid
xfs_inode_inherit_flags2( struct xfs_inode *ip, conststruct xfs_inode *pip)
{
xfs_failaddr_t failaddr;
if (pip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) {
ip->i_diflags2 |= XFS_DIFLAG2_COWEXTSIZE;
ip->i_cowextsize = pip->i_cowextsize;
} if (pip->i_diflags2 & XFS_DIFLAG2_DAX)
ip->i_diflags2 |= XFS_DIFLAG2_DAX; if (xfs_is_metadir_inode(pip))
ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
/* * If we need to create attributes immediately after allocating the inode, * initialise an empty attribute fork right now. We use the default fork offset * for attributes here as we don't know exactly what size or how many * attributes we might be adding. We can do this safely here because we know * the data fork is completely empty and this saves us from needing to run a * separate transaction to set the fork offset in the immediate future. * * If we have parent pointers and the caller hasn't told us that the file will * never be linked into a directory tree, we /must/ create the attr fork.
*/ staticinlinebool
xfs_icreate_want_attrfork( struct xfs_mount *mp, conststruct xfs_icreate_args *args)
{ if (args->flags & XFS_ICREATE_INIT_XATTRS) returntrue;
if (!(args->flags & XFS_ICREATE_UNLINKABLE) && xfs_has_parent(mp)) returntrue;
if (!args->idmap || pip == NULL) { /* creating a tree root, sb rooted, or detached file */
inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID;
ip->i_projid = 0;
inode->i_mode = args->mode;
} else { /* creating a child in the directory tree */ if (dir && !(dir->i_mode & S_ISGID) && xfs_has_grpid(mp)) {
inode_fsuid_set(inode, args->idmap);
inode->i_gid = dir->i_gid;
inode->i_mode = args->mode;
} else {
inode_init_owner(args->idmap, inode, dir, args->mode);
}
/* * If the group ID of the new file does not match the effective * group ID or one of the supplementary group IDs, the S_ISGID * bit is cleared (and only if the irix_sgid_inherit * compatibility variable is set).
*/ if (irix_sgid_inherit && (inode->i_mode & S_ISGID) &&
!vfsgid_in_group_p(i_gid_into_vfsgid(args->idmap, inode)))
inode->i_mode &= ~S_ISGID;
if (xfs_has_v3inodes(mp)) {
inode_set_iversion(inode, 1); /* also covers the di_used_blocks union arm: */
ip->i_cowextsize = 0;
times |= XFS_ICHGTIME_CREATE;
}
xfs_trans_ichgtime(tp, ip, times);
flags = XFS_ILOG_CORE; switch (args->mode & S_IFMT) { case S_IFIFO: case S_IFCHR: case S_IFBLK: case S_IFSOCK:
ip->i_df.if_format = XFS_DINODE_FMT_DEV;
flags |= XFS_ILOG_DEV; break; case S_IFREG: case S_IFDIR: if (pip && (pip->i_diflags & XFS_DIFLAG_ANY))
xfs_inode_inherit_flags(ip, pip); if (pip && (pip->i_diflags2 & XFS_DIFLAG2_ANY))
xfs_inode_inherit_flags2(ip, pip);
fallthrough; case S_IFLNK:
ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
ip->i_df.if_bytes = 0;
ip->i_df.if_data = NULL; break; default:
ASSERT(0);
}
if (!xfs_has_attr(mp)) {
spin_lock(&mp->m_sb_lock);
xfs_add_attr(mp);
spin_unlock(&mp->m_sb_lock);
xfs_log_sb(tp);
}
}
xfs_trans_log_inode(tp, ip, flags);
}
/* * In-Core Unlinked List Lookups * ============================= * * Every inode is supposed to be reachable from some other piece of metadata * with the exception of the root directory. Inodes with a connection to a * file descriptor but not linked from anywhere in the on-disk directory tree * are collectively known as unlinked inodes, though the filesystem itself * maintains links to these inodes so that on-disk metadata are consistent. * * XFS implements a per-AG on-disk hash table of unlinked inodes. The AGI * header contains a number of buckets that point to an inode, and each inode * record has a pointer to the next inode in the hash chain. This * singly-linked list causes scaling problems in the iunlink remove function * because we must walk that list to find the inode that points to the inode * being removed from the unlinked hash bucket list. * * Hence we keep an in-memory double linked list to link each inode on an * unlinked list. Because there are 64 unlinked lists per AGI, keeping pointer * based lists would require having 64 list heads in the perag, one for each * list. This is expensive in terms of memory (think millions of AGs) and cache * misses on lookups. Instead, use the fact that inodes on the unlinked list * must be referenced at the VFS level to keep them on the list and hence we * have an existence guarantee for inodes on the unlinked list. * * Given we have an existence guarantee, we can use lockless inode cache lookups * to resolve aginos to xfs inodes. This means we only need 8 bytes per inode * for the double linked unlinked list, and we don't need any extra locking to * keep the list safe as all manipulations are done under the AGI buffer lock. * Keeping the list up to date does not require memory allocation, just finding * the XFS inode and updating the next/prev unlinked list aginos.
*/
/* * Update the prev pointer of the next agino. Returns -ENOLINK if the inode * is not in cache.
*/ staticint
xfs_iunlink_update_backref( struct xfs_perag *pag,
xfs_agino_t prev_agino,
xfs_agino_t next_agino)
{ struct xfs_inode *ip;
/* No update necessary if we are at the end of the list. */ if (next_agino == NULLAGINO) return 0;
ip = xfs_iunlink_lookup(pag, next_agino); if (!ip) return -ENOLINK;
ip->i_prev_unlinked = prev_agino; return 0;
}
/* * Point the AGI unlinked bucket at an inode and log the results. The caller * is responsible for validating the old value.
*/ STATICint
xfs_iunlink_update_bucket( struct xfs_trans *tp, struct xfs_perag *pag, struct xfs_buf *agibp, unsignedint bucket_index,
xfs_agino_t new_agino)
{ struct xfs_agi *agi = agibp->b_addr;
xfs_agino_t old_value; int offset;
/* * We should never find the head of the list already set to the value * passed in because either we're adding or removing ourselves from the * head of the list.
*/ if (old_value == new_agino) {
xfs_buf_mark_corrupt(agibp);
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI); return -EFSCORRUPTED;
}
/* * Get the index into the agi hash table for the list this inode will * go on. Make sure the pointer isn't garbage and that this inode * isn't already on the list.
*/
next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); if (next_agino == agino ||
!xfs_verify_agino_or_null(pag, next_agino)) {
xfs_buf_mark_corrupt(agibp);
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI); return -EFSCORRUPTED;
}
/* * Update the prev pointer in the next inode to point back to this * inode.
*/
error = xfs_iunlink_update_backref(pag, agino, next_agino); if (error == -ENOLINK)
error = xfs_iunlink_reload_next(tp, agibp, agino, next_agino); if (error) return error;
if (next_agino != NULLAGINO) { /* * There is already another inode in the bucket, so point this * inode to the current head of the list.
*/
error = xfs_iunlink_log_inode(tp, ip, pag, next_agino); if (error) return error;
ip->i_next_unlinked = next_agino;
}
/* Point the head of the list to point to this inode. */
ip->i_prev_unlinked = NULLAGINO; return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino);
}
/* * This is called when the inode's link count has gone to 0 or we are creating * a tmpfile via O_TMPFILE. The inode @ip must have nlink == 0. * * We place the on-disk inode on a list in the AGI. It will be pulled from this * list when the inode is freed.
*/ int
xfs_iunlink( struct xfs_trans *tp, struct xfs_inode *ip)
{ struct xfs_mount *mp = tp->t_mountp; struct xfs_perag *pag; struct xfs_buf *agibp; int error;
/* * Get the index into the agi hash table for the list this inode will * go on. Make sure the head pointer isn't garbage.
*/
head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); if (!xfs_verify_agino(pag, head_agino)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
agi, sizeof(*agi));
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI); return -EFSCORRUPTED;
}
/* * Set our inode's next_unlinked pointer to NULL and then return * the old pointer value so that we can update whatever was previous * to us in the list to point to whatever was next in the list.
*/
error = xfs_iunlink_log_inode(tp, ip, pag, NULLAGINO); if (error) return error;
/* * Update the prev pointer in the next inode to point back to previous * inode in the chain.
*/
error = xfs_iunlink_update_backref(pag, ip->i_prev_unlinked,
ip->i_next_unlinked); if (error == -ENOLINK)
error = xfs_iunlink_reload_next(tp, agibp, ip->i_prev_unlinked,
ip->i_next_unlinked); if (error) return error;
if (head_agino != agino) { struct xfs_inode *prev_ip;
error = xfs_iunlink_log_inode(tp, prev_ip, pag,
ip->i_next_unlinked);
prev_ip->i_next_unlinked = ip->i_next_unlinked;
} else { /* Point the head of the list to the next unlinked inode. */
error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index,
ip->i_next_unlinked);
}
/* * Decrement the link count on an inode & log the change. If this causes the * link count to go to zero, move the inode to AGI unlinked list so that it can * be freed when the last active reference goes away via xfs_inactive().
*/ int
xfs_droplink( struct xfs_trans *tp, struct xfs_inode *ip)
{ struct inode *inode = VFS_I(ip);
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
if (inode->i_nlink == 0) {
xfs_info_ratelimited(tp->t_mountp, "Inode 0x%llx link count dropped below zero. Pinning link count.",
ip->i_ino);
set_nlink(inode, XFS_NLINK_PINNED);
} if (inode->i_nlink != XFS_NLINK_PINNED)
drop_nlink(inode);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (inode->i_nlink) return 0;
return xfs_iunlink(tp, ip);
}
/* * Increment the link count on an inode & log the change.
*/ void
xfs_bumplink( struct xfs_trans *tp, struct xfs_inode *ip)
{ struct inode *inode = VFS_I(ip);
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
if (inode->i_nlink == XFS_NLINK_PINNED - 1)
xfs_info_ratelimited(tp->t_mountp, "Inode 0x%llx link count exceeded maximum. Pinning link count.",
ip->i_ino); if (inode->i_nlink != XFS_NLINK_PINNED)
inc_nlink(inode);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
/* Free an inode in the ondisk index and zero it out. */ int
xfs_inode_uninit( struct xfs_trans *tp, struct xfs_perag *pag, struct xfs_inode *ip, struct xfs_icluster *xic)
{ struct xfs_mount *mp = ip->i_mount; int error;
/* * Free the inode first so that we guarantee that the AGI lock is going * to be taken before we remove the inode from the unlinked list. This * makes the AGI lock -> unlinked list modification order the same as * used in O_TMPFILE creation.
*/
error = xfs_difree(tp, pag, ip->i_ino, xic); if (error) return error;
error = xfs_iunlink_remove(tp, pag, ip); if (error) return error;
/* * Free any local-format data sitting around before we reset the * data fork to extents format. Note that the attr fork data has * already been freed by xfs_attr_inactive.
*/ if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
kfree(ip->i_df.if_data);
ip->i_df.if_data = NULL;
ip->i_df.if_bytes = 0;
}
VFS_I(ip)->i_mode = 0; /* mark incore inode as free */
ip->i_diflags = 0;
ip->i_diflags2 = mp->m_ino_geo.new_diflags2;
ip->i_forkoff = 0; /* mark the attr fork not in use */
ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
/* * Bump the generation count so no one will be confused * by reincarnations of this inode.
*/
VFS_I(ip)->i_generation++;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.14 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.