| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (C) 2021 Oracle. All Rights Reserved. |
| * Author: Darrick J. Wong <djwong@kernel.org> |
| */ |
| #include "libxfs_priv.h" |
| #include "xfs_fs.h" |
| #include "xfs_shared.h" |
| #include "xfs_format.h" |
| #include "xfs_log_format.h" |
| #include "xfs_trans_resv.h" |
| #include "xfs_bit.h" |
| #include "xfs_sb.h" |
| #include "xfs_mount.h" |
| #include "xfs_defer.h" |
| #include "xfs_trans.h" |
| #include "xfs_imeta.h" |
| #include "xfs_trace.h" |
| #include "xfs_inode.h" |
| #include "xfs_ialloc.h" |
| #include "xfs_bmap_btree.h" |
| #include "xfs_da_format.h" |
| #include "xfs_da_btree.h" |
| #include "xfs_trans_space.h" |
| #include "xfs_dir2.h" |
| #include "xfs_ag.h" |
| |
| /* |
| * Metadata Inode Number Management |
| * ================================ |
| * |
| * These functions provide an abstraction layer for looking up, creating, and |
| * deleting metadata inodes. These pointers live in the in-core superblock, |
| * so the functions moderate access to those fields and take care of logging. |
| * |
| * For the five existing metadata inodes (real time bitmap & summary; and the |
| * user, group, and quotas) we'll continue to maintain the in-core superblock |
| * inodes for reads and only require xfs_imeta_create and xfs_imeta_unlink to |
| * persist changes. New metadata inode types must only use the xfs_imeta_* |
| * functions. |
| * |
| * Callers wishing to create or unlink a metadata inode must pass in a |
| * xfs_imeta_end structure. After committing or cancelling the transaction, |
| * this structure must be passed to xfs_imeta_end_update to free resources that |
| * cannot be freed during the transaction. |
| * |
| * When the metadata directory tree (metadir) feature is enabled, we can create |
| * a complex directory tree in which to store metadata inodes. Inodes within |
| * the metadata directory tree should have the "metadata" inode flag set to |
| * prevent them from being exposed to the outside world. |
| * |
| * Callers are expected to take the ILOCK of metadata inodes to synchronize |
| * access to the file. This includes directory operations. It is not |
| * necessary to take the IOLOCK or MMAPLOCK since metadata inodes should never |
| * be exposed to user space. |
| */ |
| |
| /* Static metadata inode paths */ |
| static const char *rtbitmap_path[] = {"realtime", "0.bitmap"}; |
| static const char *rtsummary_path[] = {"realtime", "0.summary"}; |
| static const char *usrquota_path[] = {"quota", "user"}; |
| static const char *grpquota_path[] = {"quota", "group"}; |
| static const char *prjquota_path[] = {"quota", "project"}; |
| |
| XFS_IMETA_DEFINE_PATH(XFS_IMETA_RTBITMAP, rtbitmap_path); |
| XFS_IMETA_DEFINE_PATH(XFS_IMETA_RTSUMMARY, rtsummary_path); |
| XFS_IMETA_DEFINE_PATH(XFS_IMETA_USRQUOTA, usrquota_path); |
| XFS_IMETA_DEFINE_PATH(XFS_IMETA_GRPQUOTA, grpquota_path); |
| XFS_IMETA_DEFINE_PATH(XFS_IMETA_PRJQUOTA, prjquota_path); |
| |
| const struct xfs_imeta_path XFS_IMETA_METADIR = { |
| .im_depth = 0, |
| .im_ftype = XFS_DIR3_FT_DIR, |
| }; |
| |
| /* Are these two paths equal? */ |
| STATIC bool |
| xfs_imeta_path_compare( |
| const struct xfs_imeta_path *a, |
| const struct xfs_imeta_path *b) |
| { |
| unsigned int i; |
| |
| if (a == b) |
| return true; |
| |
| if (a->im_depth != b->im_depth) |
| return false; |
| |
| for (i = 0; i < a->im_depth; i++) |
| if (a->im_path[i] != b->im_path[i] && |
| strcmp(a->im_path[i], b->im_path[i])) |
| return false; |
| |
| return true; |
| } |
| |
| /* Is this path ok? */ |
| static inline bool |
| xfs_imeta_path_check( |
| const struct xfs_imeta_path *path) |
| { |
| return path->im_depth <= XFS_IMETA_MAX_DEPTH; |
| } |
| |
| /* Functions for storing and retrieving superblock inode values. */ |
| |
| /* Mapping of metadata inode paths to in-core superblock values. */ |
| static const struct xfs_imeta_sbmap { |
| const struct xfs_imeta_path *path; |
| unsigned int offset; |
| } xfs_imeta_sbmaps[] = { |
| { |
| .path = &XFS_IMETA_RTBITMAP, |
| .offset = offsetof(struct xfs_sb, sb_rbmino), |
| }, |
| { |
| .path = &XFS_IMETA_RTSUMMARY, |
| .offset = offsetof(struct xfs_sb, sb_rsumino), |
| }, |
| { |
| .path = &XFS_IMETA_USRQUOTA, |
| .offset = offsetof(struct xfs_sb, sb_uquotino), |
| }, |
| { |
| .path = &XFS_IMETA_GRPQUOTA, |
| .offset = offsetof(struct xfs_sb, sb_gquotino), |
| }, |
| { |
| .path = &XFS_IMETA_PRJQUOTA, |
| .offset = offsetof(struct xfs_sb, sb_pquotino), |
| }, |
| { |
| .path = &XFS_IMETA_METADIR, |
| .offset = offsetof(struct xfs_sb, sb_metadirino), |
| }, |
| { NULL, 0 }, |
| }; |
| |
| /* Return a pointer to the in-core superblock inode value. */ |
| static inline xfs_ino_t * |
| xfs_imeta_sbmap_to_inop( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_sbmap *map) |
| { |
| return (xfs_ino_t *)(((char *)&mp->m_sb) + map->offset); |
| } |
| |
| /* Compute location of metadata inode pointer in the in-core superblock */ |
| static inline xfs_ino_t * |
| xfs_imeta_path_to_sb_inop( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path) |
| { |
| const struct xfs_imeta_sbmap *p; |
| |
| for (p = xfs_imeta_sbmaps; p->path; p++) |
| if (xfs_imeta_path_compare(p->path, path)) |
| return xfs_imeta_sbmap_to_inop(mp, p); |
| |
| return NULL; |
| } |
| |
| /* Look up a superblock metadata inode by its path. */ |
| STATIC int |
| xfs_imeta_sb_lookup( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path, |
| xfs_ino_t *inop) |
| { |
| xfs_ino_t *sb_inop; |
| |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (!sb_inop) |
| return -EINVAL; |
| |
| trace_xfs_imeta_sb_lookup(mp, sb_inop); |
| *inop = *sb_inop; |
| return 0; |
| } |
| |
| /* |
| * Create a new metadata inode and set a superblock pointer to this new inode. |
| * The superblock field must not already be pointing to an inode. |
| */ |
| STATIC int |
| xfs_imeta_sb_create( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| umode_t mode, |
| unsigned int flags, |
| struct xfs_inode **ipp) |
| { |
| struct xfs_icreate_args args = { |
| .nlink = S_ISDIR(mode) ? 2 : 1, |
| }; |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| xfs_ino_t *sb_inop; |
| xfs_ino_t ino; |
| int error; |
| |
| xfs_icreate_args_rootfile(&args, mode); |
| |
| /* Reject if the sb already points to some inode. */ |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (!sb_inop) |
| return -EINVAL; |
| |
| if (*sb_inop != NULLFSINO) |
| return -EEXIST; |
| |
| /* Create a new inode and set the sb pointer. */ |
| error = xfs_dialloc(tpp, NULL, mode, &ino); |
| if (error) |
| return error; |
| error = xfs_icreate(*tpp, ino, &args, ipp); |
| if (error) |
| return error; |
| |
| /* Attach dquots to this file. Caller should have allocated them! */ |
| if (!(flags & XFS_IMETA_CREATE_NOQUOTA)) { |
| error = xfs_qm_dqattach_locked(*ipp, false); |
| if (error) |
| return error; |
| xfs_trans_mod_dquot_byino(*tpp, *ipp, XFS_TRANS_DQ_ICOUNT, 1); |
| } |
| |
| /* Update superblock pointer. */ |
| *sb_inop = ino; |
| trace_xfs_imeta_sb_create(mp, sb_inop); |
| xfs_log_sb(*tpp); |
| return 0; |
| } |
| |
| /* |
| * Clear the given inode pointer from the superblock and drop the link count |
| * of the metadata inode. |
| */ |
| STATIC int |
| xfs_imeta_sb_unlink( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip) |
| { |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| xfs_ino_t *sb_inop; |
| |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (!sb_inop) |
| return -EINVAL; |
| |
| /* Reject if the sb doesn't point to the inode that was passed in. */ |
| if (*sb_inop != ip->i_ino) |
| return -ENOENT; |
| |
| *sb_inop = NULLFSINO; |
| trace_xfs_imeta_sb_unlink(mp, sb_inop); |
| xfs_log_sb(*tpp); |
| return xfs_droplink(*tpp, ip); |
| } |
| |
| /* Set the given inode pointer in the superblock. */ |
| STATIC int |
| xfs_imeta_sb_link( |
| struct xfs_trans *tp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip) |
| { |
| struct xfs_mount *mp = tp->t_mountp; |
| xfs_ino_t *sb_inop; |
| |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (!sb_inop) |
| return -EINVAL; |
| if (*sb_inop == NULLFSINO) |
| return -EEXIST; |
| |
| xfs_ilock(ip, XFS_ILOCK_EXCL); |
| xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); |
| |
| inc_nlink(VFS_I(ip)); |
| *sb_inop = ip->i_ino; |
| trace_xfs_imeta_sb_link(mp, sb_inop); |
| xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); |
| xfs_log_sb(tp); |
| return 0; |
| } |
| |
| /* Functions for storing and retrieving metadata directory inode values. */ |
| |
| static inline void |
| set_xname( |
| struct xfs_name *xname, |
| const struct xfs_imeta_path *path, |
| unsigned int path_idx, |
| unsigned char ftype) |
| { |
| xname->name = (const unsigned char *)path->im_path[path_idx]; |
| xname->len = strlen(path->im_path[path_idx]); |
| xname->type = ftype; |
| } |
| |
| /* |
| * Given a parent directory @dp, a metadata inode @path and component |
| * @path_idx, and the expected file type @ftype of the path component, fill out |
| * the @xname and look up the inode number in the directory, returning it in |
| * @ino. |
| */ |
| static inline int |
| xfs_imeta_dir_lookup_component( |
| struct xfs_inode *dp, |
| struct xfs_name *xname, |
| xfs_ino_t *ino) |
| { |
| int type_wanted = xname->type; |
| int error; |
| |
| trace_xfs_imeta_dir_lookup_component(dp, xname, NULLFSINO); |
| |
| if (!S_ISDIR(VFS_I(dp)->i_mode)) |
| return -EFSCORRUPTED; |
| |
| error = xfs_dir_lookup(NULL, dp, xname, ino, NULL); |
| if (error) |
| return error; |
| if (!xfs_verify_ino(dp->i_mount, *ino)) |
| return -EFSCORRUPTED; |
| if (type_wanted != XFS_DIR3_FT_UNKNOWN && xname->type != type_wanted) |
| return -EFSCORRUPTED; |
| |
| trace_xfs_imeta_dir_lookup_found(dp, xname, *ino); |
| return 0; |
| } |
| |
| /* |
| * Traverse a metadata directory tree path, returning the inode corresponding |
| * to the parent of the last path component. If any of the path components do |
| * not exist, return -ENOENT. |
| */ |
| STATIC int |
| xfs_imeta_dir_parent( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode **dpp) |
| { |
| struct xfs_name xname; |
| struct xfs_inode *dp; |
| xfs_ino_t ino; |
| unsigned int i; |
| int error; |
| |
| if (mp->m_metadirip == NULL) |
| return -ENOENT; |
| |
| /* Grab the metadir root. */ |
| error = xfs_imeta_iget(mp, mp->m_metadirip->i_ino, XFS_DIR3_FT_DIR, |
| &dp); |
| if (error) |
| return error; |
| |
| /* Caller wanted the root, we're done! */ |
| if (path->im_depth == 0) { |
| *dpp = dp; |
| return 0; |
| } |
| |
| for (i = 0; i < path->im_depth - 1; i++) { |
| /* Look up the name in the current directory. */ |
| set_xname(&xname, path, i, XFS_DIR3_FT_DIR); |
| error = xfs_imeta_dir_lookup_component(dp, &xname, &ino); |
| if (error) |
| goto out_rele; |
| |
| /* Drop the existing dp and pick up the new one. */ |
| xfs_imeta_irele(dp); |
| error = xfs_imeta_iget(mp, ino, XFS_DIR3_FT_DIR, &dp); |
| if (error) |
| return error; |
| } |
| |
| *dpp = dp; |
| return 0; |
| |
| out_rele: |
| xfs_imeta_irele(dp); |
| return error; |
| } |
| |
| /* |
| * Look up a metadata inode from the metadata directory. If the last path |
| * component doesn't exist, return NULLFSINO. If any other part of the path |
| * does not exist, return -ENOENT so we can distinguish the two. |
| */ |
| STATIC int |
| xfs_imeta_dir_lookup_int( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path, |
| xfs_ino_t *inop) |
| { |
| struct xfs_name xname; |
| struct xfs_inode *dp = NULL; |
| xfs_ino_t ino; |
| int error; |
| |
| /* metadir ino is recorded in superblock */ |
| if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR)) |
| return xfs_imeta_sb_lookup(mp, path, inop); |
| |
| ASSERT(path->im_depth > 0); |
| |
| /* Find the parent of the last path component. */ |
| error = xfs_imeta_dir_parent(mp, path, &dp); |
| if (error) |
| return error; |
| |
| /* Look up the name in the current directory. */ |
| set_xname(&xname, path, path->im_depth - 1, path->im_ftype); |
| error = xfs_imeta_dir_lookup_component(dp, &xname, &ino); |
| switch (error) { |
| case 0: |
| *inop = ino; |
| break; |
| case -ENOENT: |
| *inop = NULLFSINO; |
| error = 0; |
| break; |
| } |
| |
| xfs_imeta_irele(dp); |
| return error; |
| } |
| |
| /* |
| * Look up a metadata inode from the metadata directory tree. If any of the |
| * middle path components do not exist, we consider this corruption because |
| * only the last component is allowed to not exist. |
| */ |
| STATIC int |
| xfs_imeta_dir_lookup( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path, |
| xfs_ino_t *inop) |
| { |
| int error; |
| |
| error = xfs_imeta_dir_lookup_int(mp, path, inop); |
| if (error == -ENOENT) |
| return -EFSCORRUPTED; |
| return error; |
| } |
| |
| /* |
| * Load all the metadata inode pointers that are cached in the in-core |
| * superblock but live somewhere in the metadata directory tree. |
| */ |
| STATIC int |
| xfs_imeta_dir_mount( |
| struct xfs_mount *mp) |
| { |
| const struct xfs_imeta_sbmap *p; |
| xfs_ino_t *sb_inop; |
| int err2; |
| int error = 0; |
| |
| for (p = xfs_imeta_sbmaps; p->path && p->path->im_depth > 0; p++) { |
| if (p->path == &XFS_IMETA_METADIR) |
| continue; |
| sb_inop = xfs_imeta_sbmap_to_inop(mp, p); |
| err2 = xfs_imeta_dir_lookup_int(mp, p->path, sb_inop); |
| if (err2 == -ENOENT) { |
| *sb_inop = NULLFSINO; |
| continue; |
| } |
| if (!error && err2) |
| error = err2; |
| } |
| |
| return error; |
| } |
| |
| /* Set up an inode to be recognized as a metadata inode. */ |
| void |
| xfs_imeta_set_metaflag( |
| struct xfs_trans *tp, |
| struct xfs_inode *ip) |
| { |
| VFS_I(ip)->i_mode &= ~0777; |
| VFS_I(ip)->i_uid = GLOBAL_ROOT_UID; |
| VFS_I(ip)->i_gid = GLOBAL_ROOT_GID; |
| ip->i_projid = 0; |
| ip->i_diflags |= (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_SYNC | |
| XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | |
| XFS_DIFLAG_NODEFRAG); |
| if (S_ISDIR(VFS_I(ip)->i_mode)) |
| ip->i_diflags |= XFS_DIFLAG_NOSYMLINKS; |
| ip->i_diflags2 &= ~XFS_DIFLAG2_DAX; |
| ip->i_diflags2 |= XFS_DIFLAG2_METADATA; |
| xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); |
| } |
| |
| /* |
| * Create a new metadata inode accessible via the given metadata directory path. |
| * Callers must ensure that the directory entry does not already exist; a new |
| * one will be created. |
| */ |
| STATIC int |
| xfs_imeta_dir_create( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| umode_t mode, |
| unsigned int flags, |
| struct xfs_inode **ipp, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_icreate_args args = { |
| .nlink = S_ISDIR(mode) ? 2 : 1, |
| }; |
| struct xfs_name xname; |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| struct xfs_inode *dp = NULL; |
| xfs_ino_t *sb_inop; |
| xfs_ino_t ino; |
| unsigned int resblks; |
| int error; |
| |
| xfs_icreate_args_rootfile(&args, mode); |
| |
| /* metadir ino is recorded in superblock; only mkfs gets to do this */ |
| if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR)) { |
| error = xfs_imeta_sb_create(tpp, path, mode, flags, ipp); |
| if (error) |
| return error; |
| |
| /* Set the metadata iflag, initialize directory. */ |
| xfs_imeta_set_metaflag(*tpp, *ipp); |
| return xfs_dir_init(*tpp, *ipp, *ipp); |
| } |
| |
| ASSERT(path->im_depth > 0); |
| |
| /* |
| * Find the parent of the last path component. If the parent path does |
| * not exist, we consider this corruption because paths are supposed |
| * to exist. |
| */ |
| error = xfs_imeta_dir_parent(mp, path, &dp); |
| if (error == -ENOENT) |
| return -EFSCORRUPTED; |
| if (error) |
| return error; |
| |
| /* Check that the name does not already exist in the directory. */ |
| set_xname(&xname, path, path->im_depth - 1, XFS_DIR3_FT_UNKNOWN); |
| error = xfs_imeta_dir_lookup_component(dp, &xname, &ino); |
| switch (error) { |
| case -ENOENT: |
| break; |
| case 0: |
| error = -EEXIST; |
| fallthrough; |
| default: |
| goto out_rele; |
| } |
| |
| xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT); |
| args.pip = dp; |
| |
| /* |
| * A newly created regular or special file just has one directory |
| * entry pointing to them, but a directory also the "." entry |
| * pointing to itself. |
| */ |
| error = xfs_dialloc(tpp, dp, mode, &ino); |
| if (error) |
| goto out_ilock; |
| error = xfs_icreate(*tpp, ino, &args, ipp); |
| if (error) |
| goto out_ilock; |
| xfs_imeta_set_metaflag(*tpp, *ipp); |
| |
| /* |
| * Once we join the parent directory to the transaction we can't |
| * release it until after the transaction commits or cancels, so we |
| * must defer releasing it to end_update. This is different from |
| * regular file creation, where the vfs holds the parent dir reference |
| * and will free it. The caller is always responsible for releasing |
| * ipp, even if we failed. |
| */ |
| xfs_trans_ijoin(*tpp, dp, XFS_ILOCK_EXCL); |
| cleanup->dp = dp; |
| |
| /* Create the entry. */ |
| if (S_ISDIR(args.mode)) |
| resblks = XFS_MKDIR_SPACE_RES(mp, xname.len); |
| else |
| resblks = XFS_CREATE_SPACE_RES(mp, xname.len); |
| xname.type = xfs_mode_to_ftype(args.mode); |
| trace_xfs_imeta_dir_try_create(dp, &xname, NULLFSINO); |
| error = xfs_dir_create_new_child(*tpp, resblks, dp, &xname, *ipp); |
| if (error) |
| return error; |
| trace_xfs_imeta_dir_created(*ipp, &xname, ino); |
| |
| /* Attach dquots to this file. Caller should have allocated them! */ |
| if (!(flags & XFS_IMETA_CREATE_NOQUOTA)) { |
| error = xfs_qm_dqattach_locked(*ipp, false); |
| if (error) |
| return error; |
| xfs_trans_mod_dquot_byino(*tpp, *ipp, XFS_TRANS_DQ_ICOUNT, 1); |
| } |
| |
| /* Update the in-core superblock value if there is one. */ |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (sb_inop) |
| *sb_inop = ino; |
| return 0; |
| |
| out_ilock: |
| xfs_iunlock(dp, XFS_ILOCK_EXCL); |
| out_rele: |
| xfs_imeta_irele(dp); |
| return error; |
| } |
| |
| /* |
| * Remove the given entry from the metadata directory and drop the link count |
| * of the metadata inode. |
| */ |
| STATIC int |
| xfs_imeta_dir_unlink( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_name xname; |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| struct xfs_inode *dp = NULL; |
| xfs_ino_t *sb_inop; |
| xfs_ino_t ino; |
| unsigned int resblks; |
| int error; |
| |
| /* Metadata directory root cannot be unlinked. */ |
| if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR)) { |
| ASSERT(0); |
| return -EFSCORRUPTED; |
| } |
| |
| ASSERT(path->im_depth > 0); |
| |
| /* |
| * Find the parent of the last path component. If the parent path does |
| * not exist, we consider this corruption because paths are supposed |
| * to exist. |
| */ |
| error = xfs_imeta_dir_parent(mp, path, &dp); |
| if (error == -ENOENT) |
| return -EFSCORRUPTED; |
| if (error) |
| return error; |
| |
| /* Look up the name in the current directory. */ |
| set_xname(&xname, path, path->im_depth - 1, |
| xfs_mode_to_ftype(VFS_I(ip)->i_mode)); |
| error = xfs_imeta_dir_lookup_component(dp, &xname, &ino); |
| switch (error) { |
| case 0: |
| if (ino != ip->i_ino) |
| error = -ENOENT; |
| break; |
| case -ENOENT: |
| error = -EFSCORRUPTED; |
| break; |
| } |
| if (error) |
| goto out_rele; |
| |
| xfs_lock_two_inodes(dp, XFS_ILOCK_EXCL, ip, XFS_ILOCK_EXCL); |
| |
| /* |
| * Once we join the parent directory to the transaction we can't |
| * release it until after the transaction commits or cancels, so we |
| * must defer releasing it to end_update. This is different from |
| * regular file removal, where the vfs holds the parent dir reference |
| * and will free it. The unlink caller is always responsible for |
| * releasing ip, so we don't need to take care of that. |
| */ |
| xfs_trans_ijoin(*tpp, dp, XFS_ILOCK_EXCL); |
| xfs_trans_ijoin(*tpp, ip, XFS_ILOCK_EXCL); |
| cleanup->dp = dp; |
| |
| resblks = XFS_REMOVE_SPACE_RES(mp); |
| error = xfs_dir_remove_child(*tpp, resblks, dp, &xname, ip); |
| if (error) |
| return error; |
| trace_xfs_imeta_dir_unlinked(dp, &xname, ip->i_ino); |
| |
| /* Update the in-core superblock value if there is one. */ |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (sb_inop) |
| *sb_inop = NULLFSINO; |
| return 0; |
| |
| out_rele: |
| xfs_imeta_irele(dp); |
| return error; |
| } |
| |
| /* Set the given path in the metadata directory to point to an inode. */ |
| STATIC int |
| xfs_imeta_dir_link( |
| struct xfs_trans *tp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_name xname; |
| struct xfs_mount *mp = tp->t_mountp; |
| struct xfs_inode *dp = NULL; |
| xfs_ino_t *sb_inop; |
| xfs_ino_t ino; |
| unsigned int resblks; |
| int error; |
| |
| /* Metadata directory root cannot be linked. */ |
| if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR)) { |
| ASSERT(0); |
| return -EFSCORRUPTED; |
| } |
| |
| ASSERT(path->im_depth > 0); |
| |
| /* |
| * Find the parent of the last path component. If the parent path does |
| * not exist, we consider this corruption because paths are supposed |
| * to exist. |
| */ |
| error = xfs_imeta_dir_parent(mp, path, &dp); |
| if (error == -ENOENT) |
| return -EFSCORRUPTED; |
| if (error) |
| return error; |
| |
| /* Look up the name in the current directory. */ |
| set_xname(&xname, path, path->im_depth - 1, |
| xfs_mode_to_ftype(VFS_I(ip)->i_mode)); |
| error = xfs_imeta_dir_lookup_component(dp, &xname, &ino); |
| switch (error) { |
| case -ENOENT: |
| break; |
| case 0: |
| error = -EEXIST; |
| fallthrough; |
| default: |
| goto out_rele; |
| } |
| |
| xfs_lock_two_inodes(ip, XFS_ILOCK_EXCL, dp, XFS_ILOCK_EXCL); |
| |
| /* |
| * Once we join the parent directory to the transaction we can't |
| * release it until after the transaction commits or cancels, so we |
| * must defer releasing it to end_update. This is different from |
| * regular file removal, where the vfs holds the parent dir reference |
| * and will free it. The link caller is always responsible for |
| * releasing ip, so we don't need to take care of that. |
| */ |
| xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); |
| xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); |
| cleanup->dp = dp; |
| |
| resblks = XFS_LINK_SPACE_RES(mp, target_name->len); |
| error = xfs_dir_link_existing_child(tp, resblks, dp, &xname, ip); |
| if (error) |
| return error; |
| |
| trace_xfs_imeta_dir_link(dp, &xname, ip->i_ino); |
| |
| /* Update the in-core superblock value if there is one. */ |
| sb_inop = xfs_imeta_path_to_sb_inop(mp, path); |
| if (sb_inop) |
| *sb_inop = ip->i_ino; |
| return 0; |
| |
| out_rele: |
| xfs_imeta_irele(dp); |
| return error; |
| } |
| |
| /* General functions for managing metadata inode pointers */ |
| |
| /* |
| * Is this metadata inode pointer ok? We allow the fields to be set to |
| * NULLFSINO if the metadata structure isn't present, and we don't allow |
| * obviously incorrect inode pointers. |
| */ |
| static inline bool |
| xfs_imeta_verify( |
| struct xfs_mount *mp, |
| xfs_ino_t ino) |
| { |
| if (ino == NULLFSINO) |
| return true; |
| return xfs_verify_ino(mp, ino); |
| } |
| |
| /* Look up a metadata inode by its path. */ |
| int |
| xfs_imeta_lookup( |
| struct xfs_mount *mp, |
| const struct xfs_imeta_path *path, |
| xfs_ino_t *inop) |
| { |
| xfs_ino_t ino; |
| int error; |
| |
| ASSERT(xfs_imeta_path_check(path)); |
| |
| if (xfs_has_metadir(mp)) |
| error = xfs_imeta_dir_lookup(mp, path, &ino); |
| else |
| error = xfs_imeta_sb_lookup(mp, path, &ino); |
| if (error) |
| return error; |
| |
| if (!xfs_imeta_verify(mp, ino)) |
| return -EFSCORRUPTED; |
| |
| *inop = ino; |
| return 0; |
| } |
| |
| /* |
| * Create a metadata inode with the given @mode, and insert it into the |
| * metadata directory tree at the given @path. The path (up to the final |
| * component) must already exist. The new metadata inode @ipp will be ijoined |
| * and logged to @tpp, with the ILOCK held until the next transaction commit. |
| * The caller must provide a @cleanup structure. |
| * |
| * Callers must ensure that the root dquots are allocated, if applicable. |
| * |
| * NOTE: This function may pass a child inode @ipp back to the caller even if |
| * it returns a negative error code. If an inode is passed back, the caller |
| * must finish setting up the incore inode before releasing it. |
| */ |
| int |
| xfs_imeta_create( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| umode_t mode, |
| unsigned int flags, |
| struct xfs_inode **ipp, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| |
| ASSERT(xfs_imeta_path_check(path)); |
| *ipp = NULL; |
| cleanup->dp = NULL; |
| |
| if (xfs_has_metadir(mp)) |
| return xfs_imeta_dir_create(tpp, path, mode, flags, ipp, |
| cleanup); |
| return xfs_imeta_sb_create(tpp, path, mode, flags, ipp); |
| } |
| |
| /* Free a file from the metadata directory tree. */ |
| STATIC int |
| xfs_imeta_ifree( |
| struct xfs_trans *tp, |
| struct xfs_inode *ip) |
| { |
| struct xfs_mount *mp = ip->i_mount; |
| struct xfs_perag *pag; |
| struct xfs_icluster xic = { 0 }; |
| int error; |
| |
| ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); |
| ASSERT(VFS_I(ip)->i_nlink == 0); |
| ASSERT(ip->i_df.if_nextents == 0); |
| ASSERT(ip->i_disk_size == 0 || !S_ISREG(VFS_I(ip)->i_mode)); |
| ASSERT(ip->i_nblocks == 0); |
| |
| pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); |
| |
| error = xfs_dir_ifree(tp, pag, ip, &xic); |
| if (error) |
| goto out; |
| |
| /* Metadata files do not support ownership changes or DMAPI. */ |
| |
| if (xic.deleted) |
| error = xfs_ifree_cluster(tp, pag, ip, &xic); |
| out: |
| xfs_perag_put(pag); |
| return error; |
| } |
| |
| /* |
| * Unlink a metadata inode @ip from the metadata directory given by @path. The |
| * metadata inode must not be ILOCKed. Upon return, the inode will be ijoined |
| * and logged to @tpp, and returned with reduced link count, ready to be |
| * released. The caller must provide a @cleanup structure. |
| */ |
| int |
| xfs_imeta_unlink( |
| struct xfs_trans **tpp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_mount *mp = (*tpp)->t_mountp; |
| int error; |
| |
| cleanup->dp = NULL; |
| |
| ASSERT(xfs_imeta_path_check(path)); |
| ASSERT(xfs_imeta_verify((*tpp)->t_mountp, ip->i_ino)); |
| |
| if (xfs_has_metadir(mp)) |
| error = xfs_imeta_dir_unlink(tpp, path, ip, cleanup); |
| else |
| error = xfs_imeta_sb_unlink(tpp, path, ip); |
| if (error) |
| return error; |
| |
| /* |
| * Metadata files require explicit resource cleanup. In other words, |
| * the inactivation system will not touch these files, so we must free |
| * the ondisk inode by ourselves if warranted. |
| */ |
| if (VFS_I(ip)->i_nlink > 0) |
| return 0; |
| |
| return xfs_imeta_ifree(*tpp, ip); |
| } |
| |
| /* |
| * Link the metadata directory given by @path point to the given inode number. |
| * The path must not already exist. The caller must not hold the ILOCK, and |
| * the function will return with the inode joined to the transaction. |
| */ |
| int |
| xfs_imeta_link( |
| struct xfs_trans *tp, |
| const struct xfs_imeta_path *path, |
| struct xfs_inode *ip, |
| struct xfs_imeta_end *cleanup) |
| { |
| struct xfs_mount *mp = tp->t_mountp; |
| |
| ASSERT(xfs_imeta_path_check(path)); |
| |
| cleanup->dp = NULL; |
| if (xfs_has_metadir(mp)) |
| return xfs_imeta_dir_link(tp, path, ip, cleanup); |
| return xfs_imeta_sb_link(tp, path, ip); |
| } |
| |
| /* |
| * Clean up after committing (or cancelling) a metadata inode creation or |
| * removal. |
| */ |
| void |
| xfs_imeta_end_update( |
| struct xfs_mount *mp, |
| struct xfs_imeta_end *cleanup, |
| int error) |
| { |
| trace_xfs_imeta_end_update(mp, error, __return_address); |
| |
| if (cleanup->dp) |
| xfs_imeta_irele(cleanup->dp); |
| cleanup->dp = NULL; |
| } |
| |
| /* Does this inode number refer to a static metadata inode? */ |
| bool |
| xfs_is_static_meta_ino( |
| struct xfs_mount *mp, |
| xfs_ino_t ino) |
| { |
| const struct xfs_imeta_sbmap *p; |
| |
| if (ino == NULLFSINO) |
| return false; |
| |
| for (p = xfs_imeta_sbmaps; p->path; p++) |
| if (ino == *xfs_imeta_sbmap_to_inop(mp, p)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Ensure that the in-core superblock has all the values that it should. */ |
| int |
| xfs_imeta_mount( |
| struct xfs_mount *mp) |
| { |
| if (xfs_has_metadir(mp)) |
| return xfs_imeta_dir_mount(mp); |
| |
| return 0; |
| } |
| |
| /* Calculate the log block reservation to create a metadata inode. */ |
| unsigned int |
| xfs_imeta_create_space_res( |
| struct xfs_mount *mp) |
| { |
| if (xfs_has_metadir(mp)) |
| return max(XFS_MKDIR_SPACE_RES(mp, NAME_MAX), |
| XFS_CREATE_SPACE_RES(mp, NAME_MAX)); |
| return XFS_IALLOC_SPACE_RES(mp); |
| } |
| |
| /* Calculate the log block reservation to unlink a metadata inode. */ |
| unsigned int |
| xfs_imeta_unlink_space_res( |
| struct xfs_mount *mp) |
| { |
| return XFS_REMOVE_SPACE_RES(mp); |
| } |
| |
| /* Clear the metadata iflag if we're unlinking this inode. */ |
| void |
| xfs_imeta_droplink( |
| struct xfs_inode *ip) |
| { |
| if (VFS_I(ip)->i_nlink == 0 && |
| xfs_has_metadir(ip->i_mount) && |
| xfs_is_metadata_inode(ip)) |
| ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA; |
| } |