/* * The Orphanage * ============= * * If the directory tree is damaged, children of that directory become * inaccessible via that file path. If a child has no other parents, the file * is said to be orphaned. xfs_repair fixes this situation by creating a * orphanage directory (specifically, /lost+found) and creating a directory * entry pointing to the orphaned file. * * Online repair follows this tactic by creating a root-owned /lost+found * directory if one does not exist. If an orphan is found, it will move that * files into orphanage.
*/
/* * Always clear setuid/setgid/sticky on the orphanage since we don't * normally want that functionality on this directory and xfs_repair * doesn't create it this way either. Leave the other access bits * unchanged.
*/
inode->i_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
/* * Change the ownerships and register quota modifications * in the transaction.
*/ if (!uid_eq(inode->i_uid, GLOBAL_ROOT_UID)) { if (XFS_IS_UQUOTA_ON(mp))
oldu = xfs_qm_vop_chown(tp, dp, &dp->i_udquot, udqp);
inode->i_uid = GLOBAL_ROOT_UID;
} if (!gid_eq(inode->i_gid, GLOBAL_ROOT_GID)) { if (XFS_IS_GQUOTA_ON(mp))
oldg = xfs_qm_vop_chown(tp, dp, &dp->i_gdquot, gdqp);
inode->i_gid = GLOBAL_ROOT_GID;
} if (dp->i_projid != 0) { if (XFS_IS_PQUOTA_ON(mp))
oldp = xfs_qm_vop_chown(tp, dp, &dp->i_pdquot, pdqp);
dp->i_projid = 0;
}
/* Find the dentry for the root directory... */
root_dentry = d_find_alias(root_inode); if (!root_dentry) {
error = -EFSCORRUPTED; goto out;
}
/* ...which is a directory, right? */ if (!d_is_dir(root_dentry)) {
error = -EFSCORRUPTED; goto out_dput_root;
}
/* Try to find the orphanage directory. */
inode_lock_nested(root_inode, I_MUTEX_PARENT);
orphanage_dentry = lookup_noperm(&QSTR(ORPHANAGE), root_dentry); if (IS_ERR(orphanage_dentry)) {
error = PTR_ERR(orphanage_dentry); goto out_unlock_root;
}
/* * Nothing found? Call mkdir to create the orphanage. Create the * directory without other-user access because we're live and someone * could have been relying partly on minimal access to a parent * directory to control access to a file we put in here.
*/ if (d_really_is_negative(orphanage_dentry)) {
orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
orphanage_dentry, 0750);
error = PTR_ERR(orphanage_dentry); if (IS_ERR(orphanage_dentry)) goto out_unlock_root;
}
/* Not a directory? Bail out. */ if (!d_is_dir(orphanage_dentry)) {
error = -ENOTDIR; goto out_dput_orphanage;
}
/* * Grab a reference to the orphanage. This /should/ succeed since * we hold the root directory locked and therefore nobody can delete * the orphanage.
*/
orphanage_inode = igrab(d_inode(orphanage_dentry)); if (!orphanage_inode) {
error = -ENOENT; goto out_dput_orphanage;
}
/* Make sure the orphanage is owned by root. */
error = xrep_chown_orphanage(sc, XFS_I(orphanage_inode)); if (error) goto out_dput_orphanage;
/* Stash the reference for later and bail out. */
sc->orphanage = XFS_I(orphanage_inode);
sc->orphanage_ilock_flags = 0;
/* Grab the IOLOCK of the orphanage and sc->ip. */ int
xrep_orphanage_iolock_two( struct xfs_scrub *sc)
{ int error = 0;
while (true) { if (xchk_should_terminate(sc, &error)) return error;
/* * Normal XFS takes the IOLOCK before grabbing a transaction. * Scrub holds a transaction, which means that we can't block * on either IOLOCK.
*/ if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) { if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL)) break;
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
}
delay(1);
}
return 0;
}
/* Release the orphanage. */ void
xrep_orphanage_rele( struct xfs_scrub *sc)
{ if (!sc->orphanage) return;
if (sc->orphanage_ilock_flags)
xfs_iunlock(sc->orphanage, sc->orphanage_ilock_flags);
/* Can the orphanage adopt @sc->ip? */ bool
xrep_orphanage_can_adopt( struct xfs_scrub *sc)
{
ASSERT(sc->ip != NULL);
if (!sc->orphanage) returnfalse; if (sc->ip == sc->orphanage) returnfalse; if (xchk_inode_is_sb_rooted(sc->ip)) returnfalse; if (xfs_is_internal_inode(sc->ip)) returnfalse; returntrue;
}
/* * Create a new transaction to send a child to the orphanage. * * Allocate a new transaction with sufficient disk space to handle the * adoption, take ILOCK_EXCL of the orphanage and sc->ip, joins them to the * transaction, and reserve quota to reparent the latter. Caller must hold the * IOLOCK of the orphanage and sc->ip.
*/ int
xrep_adoption_trans_alloc( struct xfs_scrub *sc, struct xrep_adoption *adopt)
{ struct xfs_mount *mp = sc->mp; unsignedint child_blkres = 0; int error;
/* Compute the worst case space reservation that we need. */
adopt->sc = sc;
adopt->orphanage_blkres = xfs_link_space_res(mp, MAXNAMELEN); if (S_ISDIR(VFS_I(sc->ip)->i_mode))
child_blkres = xfs_rename_space_res(mp, 0, false,
xfs_name_dotdot.len, false); if (xfs_has_parent(mp))
child_blkres += XFS_ADDAFORK_SPACE_RES(mp);
adopt->child_blkres = child_blkres;
/* * Allocate a transaction to link the child into the parent, along with * enough disk space to handle expansion of both the orphanage and the * dotdot entry of a child directory.
*/
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link,
adopt->orphanage_blkres + adopt->child_blkres, 0, 0,
&sc->tp); if (error) return error;
/* * Reserve enough quota in the orphan directory to add the new name. * Normally the orphanage should have user/group/project ids of zero * and hence is not subject to quota enforcement, but we're allowed to * exceed quota to reattach disconnected parts of the directory tree.
*/
error = xfs_trans_reserve_quota_nblks(sc->tp, sc->orphanage,
adopt->orphanage_blkres, 0, true); if (error) goto out_cancel;
/* * Reserve enough quota in the child directory to change dotdot. * Here we're also allowed to exceed file quota to repair inconsistent * metadata.
*/ if (adopt->child_blkres) {
error = xfs_trans_reserve_quota_nblks(sc->tp, sc->ip,
adopt->child_blkres, 0, true); if (error) goto out_cancel;
}
/* * Compute the xfs_name for the directory entry that we're adding to the * orphanage. Caller must hold ILOCKs of sc->ip and the orphanage and must not * reuse namebuf until the adoption completes or is dissolved.
*/ int
xrep_adoption_compute_name( struct xrep_adoption *adopt, struct xfs_name *xname)
{ struct xfs_scrub *sc = adopt->sc; char *namebuf = (void *)xname->name;
xfs_ino_t ino; unsignedint incr = 0; int error = 0;
/* Make sure the filename is unique in the lost+found. */
error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino); while (error == 0 && incr < 10000) {
xname->len = snprintf(namebuf, MAXNAMELEN, "%llu.%u",
sc->ip->i_ino, ++incr);
error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino);
} if (error == 0) { /* We already have 10,000 entries in the orphanage? */ return -EFSCORRUPTED;
}
if (error != -ENOENT) return error; return 0;
}
/* * Make sure the dcache does not have a positive dentry for the name we've * chosen. The caller should have checked with the ondisk directory, so any * discrepancy is a sign that something is seriously wrong.
*/ staticint
xrep_adoption_check_dcache( struct xrep_adoption *adopt)
{ struct qstr qname = QSTR_INIT(adopt->xname->name,
adopt->xname->len); struct xfs_scrub *sc = adopt->sc; struct dentry *d_orphanage, *d_child; int error = 0;
d_orphanage = d_find_alias(VFS_I(sc->orphanage)); if (!d_orphanage) return 0;
d_child = try_lookup_noperm(&qname, d_orphanage); if (d_child) {
trace_xrep_adoption_check_child(sc->mp, d_child);
if (d_is_positive(d_child)) {
ASSERT(d_is_negative(d_child));
error = -EFSCORRUPTED;
}
dput(d_child);
}
dput(d_orphanage); return error;
}
/* * Invalidate all dentries for the name that was added to the orphanage * directory, and all dentries pointing to the child inode that was moved. * * There should not be any positive entries for the name, since we've * maintained our lock on the orphanage directory.
*/ staticvoid
xrep_adoption_zap_dcache( struct xrep_adoption *adopt)
{ struct qstr qname = QSTR_INIT(adopt->xname->name,
adopt->xname->len); struct xfs_scrub *sc = adopt->sc; struct dentry *d_orphanage, *d_child;
/* Invalidate all dentries for the adoption name */
d_orphanage = d_find_alias(VFS_I(sc->orphanage)); if (!d_orphanage) return;
/* Invalidate all the dentries pointing down to this file. */ while ((d_child = d_find_alias(VFS_I(sc->ip))) != NULL) {
trace_xrep_adoption_invalidate_child(sc->mp, d_child);
d_invalidate(d_child);
dput(d_child);
}
}
/* * If we have to add an attr fork ahead of a parent pointer update, how much * space should we ask for?
*/ staticinlineint
xrep_adoption_attr_sizeof( conststruct xrep_adoption *adopt)
{ returnsizeof(struct xfs_attr_sf_hdr) +
xfs_attr_sf_entsize_byname(sizeof(struct xfs_parent_rec),
adopt->xname->len);
}
/* * Move the current file to the orphanage under the computed name. * * Returns with a dirty transaction so that the caller can handle any other * work, such as fixing up unlinked lists or resetting link counts.
*/ int
xrep_adoption_move( struct xrep_adoption *adopt)
{ struct xfs_scrub *sc = adopt->sc; bool isdir = S_ISDIR(VFS_I(sc->ip)->i_mode); int error;
error = xrep_adoption_check_dcache(adopt); if (error) return error;
/* * If this filesystem has parent pointers, ensure that the file being * moved to the orphanage has an attribute fork. This is required * because the parent pointer code does not itself add attr forks.
*/ if (!xfs_inode_has_attr_fork(sc->ip) && xfs_has_parent(sc->mp)) { int sf_size = xrep_adoption_attr_sizeof(adopt);
/* Create the new name in the orphanage. */
error = xfs_dir_createname(sc->tp, sc->orphanage, adopt->xname,
sc->ip->i_ino, adopt->orphanage_blkres); if (error) return error;
/* * Bump the link count of the orphanage if we just added a * subdirectory, and update its timestamps.
*/
xfs_trans_ichgtime(sc->tp, sc->orphanage,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); if (isdir)
xfs_bumplink(sc->tp, sc->orphanage);
xfs_trans_log_inode(sc->tp, sc->orphanage, XFS_ILOG_CORE);
/* Bump the link count of the child. */ if (adopt->bump_child_nlink) {
xfs_bumplink(sc->tp, sc->ip);
xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
}
/* Replace the dotdot entry if the child is a subdirectory. */ if (isdir) {
error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
sc->orphanage->i_ino, adopt->child_blkres); if (error) return error;
}
/* Add a parent pointer from the file back to the lost+found. */ if (xfs_has_parent(sc->mp)) {
error = xfs_parent_addname(sc->tp, &adopt->ppargs,
sc->orphanage, adopt->xname, sc->ip); if (error) return error;
}
/* * Notify dirent hooks that we moved the file to /lost+found, and * finish all the deferred work so that we know the adoption is fully * recorded in the log.
*/
xfs_dir_update_hook(sc->orphanage, sc->ip, 1, adopt->xname);
/* Remove negative dentries from the lost+found's dcache */
xrep_adoption_zap_dcache(adopt); return 0;
}
/* * Roll to a clean scrub transaction so that we can release the orphanage, * even if xrep_adoption_move was not called. * * Commits all the work and deferred ops attached to an adoption request and * rolls to a clean scrub transaction. On success, returns 0 with the scrub * context holding a clean transaction with no inodes joined. On failure, * returns negative errno with no scrub transaction. All inode locks are * still held after this function returns.
*/ int
xrep_adoption_trans_roll( struct xrep_adoption *adopt)
{ struct xfs_scrub *sc = adopt->sc; int error;
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.