📄 xfs_attr.c
字号:
/* * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */#include <linux/capability.h>#include "xfs.h"#include "xfs_fs.h"#include "xfs_types.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_da_btree.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_alloc.h"#include "xfs_btree.h"#include "xfs_inode_item.h"#include "xfs_bmap.h"#include "xfs_attr.h"#include "xfs_attr_leaf.h"#include "xfs_error.h"#include "xfs_quota.h"#include "xfs_trans_space.h"#include "xfs_acl.h"#include "xfs_rw.h"#include "xfs_vnodeops.h"/* * xfs_attr.c * * Provide the external interfaces to manage attribute lists. */#define ATTR_SYSCOUNT 2static struct attrnames posix_acl_access;static struct attrnames posix_acl_default;static struct attrnames *attr_system_names[ATTR_SYSCOUNT];/*======================================================================== * Function prototypes for the kernel. *========================================================================*//* * Internal routines when attribute list fits inside the inode. */STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);/* * Internal routines when attribute list is one block. */STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);STATIC int xfs_attr_leaf_list(xfs_attr_list_context_t *context);/* * Internal routines when attribute list is more than one block. */STATIC int xfs_attr_node_get(xfs_da_args_t *args);STATIC int xfs_attr_node_addname(xfs_da_args_t *args);STATIC int xfs_attr_node_removename(xfs_da_args_t *args);STATIC int xfs_attr_node_list(xfs_attr_list_context_t *context);STATIC int xfs_attr_fillstate(xfs_da_state_t *state);STATIC int xfs_attr_refillstate(xfs_da_state_t *state);/* * Routines to manipulate out-of-line attribute values. */STATIC int xfs_attr_rmtval_set(xfs_da_args_t *args);STATIC int xfs_attr_rmtval_remove(xfs_da_args_t *args);#define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */#if defined(XFS_ATTR_TRACE)ktrace_t *xfs_attr_trace_buf;#endif/*======================================================================== * Overall external interface routines. *========================================================================*/intxfs_attr_fetch(xfs_inode_t *ip, const char *name, int namelen, char *value, int *valuelenp, int flags, struct cred *cred){ xfs_da_args_t args; int error; if ((XFS_IFORK_Q(ip) == 0) || (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS && ip->i_d.di_anextents == 0)) return(ENOATTR); /* * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); args.name = name; args.namelen = namelen; args.value = value; args.valuelen = *valuelenp; args.flags = flags; args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = ip; args.whichfork = XFS_ATTR_FORK; /* * Decide on what work routines to call based on the inode size. */ if (XFS_IFORK_Q(ip) == 0 || (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS && ip->i_d.di_anextents == 0)) { error = XFS_ERROR(ENOATTR); } else if (ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) { error = xfs_attr_shortform_getvalue(&args); } else if (xfs_bmap_one_block(ip, XFS_ATTR_FORK)) { error = xfs_attr_leaf_get(&args); } else { error = xfs_attr_node_get(&args); } /* * Return the number of bytes in the value to the caller. */ *valuelenp = args.valuelen; if (error == EEXIST) error = 0; return(error);}intxfs_attr_get( xfs_inode_t *ip, const char *name, char *value, int *valuelenp, int flags, cred_t *cred){ int error, namelen; XFS_STATS_INC(xs_attr_get); if (!name) return(EINVAL); namelen = strlen(name); if (namelen >= MAXNAMELEN) return(EFAULT); /* match IRIX behaviour */ if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return(EIO); xfs_ilock(ip, XFS_ILOCK_SHARED); error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred); xfs_iunlock(ip, XFS_ILOCK_SHARED); return(error);}intxfs_attr_set_int(xfs_inode_t *dp, const char *name, int namelen, char *value, int valuelen, int flags){ xfs_da_args_t args; xfs_fsblock_t firstblock; xfs_bmap_free_t flist; int error, err2, committed; int local, size; uint nblks; xfs_mount_t *mp = dp->i_mount; int rsvd = (flags & ATTR_ROOT) != 0; /* * Attach the dquots to the inode. */ if ((error = XFS_QM_DQATTACH(mp, dp, 0))) return (error); /* * If the inode doesn't have an attribute fork, add one. * (inode must not be locked when we call this routine) */ if (XFS_IFORK_Q(dp) == 0) { int sf_size = sizeof(xfs_attr_sf_hdr_t) + XFS_ATTR_SF_ENTSIZE_BYNAME(namelen, valuelen); if ((error = xfs_bmap_add_attrfork(dp, sf_size, rsvd))) return(error); } /* * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); args.name = name; args.namelen = namelen; args.value = value; args.valuelen = valuelen; args.flags = flags; args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = dp; args.firstblock = &firstblock; args.flist = &flist; args.whichfork = XFS_ATTR_FORK; args.addname = 1; args.oknoent = 1; /* * Determine space new attribute will use, and if it would be * "local" or "remote" (note: local != inline). */ size = xfs_attr_leaf_newentsize(namelen, valuelen, mp->m_sb.sb_blocksize, &local); nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); if (local) { if (size > (mp->m_sb.sb_blocksize >> 1)) { /* Double split possible */ nblks <<= 1; } } else { uint dblocks = XFS_B_TO_FSB(mp, valuelen); /* Out of line attribute, cannot double split, but make * room for the attribute value itself. */ nblks += dblocks; nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK); } /* Size is now blocks for attribute data */ args.total = nblks; /* * Start our first transaction of the day. * * All future transactions during this code must be "chained" off * this one via the trans_dup() call. All transactions will contain * the inode, and the inode will always be marked with trans_ihold(). * Since the inode will be locked in all transactions, we must log * the inode in every transaction to let it float upward through * the log. */ args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_SET); /* * Root fork attributes can use reserved data blocks for this * operation if necessary */ if (rsvd) args.trans->t_flags |= XFS_TRANS_RESERVE; if ((error = xfs_trans_reserve(args.trans, (uint) nblks, XFS_ATTRSET_LOG_RES(mp, nblks), 0, XFS_TRANS_PERM_LOG_RES, XFS_ATTRSET_LOG_COUNT))) { xfs_trans_cancel(args.trans, 0); return(error); } xfs_ilock(dp, XFS_ILOCK_EXCL); error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, args.trans, dp, nblks, 0, rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES : XFS_QMOPT_RES_REGBLKS); if (error) { xfs_iunlock(dp, XFS_ILOCK_EXCL); xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES); return (error); } xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL); xfs_trans_ihold(args.trans, dp); /* * If the attribute list is non-existent or a shortform list, * upgrade it to a single-leaf-block attribute list. */ if ((dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) || ((dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) && (dp->i_d.di_anextents == 0))) { /* * Build initial attribute list (if required). */ if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) xfs_attr_shortform_create(&args); /* * Try to add the attr to the attribute list in * the inode. */ error = xfs_attr_shortform_addname(&args); if (error != ENOSPC) { /* * Commit the shortform mods, and we're done. * NOTE: this is also the error path (EEXIST, etc). */ ASSERT(args.trans != NULL); /* * If this is a synchronous mount, make sure that * the transaction goes to disk before returning * to the user. */ if (mp->m_flags & XFS_MOUNT_WSYNC) { xfs_trans_set_sync(args.trans); } err2 = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); /* * Hit the inode change time. */ if (!error && (flags & ATTR_KERNOTIME) == 0) { xfs_ichgtime(dp, XFS_ICHGTIME_CHG); } return(error == 0 ? err2 : error); } /* * It won't fit in the shortform, transform to a leaf block. * GROT: another possible req'mt for a double-split btree op. */ XFS_BMAP_INIT(args.flist, args.firstblock); error = xfs_attr_shortform_to_leaf(&args); if (!error) { error = xfs_bmap_finish(&args.trans, args.flist, &committed); } if (error) { ASSERT(committed); args.trans = NULL; xfs_bmap_cancel(&flist); goto out; } /* * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ if (committed) { xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL); xfs_trans_ihold(args.trans, dp); } /* * Commit the leaf transformation. We'll need another (linked) * transaction to add the new attribute to the leaf. */ if ((error = xfs_attr_rolltrans(&args.trans, dp))) goto out; } if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { error = xfs_attr_leaf_addname(&args); } else { error = xfs_attr_node_addname(&args); } if (error) { goto out; } /* * If this is a synchronous mount, make sure that the * transaction goes to disk before returning to the user. */ if (mp->m_flags & XFS_MOUNT_WSYNC) { xfs_trans_set_sync(args.trans); } /* * Commit the last in the sequence of transactions. */ xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE); error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); /* * Hit the inode change time. */ if (!error && (flags & ATTR_KERNOTIME) == 0) { xfs_ichgtime(dp, XFS_ICHGTIME_CHG); } return(error);out: if (args.trans) xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); xfs_iunlock(dp, XFS_ILOCK_EXCL); return(error);}intxfs_attr_set( xfs_inode_t *dp, const char *name, char *value, int valuelen, int flags){ int namelen; namelen = strlen(name); if (namelen >= MAXNAMELEN) return EFAULT; /* match IRIX behaviour */ XFS_STATS_INC(xs_attr_set); if (XFS_FORCED_SHUTDOWN(dp->i_mount)) return (EIO); return xfs_attr_set_int(dp, name, namelen, value, valuelen, flags);}/* * Generic handler routine to remove a name from an attribute list. * Transitions attribute list from Btree to shortform as necessary. */intxfs_attr_remove_int(xfs_inode_t *dp, const char *name, int namelen, int flags){ xfs_da_args_t args; xfs_fsblock_t firstblock; xfs_bmap_free_t flist; int error; xfs_mount_t *mp = dp->i_mount; /* * Fill in the arg structure for this request. */ memset((char *)&args, 0, sizeof(args)); args.name = name; args.namelen = namelen; args.flags = flags; args.hashval = xfs_da_hashname(args.name, args.namelen); args.dp = dp; args.firstblock = &firstblock; args.flist = &flist; args.total = 0; args.whichfork = XFS_ATTR_FORK; /* * Attach the dquots to the inode. */ if ((error = XFS_QM_DQATTACH(mp, dp, 0))) return (error); /* * Start our first transaction of the day. * * All future transactions during this code must be "chained" off * this one via the trans_dup() call. All transactions will contain * the inode, and the inode will always be marked with trans_ihold(). * Since the inode will be locked in all transactions, we must log * the inode in every transaction to let it float upward through * the log. */ args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_RM); /* * Root fork attributes can use reserved data blocks for this * operation if necessary */ if (flags & ATTR_ROOT) args.trans->t_flags |= XFS_TRANS_RESERVE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -