+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002,2004-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 <libxfs.h>
+#include "libxfs.h"
#include "globals.h"
#include "err_protos.h"
#include "attr_repair.h"
#include "bmap.h"
#include "protos.h"
#include "dir2.h"
+#include "da_util.h"
-static int xfs_acl_valid(xfs_acl_disk_t *daclp);
+static int xfs_acl_valid(struct xfs_mount *mp, struct xfs_acl *daclp);
static int xfs_mac_valid(xfs_mac_label_t *lp);
/*
* dir v1 code, but that format is no longer supported yb the userspace
* utilities and hence is now specific to the attribute tree implementation.
*/
-#define XR_DA_LEAF_MAPSIZE XFS_ATTR_LEAF_MAPSIZE
typedef unsigned char da_freemap_t;
-/*
- * the cursor gets passed up and down the da btree processing
- * routines. The interior block processing routines use the
- * cursor to determine if the pointers to and from the preceding
- * and succeeding sibling blocks are ok and whether the values in
- * the current block are consistent with the entries in the parent
- * nodes. When a block is traversed, a parent-verification routine
- * is called to verify if the next logical entry in the next level up
- * is consistent with the greatest hashval in the next block of the
- * current level. The verification routine is itself recursive and
- * calls itself if it has to traverse an interior block to get
- * the next logical entry. The routine recurses upwards through
- * the tree until it finds a block where it can simply step to
- * the next entry. The hashval in that entry should be equal to
- * the hashval being passed to it (the greatest hashval in the block
- * that the entry points to). If that isn't true, then the tree
- * is blown and we need to trash it, salvage and trash it, or fix it.
- * Currently, we just trash it.
- */
-typedef struct da_level_state {
- xfs_buf_t *bp; /* block bp */
-#ifdef XR_DIR_TRACE
- xfs_da_intnode_t *n; /* bp data */
-#endif
- xfs_dablk_t bno; /* file block number */
- xfs_dahash_t hashval; /* last verified hashval */
- int index; /* current index in block */
- int dirty; /* is buffer dirty ? (1 == yes) */
-} da_level_state_t;
-
-typedef struct da_bt_cursor {
- int active; /* highest level in tree (# levels-1) */
- int type; /* 0 if dir, 1 if attr */
- xfs_ino_t ino;
- xfs_dablk_t greatest_bno;
- xfs_dinode_t *dip;
- da_level_state_t level[XFS_DA_NODE_MAXDEPTH];
- struct blkmap *blkmap;
-} da_bt_cursor_t;
-
-
/*
* Allocate a freespace map for directory or attr leaf blocks (1 bit per byte)
* 1 == used, 0 == free.
return(0);
}
-/*
- * walk tree from root to the left-most leaf block reading in
- * blocks and setting up cursor. passes back file block number of the
- * left-most leaf block if successful (bno). returns 1 if successful,
- * 0 if unsuccessful.
- */
-static int
-traverse_int_dablock(xfs_mount_t *mp,
- da_bt_cursor_t *da_cursor,
- xfs_dablk_t *rbno,
- int whichfork)
-{
- xfs_dablk_t bno;
- int i;
- xfs_da_intnode_t *node;
- xfs_dfsbno_t fsbno;
- xfs_buf_t *bp;
- struct xfs_da_node_entry *btree;
- struct xfs_da3_icnode_hdr nodehdr;
-
- /*
- * traverse down left-side of tree until we hit the
- * left-most leaf block setting up the btree cursor along
- * the way.
- */
- bno = 0;
- i = -1;
- node = NULL;
- da_cursor->active = 0;
-
- do {
- /*
- * read in each block along the way and set up cursor
- */
- fsbno = blkmap_get(da_cursor->blkmap, bno);
-
- if (fsbno == NULLDFSBNO)
- goto error_out;
-
- bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
- XFS_FSB_TO_BB(mp, 1), 0, &xfs_da3_node_buf_ops);
- if (!bp) {
- if (whichfork == XFS_DATA_FORK)
- do_warn(
- _("can't read block %u (fsbno %" PRIu64 ") for directory inode %" PRIu64 "\n"),
- bno, fsbno, da_cursor->ino);
- else
- do_warn(
- _("can't read block %u (fsbno %" PRIu64 ") for attrbute fork of inode %" PRIu64 "\n"),
- bno, fsbno, da_cursor->ino);
- goto error_out;
- }
-
- node = (xfs_da_intnode_t *)XFS_BUF_PTR(bp);
- btree = xfs_da3_node_tree_p(node);
- xfs_da3_node_hdr_from_disk(&nodehdr, node);
-
- if (nodehdr.magic != XFS_DA_NODE_MAGIC &&
- nodehdr.magic != XFS_DA3_NODE_MAGIC) {
- do_warn(_("bad dir/attr magic number in inode %" PRIu64 ", "
- "file bno = %u, fsbno = %" PRIu64 "\n"),
- da_cursor->ino, bno, fsbno);
- libxfs_putbuf(bp);
- goto error_out;
- }
-
- if (nodehdr.count > mp->m_dir_node_ents) {
- do_warn(_("bad record count in inode %" PRIu64 ", "
- "count = %d, max = %d\n"),
- da_cursor->ino,
- nodehdr.count,
- mp->m_dir_node_ents);
- libxfs_putbuf(bp);
- goto error_out;
- }
-
- /*
- * maintain level counter
- */
- if (i == -1)
- i = da_cursor->active = nodehdr.level;
- else {
- if (nodehdr.level == i - 1) {
- i--;
- } else {
- if (whichfork == XFS_DATA_FORK)
- do_warn(_("bad directory btree for "
- "directory inode %" PRIu64 "\n"),
- da_cursor->ino);
- else
- do_warn(_("bad attribute fork btree "
- "for inode %" PRIu64 "\n"),
- da_cursor->ino);
- libxfs_putbuf(bp);
- goto error_out;
- }
- }
-
- da_cursor->level[i].hashval = be32_to_cpu(btree[0].hashval);
- da_cursor->level[i].bp = bp;
- da_cursor->level[i].bno = bno;
- da_cursor->level[i].index = 0;
-#ifdef XR_DIR_TRACE
- da_cursor->level[i].n = XFS_BUF_TO_DA_INTNODE(bp);
-#endif
-
- /*
- * set up new bno for next level down
- */
- bno = be32_to_cpu(btree[0].before);
- } while (node != NULL && i > 1);
-
- /*
- * now return block number and get out
- */
- *rbno = da_cursor->level[0].bno = bno;
- return(1);
-
-error_out:
- while (i > 1 && i <= da_cursor->active) {
- libxfs_putbuf(da_cursor->level[i].bp);
- i++;
- }
-
- return(0);
-}
-
-/*
- * blow out buffer for this level and all the rest above as well
- * if error == 0, we are not expecting to encounter any unreleased
- * buffers (e.g. if we do, it's a mistake). if error == 1, we're
- * in an error-handling case so unreleased buffers may exist.
- */
-static void
-release_da_cursor_int(xfs_mount_t *mp,
- da_bt_cursor_t *cursor,
- int prev_level,
- int error)
-{
- int level = prev_level + 1;
-
- if (cursor->level[level].bp != NULL) {
- if (!error) {
- do_warn(_("release_da_cursor_int got unexpected "
- "non-null bp, dabno = %u\n"),
- cursor->level[level].bno);
- }
- ASSERT(error != 0);
-
- libxfs_putbuf(cursor->level[level].bp);
- cursor->level[level].bp = NULL;
- }
-
- if (level < cursor->active)
- release_da_cursor_int(mp, cursor, level, error);
-
- return;
-}
-
-static void
-release_da_cursor(xfs_mount_t *mp,
- da_bt_cursor_t *cursor,
- int prev_level)
-{
- release_da_cursor_int(mp, cursor, prev_level, 0);
-}
-
-static void
-err_release_da_cursor(xfs_mount_t *mp,
- da_bt_cursor_t *cursor,
- int prev_level)
-{
- release_da_cursor_int(mp, cursor, prev_level, 1);
-}
-
-/*
- * make sure that all entries in all blocks along the right side of
- * of the tree are used and hashval's are consistent. level is the
- * level of the descendent block. returns 0 if good (even if it had
- * to be fixed up), and 1 if bad. The right edge of the tree is
- * technically a block boundary. this routine should be used then
- * instead of verify_da_path().
- */
-static int
-verify_final_da_path(xfs_mount_t *mp,
- da_bt_cursor_t *cursor,
- const int p_level)
-{
- xfs_da_intnode_t *node;
- xfs_dahash_t hashval;
- int bad = 0;
- int entry;
- int this_level = p_level + 1;
- struct xfs_da_node_entry *btree;
- struct xfs_da3_icnode_hdr nodehdr;
-
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "in verify_final_da_path, this_level = %d\n",
- this_level);
-#endif
- /*
- * the index should point to the next "unprocessed" entry
- * in the block which should be the final (rightmost) entry
- */
- entry = cursor->level[this_level].index;
- node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp);
- btree = xfs_da3_node_tree_p(node);
- xfs_da3_node_hdr_from_disk(&nodehdr, node);
-
- /*
- * check internal block consistency on this level -- ensure
- * that all entries are used, encountered and expected hashvals
- * match, etc.
- */
- if (entry != nodehdr.count - 1) {
- do_warn(_("directory/attribute block used/count "
- "inconsistency - %d/%hu\n"),
- entry, nodehdr.count);
- bad++;
- }
- /*
- * hash values monotonically increasing ???
- */
- if (cursor->level[this_level].hashval >=
- be32_to_cpu(btree[entry].hashval)) {
- do_warn(_("directory/attribute block hashvalue inconsistency, "
- "expected > %u / saw %u\n"),
- cursor->level[this_level].hashval,
- be32_to_cpu(btree[entry].hashval));
- bad++;
- }
- if (nodehdr.forw != 0) {
- do_warn(_("bad directory/attribute forward block pointer, "
- "expected 0, saw %u\n"),
- nodehdr.forw);
- bad++;
- }
- if (bad) {
- do_warn(_("bad directory block in dir ino %" PRIu64 "\n"),
- cursor->ino);
- return(1);
- }
- /*
- * keep track of greatest block # -- that gets
- * us the length of the directory
- */
- if (cursor->level[this_level].bno > cursor->greatest_bno)
- cursor->greatest_bno = cursor->level[this_level].bno;
-
- /*
- * ok, now check descendant block number against this level
- */
- if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before)) {
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "bad directory btree pointer, child bno should "
- "be %d, block bno is %d, hashval is %u\n",
- be16_to_cpu(btree[entry].before),
- cursor->level[p_level].bno,
- cursor->level[p_level].hashval);
- fprintf(stderr, "verify_final_da_path returns 1 (bad) #1a\n");
-#endif
- return(1);
- }
-
- if (cursor->level[p_level].hashval != be32_to_cpu(btree[entry].hashval)) {
- if (!no_modify) {
- do_warn(_("correcting bad hashval in non-leaf "
- "dir/attr block\n\tin (level %d) in "
- "inode %" PRIu64 ".\n"),
- this_level, cursor->ino);
- btree[entry].hashval = cpu_to_be32(
- cursor->level[p_level].hashval);
- cursor->level[this_level].dirty++;
- } else {
- do_warn(_("would correct bad hashval in non-leaf "
- "dir/attr block\n\tin (level %d) in "
- "inode %" PRIu64 ".\n"),
- this_level, cursor->ino);
- }
- }
-
- /*
- * Note: squirrel hashval away _before_ releasing the
- * buffer, preventing a use-after-free problem.
- */
- hashval = be32_to_cpu(btree[entry].hashval);
-
- /*
- * release/write buffer
- */
- ASSERT(cursor->level[this_level].dirty == 0 ||
- (cursor->level[this_level].dirty && !no_modify));
-
- if (cursor->level[this_level].dirty && !no_modify)
- libxfs_writebuf(cursor->level[this_level].bp, 0);
- else
- libxfs_putbuf(cursor->level[this_level].bp);
-
- cursor->level[this_level].bp = NULL;
-
- /*
- * bail out if this is the root block (top of tree)
- */
- if (this_level >= cursor->active) {
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "verify_final_da_path returns 0 (ok)\n");
-#endif
- return(0);
- }
- /*
- * set hashvalue to correctly reflect the now-validated
- * last entry in this block and continue upwards validation
- */
- cursor->level[this_level].hashval = hashval;
- return(verify_final_da_path(mp, cursor, this_level));
-}
-
-/*
- * Verifies the path from a descendant block up to the root.
- * Should be called when the descendant level traversal hits
- * a block boundary before crossing the boundary (reading in a new
- * block).
- *
- * the directory/attr btrees work differently to the other fs btrees.
- * each interior block contains records that are <hashval, bno>
- * pairs. The bno is a file bno, not a filesystem bno. The last
- * hashvalue in the block <bno> will be <hashval>. BUT unlike
- * the freespace btrees, the *last* value in each block gets
- * propagated up the tree instead of the first value in each block.
- * that is, the interior records point to child blocks and the *greatest*
- * hash value contained by the child block is the one the block above
- * uses as the key for the child block.
- *
- * level is the level of the descendent block. returns 0 if good,
- * and 1 if bad. The descendant block may be a leaf block.
- *
- * the invariant here is that the values in the cursor for the
- * levels beneath this level (this_level) and the cursor index
- * for this level *must* be valid.
- *
- * that is, the hashval/bno info is accurate for all
- * DESCENDANTS and match what the node[index] information
- * for the current index in the cursor for this level.
- *
- * the index values in the cursor for the descendant level
- * are allowed to be off by one as they will reflect the
- * next entry at those levels to be processed.
- *
- * the hashvalue for the current level can't be set until
- * we hit the last entry in the block so, it's garbage
- * until set by this routine.
- *
- * bno and bp for the current block/level are always valid
- * since they have to be set so we can get a buffer for the
- * block.
- */
-static int
-verify_da_path(xfs_mount_t *mp,
- da_bt_cursor_t *cursor,
- const int p_level)
-{
- xfs_da_intnode_t *node;
- xfs_da_intnode_t *newnode;
- xfs_dfsbno_t fsbno;
- xfs_dablk_t dabno;
- xfs_buf_t *bp;
- int bad;
- int entry;
- int this_level = p_level + 1;
- struct xfs_da_node_entry *btree;
- struct xfs_da3_icnode_hdr nodehdr;
-
- /*
- * index is currently set to point to the entry that
- * should be processed now in this level.
- */
- entry = cursor->level[this_level].index;
- node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp);
- btree = xfs_da3_node_tree_p(node);
- xfs_da3_node_hdr_from_disk(&nodehdr, node);
-
- /*
- * if this block is out of entries, validate this
- * block and move on to the next block.
- * and update cursor value for said level
- */
- if (entry >= nodehdr.count) {
- /*
- * update the hash value for this level before
- * validating it. bno value should be ok since
- * it was set when the block was first read in.
- */
- cursor->level[this_level].hashval =
- be32_to_cpu(btree[entry - 1].hashval);
-
- /*
- * keep track of greatest block # -- that gets
- * us the length of the directory
- */
- if (cursor->level[this_level].bno > cursor->greatest_bno)
- cursor->greatest_bno = cursor->level[this_level].bno;
-
- /*
- * validate the path for the current used-up block
- * before we trash it
- */
- if (verify_da_path(mp, cursor, this_level))
- return(1);
- /*
- * ok, now get the next buffer and check sibling pointers
- */
- dabno = nodehdr.forw;
- ASSERT(dabno != 0);
- fsbno = blkmap_get(cursor->blkmap, dabno);
-
- if (fsbno == NULLDFSBNO) {
- do_warn(_("can't get map info for block %u "
- "of directory inode %" PRIu64 "\n"),
- dabno, cursor->ino);
- return(1);
- }
-
- bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
- XFS_FSB_TO_BB(mp, 1), 0, &xfs_da3_node_buf_ops);
- if (!bp) {
- do_warn(
- _("can't read block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
- dabno, fsbno, cursor->ino);
- return(1);
- }
-
- newnode = (xfs_da_intnode_t *)XFS_BUF_PTR(bp);
- btree = xfs_da3_node_tree_p(node);
- xfs_da3_node_hdr_from_disk(&nodehdr, newnode);
- /*
- * verify magic number and back pointer, sanity-check
- * entry count, verify level
- */
- bad = 0;
- if (nodehdr.magic != XFS_DA_NODE_MAGIC ||
- nodehdr.magic != XFS_DA3_NODE_MAGIC) {
- do_warn(
- _("bad magic number %x in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
- nodehdr.magic,
- dabno, fsbno, cursor->ino);
- bad++;
- }
- if (nodehdr.back != cursor->level[this_level].bno) {
- do_warn(
- _("bad back pointer in block %u (%"PRIu64 ") for directory inode %" PRIu64 "\n"),
- dabno, fsbno, cursor->ino);
- bad++;
- }
- if (nodehdr.count > mp->m_dir_node_ents) {
- do_warn(
- _("entry count %d too large in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
- nodehdr.count,
- dabno, fsbno, cursor->ino);
- bad++;
- }
- if (nodehdr.level != this_level) {
- do_warn(
- _("bad level %d in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
- nodehdr.level,
- dabno, fsbno, cursor->ino);
- bad++;
- }
- if (bad) {
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "verify_da_path returns 1 (bad) #4\n");
-#endif
- libxfs_putbuf(bp);
- return(1);
- }
-
- /*
- * update cursor, write out the *current* level if
- * required. don't write out the descendant level
- */
- ASSERT(cursor->level[this_level].dirty == 0 ||
- (cursor->level[this_level].dirty && !no_modify));
-
- if (cursor->level[this_level].dirty && !no_modify)
- libxfs_writebuf(cursor->level[this_level].bp, 0);
- else
- libxfs_putbuf(cursor->level[this_level].bp);
-
- /* switch cursor to point at the new buffer we just read */
- cursor->level[this_level].bp = bp;
- cursor->level[this_level].dirty = 0;
- cursor->level[this_level].bno = dabno;
- cursor->level[this_level].hashval =
- be32_to_cpu(btree[0].hashval);
-#ifdef XR_DIR_TRACE
- cursor->level[this_level].n = newnode;
-#endif
- entry = cursor->level[this_level].index = 0;
-
- /*
- * We want to rewrite the buffer on a CRC error seeing as it
- * contains what appears to be a valid node block, but only if
- * we are fixing errors.
- */
- if (bp->b_error == EFSBADCRC && !no_modify)
- cursor->level[this_level].dirty++;
- }
- /*
- * ditto for block numbers
- */
- if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before)) {
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "bad directory btree pointer, child bno "
- "should be %d, block bno is %d, hashval is %u\n",
- be32_to_cpu(btree[entry].before),
- cursor->level[p_level].bno,
- cursor->level[p_level].hashval);
- fprintf(stderr, "verify_da_path returns 1 (bad) #1a\n");
-#endif
- return(1);
- }
- /*
- * ok, now validate last hashvalue in the descendant
- * block against the hashval in the current entry
- */
- if (cursor->level[p_level].hashval !=
- be32_to_cpu(btree[entry].hashval)) {
- if (!no_modify) {
- do_warn(_("correcting bad hashval in interior "
- "dir/attr block\n\tin (level %d) in "
- "inode %" PRIu64 ".\n"),
- this_level, cursor->ino);
- btree[entry].hashval = cpu_to_be32(
- cursor->level[p_level].hashval);
- cursor->level[this_level].dirty++;
- } else {
- do_warn(_("would correct bad hashval in interior "
- "dir/attr block\n\tin (level %d) in "
- "inode %" PRIu64 ".\n"),
- this_level, cursor->ino);
- }
- }
- /*
- * increment index for this level to point to next entry
- * (which should point to the next descendant block)
- */
- cursor->level[this_level].index++;
-#ifdef XR_DIR_TRACE
- fprintf(stderr, "verify_da_path returns 0 (ok)\n");
-#endif
- return(0);
-}
-
/*
* For attribute repair, there are 3 formats to worry about. First, is
* shortform attributes which reside in the inode. Second is the leaf
* fork being emptied and put in shortform format.
*/
+static int
+attr_namecheck(
+ uint8_t *name,
+ int length)
+{
+ return namecheck((char *)name, length, false);
+}
+
/*
* This routine just checks what security needs are for attribute values
* only called when root flag is set, otherwise these names could exist in
* If value is non-zero, then a remote attribute is being passed in
*/
static int
-valuecheck(char *namevalue, char *value, int namelen, int valuelen)
+valuecheck(
+ struct xfs_mount *mp,
+ char *namevalue,
+ char *value,
+ int namelen,
+ int valuelen)
{
/* for proper alignment issues, get the structs and memmove the values */
xfs_mac_label_t macl;
- xfs_acl_t thisacl;
void *valuep;
int clearit = 0;
- if ((strncmp(namevalue, SGI_ACL_FILE, SGI_ACL_FILE_SIZE) == 0) ||
- (strncmp(namevalue, SGI_ACL_DEFAULT,
- SGI_ACL_DEFAULT_SIZE) == 0)) {
+ if ((namelen == SGI_ACL_FILE_SIZE &&
+ strncmp(namevalue, SGI_ACL_FILE, SGI_ACL_FILE_SIZE) == 0) ||
+ (namelen == SGI_ACL_DEFAULT_SIZE &&
+ strncmp(namevalue, SGI_ACL_DEFAULT, SGI_ACL_DEFAULT_SIZE) == 0)) {
if (value == NULL) {
- memset(&thisacl, 0, sizeof(xfs_acl_t));
- memmove(&thisacl, namevalue+namelen, valuelen);
- valuep = &thisacl;
+ valuep = malloc(valuelen);
+ if (!valuep)
+ do_error(_("No memory for ACL check!\n"));
+ memcpy(valuep, namevalue + namelen, valuelen);
} else
valuep = value;
- if (xfs_acl_valid((xfs_acl_disk_t *)valuep) != 0) {
+ if (xfs_acl_valid(mp, valuep) != 0) {
clearit = 1;
do_warn(
_("entry contains illegal value in attribute named SGI_ACL_FILE "
"or SGI_ACL_DEFAULT\n"));
}
+
+ if (valuep != value)
+ free(valuep);
+
} else if (strncmp(namevalue, SGI_MAC_FILE, SGI_MAC_FILE_SIZE) == 0) {
if (value == NULL) {
memset(&macl, 0, sizeof(xfs_mac_label_t));
*/
static int
process_shortform_attr(
+ struct xfs_mount *mp,
xfs_ino_t ino,
xfs_dinode_t *dip,
int *repair)
do_warn(
_("there are no attributes in the fork for inode %" PRIu64 "\n"),
ino);
- asf->hdr.totsize =
+ asf->hdr.totsize =
cpu_to_be16(sizeof(xfs_attr_sf_hdr_t));
*repair = 1;
return(1);
}
}
- /* namecheck checks for / and null terminated for file names.
- * attributes names currently follow the same rules.
- */
- if (namecheck((char *)¤tentry->nameval[0],
- currententry->namelen)) {
+ /* namecheck checks for null chars in attr names. */
+ if (attr_namecheck(currententry->nameval,
+ currententry->namelen)) {
do_warn(
_("entry contains illegal character in shortform attribute name\n"));
junkit = 1;
/* Only check values for root security attributes */
if (currententry->flags & XFS_ATTR_ROOT)
- junkit = valuecheck((char *)¤tentry->nameval[0],
- NULL, currententry->namelen,
+ junkit |= valuecheck(mp,
+ (char *)¤tentry->nameval[0],
+ NULL, currententry->namelen,
currententry->valuelen);
remainingspace = remainingspace -
_("removing attribute entry %d for inode %" PRIu64 "\n"),
i, ino);
tempentry = (xfs_attr_sf_entry_t *)
- ((__psint_t) currententry +
+ ((intptr_t) currententry +
XFS_ATTR_SF_ENTSIZE(currententry));
memmove(currententry,tempentry,remainingspace);
asf->hdr.count -= 1;
}
/* Let's get ready for the next entry... */
- nextentry = (xfs_attr_sf_entry_t *)((__psint_t) nextentry +
+ nextentry = (xfs_attr_sf_entry_t *)((intptr_t) nextentry +
XFS_ATTR_SF_ENTSIZE(currententry));
currentsize = currentsize + XFS_ATTR_SF_ENTSIZE(currententry);
rmtval_get(xfs_mount_t *mp, xfs_ino_t ino, blkmap_t *blkmap,
xfs_dablk_t blocknum, int valuelen, char* value)
{
- xfs_dfsbno_t bno;
+ xfs_fsblock_t bno;
xfs_buf_t *bp;
int clearit = 0, i = 0, length = 0, amountdone = 0;
int hdrsize = 0;
/* Note that valuelen is not a multiple of blocksize */
while (amountdone < valuelen) {
bno = blkmap_get(blkmap, blocknum + i);
- if (bno == NULLDFSBNO) {
+ if (bno == NULLFSBLOCK) {
do_warn(
_("remote block for attributes of inode %" PRIu64 " is missing\n"), ino);
clearit = 1;
break;
}
- if (bp->b_error == EFSBADCRC || bp->b_error == EFSCORRUPTED) {
+ if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) {
do_warn(
_("Corrupt remote block for attributes of inode %" PRIu64 "\n"), ino);
+ libxfs_putbuf(bp);
clearit = 1;
break;
}
- ASSERT(mp->m_sb.sb_blocksize == XFS_BUF_COUNT(bp));
+ ASSERT(mp->m_sb.sb_blocksize == bp->b_bcount);
- length = MIN(XFS_BUF_COUNT(bp) - hdrsize, valuelen - amountdone);
- memmove(value, XFS_BUF_PTR(bp) + hdrsize, length);
+ length = min(bp->b_bcount - hdrsize, valuelen - amountdone);
+ memmove(value, bp->b_addr + hdrsize, length);
amountdone += length;
value += length;
i++;
static int
process_leaf_attr_local(
+ struct xfs_mount *mp,
xfs_attr_leafblock_t *leaf,
int i,
xfs_attr_leaf_entry_t *entry,
xfs_attr_leaf_name_local_t *local;
local = xfs_attr3_leaf_name_local(leaf, i);
- if (local->namelen == 0 || namecheck((char *)&local->nameval[0],
+ if (local->namelen == 0 || attr_namecheck(local->nameval,
local->namelen)) {
do_warn(
_("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"),
/* Only check values for root security attributes */
if (entry->flags & XFS_ATTR_ROOT) {
- if (valuecheck((char *)&local->nameval[0], NULL,
+ if (valuecheck(mp, (char *)&local->nameval[0], NULL,
local->namelen, be16_to_cpu(local->valuelen))) {
do_warn(
_("bad security value for attribute entry %d in attr block %u, inode %" PRIu64 "\n"),
return -1;
}
}
- return xfs_attr_leaf_entsize_local(local->namelen,
+ return xfs_attr_leaf_entsize_local(local->namelen,
be16_to_cpu(local->valuelen));
}
remotep = xfs_attr3_leaf_name_remote(leaf, i);
- if (remotep->namelen == 0 || namecheck((char *)&remotep->name[0],
- remotep->namelen) ||
- be32_to_cpu(entry->hashval) !=
- libxfs_da_hashname((uchar_t *)&remotep->name[0],
+ if (remotep->namelen == 0 || attr_namecheck(remotep->name,
+ remotep->namelen) ||
+ be32_to_cpu(entry->hashval) !=
+ libxfs_da_hashname((unsigned char *)&remotep->name[0],
remotep->namelen) ||
be32_to_cpu(entry->hashval) < last_hashval ||
be32_to_cpu(remotep->valueblk) == 0) {
return -1;
}
- if (!(entry->flags & XFS_ATTR_ROOT))
- goto out;
-
value = malloc(be32_to_cpu(remotep->valuelen));
if (value == NULL) {
do_warn(
i, ino);
goto bad_free_out;
}
- if (valuecheck((char *)&remotep->name[0], value, remotep->namelen,
+ if ((entry->flags & XFS_ATTR_ROOT) &&
+ valuecheck(mp, (char *)&remotep->name[0], value, remotep->namelen,
be32_to_cpu(remotep->valuelen))) {
do_warn(
_("remote attribute value check failed for entry %d, inode %" PRIu64 "\n"),
da_freemap_t *attr_freemap;
struct xfs_attr3_icleaf_hdr leafhdr;
- xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
+ xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
clearit = usedbs = 0;
firstb = mp->m_sb.sb_blocksize;
stop = xfs_attr3_leaf_hdr_size(leaf);
/* does the count look sorta valid? */
- if (leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop >
- XFS_LBSIZE(mp)) {
+ if (!leafhdr.count ||
+ leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop >
+ mp->m_sb.sb_blocksize) {
do_warn(
_("bad attribute count %d in attr block %u, inode %" PRIu64 "\n"),
leafhdr.count, da_bno, ino);
i < leafhdr.count; i++, entry++) {
/* check if index is within some boundary. */
- if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) {
+ if (be16_to_cpu(entry->nameidx) > mp->m_sb.sb_blocksize) {
do_warn(
_("bad attribute nameidx %d in attr block %u, inode %" PRIu64 "\n"),
be16_to_cpu(entry->nameidx), da_bno, ino);
}
/* mark the entry used */
- start = (__psint_t)entry - (__psint_t)leaf;
+ start = (intptr_t)entry - (intptr_t)leaf;
stop = start + sizeof(xfs_attr_leaf_entry_t);
if (set_da_freemap(mp, attr_freemap, start, stop)) {
do_warn(
break; /* got an overlap */
}
- if (entry->flags & XFS_ATTR_LOCAL)
- thissize = process_leaf_attr_local(leaf, i, entry,
+ if (entry->flags & XFS_ATTR_LOCAL)
+ thissize = process_leaf_attr_local(mp, leaf, i, entry,
last_hashval, da_bno, ino);
else
thissize = process_leaf_attr_remote(leaf, i, entry,
last_hashval, da_bno, ino,
mp, blkmap);
if (thissize < 0) {
- clearit = 1;
+ clearit = 1;
break;
}
* since the block will get compacted anyhow by the kernel.
*/
- if ((leafhdr.holes == 0 &&
+ if ((leafhdr.holes == 0 &&
firstb != leafhdr.firstused) ||
leafhdr.firstused > firstb) {
if (!no_modify) {
do_warn(
_("- resetting first used heap value from %d to %d in "
"block %u of attribute fork of inode %" PRIu64 "\n"),
- leafhdr.firstused,
+ leafhdr.firstused,
firstb, da_bno, ino);
leafhdr.firstused = firstb;
*repair = 1;
do_warn(
_("- would reset first used value from %d to %d in "
"block %u of attribute fork of inode %" PRIu64 "\n"),
- leafhdr.firstused,
+ leafhdr.firstused,
firstb, da_bno, ino);
}
}
do_warn(
_("- resetting usedbytes cnt from %d to %d in "
"block %u of attribute fork of inode %" PRIu64 "\n"),
- leafhdr.usedbytes,
+ leafhdr.usedbytes,
usedbs, da_bno, ino);
leafhdr.usedbytes = usedbs;
*repair = 1;
do_warn(
_("- would reset usedbytes cnt from %d to %d in "
"block %u of attribute fork of %" PRIu64 "\n"),
- leafhdr.usedbytes,
+ leafhdr.usedbytes,
usedbs, da_bno, ino);
}
}
* we can add it then.
*/
}
+ /*
+ * If we're just going to zap the block, don't pretend like we
+ * repaired it, because repairing the block stops the clear
+ * operation.
+ */
+ if (clearit)
+ *repair = 0;
if (*repair)
- xfs_attr3_leaf_hdr_to_disk(leaf, &leafhdr);
+ xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, leaf, &leafhdr);
free(attr_freemap);
return (clearit); /* and repair */
xfs_attr_leafblock_t *leaf;
xfs_buf_t *bp;
xfs_ino_t ino;
- xfs_dfsbno_t dev_bno;
+ xfs_fsblock_t dev_bno;
xfs_dablk_t da_bno;
xfs_dablk_t prev_bno;
xfs_dahash_t current_hashval = 0;
da_bno = da_cursor->level[0].bno;
ino = da_cursor->ino;
+ /*
+ * 0 is the root block and no block
+ * pointer can point to the root block of the btree
+ */
+ if (da_bno == 0) {
+ do_warn(
+ _("btree cycle detected in attribute fork for inode %" PRIu64 "\n"),
+ ino);
+ goto error_out;
+ }
+
prev_bno = 0;
do {
repair = 0;
dev_bno = blkmap_get(da_cursor->blkmap, da_bno);
- /*
- * 0 is the root block and no block
- * pointer can point to the root block of the btree
- */
- ASSERT(da_bno != 0);
-
- if (dev_bno == NULLDFSBNO) {
+ if (dev_bno == NULLFSBLOCK) {
do_warn(
_("can't map block %u for attribute fork for inode %" PRIu64 "\n"),
da_bno, ino);
da_bno, dev_bno, ino);
goto error_out;
}
- if (bp->b_error == EFSBADCRC)
- repair++;
leaf = bp->b_addr;
- xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
+ xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
/* check magic number for leaf directory btree block */
if (!(leafhdr.magic == XFS_ATTR_LEAF_MAGIC ||
prev_bno = da_bno;
da_bno = leafhdr.forw;
- if (da_bno != 0 && verify_da_path(mp, da_cursor, 0)) {
- libxfs_putbuf(bp);
- goto error_out;
+ if (da_bno != 0) {
+ if (verify_da_path(mp, da_cursor, 0, XFS_ATTR_FORK)) {
+ libxfs_putbuf(bp);
+ goto error_out;
+ }
}
current_hashval = greatest_hashval;
+ /*
+ * If block looks ok but CRC didn't match, make sure to
+ * recompute it.
+ */
+ if (!no_modify && bp->b_error == -EFSBADCRC)
+ repair++;
if (repair && !no_modify)
libxfs_writebuf(bp, 0);
libxfs_putbuf(bp);
} while (da_bno != 0);
- if (verify_final_da_path(mp, da_cursor, 0)) {
+ if (verify_final_da_path(mp, da_cursor, 0, XFS_ATTR_FORK)) {
/*
* verify the final path up (right-hand-side) if still ok
*/
*/
memset(&da_cursor, 0, sizeof(da_bt_cursor_t));
da_cursor.active = 0;
- da_cursor.type = 0;
da_cursor.ino = ino;
da_cursor.dip = dip;
da_cursor.greatest_bno = 0;
return (process_leaf_attr_level(mp, &da_cursor));
}
+/* check v5 metadata */
+static int
+__check_attr_header(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp,
+ xfs_ino_t ino)
+{
+ struct xfs_da3_blkinfo *info = bp->b_addr;
+
+ if (info->hdr.magic != cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) &&
+ info->hdr.magic != cpu_to_be16(XFS_DA3_NODE_MAGIC))
+ return 0;
+
+ /* verify owner */
+ if (be64_to_cpu(info->owner) != ino) {
+ do_warn(
+_("expected owner inode %" PRIu64 ", got %llu, attr block %" PRIu64 "\n"),
+ ino, (unsigned long long)be64_to_cpu(info->owner),
+ bp->b_bn);
+ return 1;
+ }
+ /* verify block number */
+ if (be64_to_cpu(info->blkno) != bp->b_bn) {
+ do_warn(
+_("expected block %" PRIu64 ", got %llu, inode %" PRIu64 "attr block\n"),
+ bp->b_bn, (unsigned long long)be64_to_cpu(info->blkno),
+ ino);
+ return 1;
+ }
+ /* verify uuid */
+ if (platform_uuid_compare(&info->uuid, &mp->m_sb.sb_meta_uuid) != 0) {
+ do_warn(
+_("wrong FS UUID, inode %" PRIu64 " attr block %" PRIu64 "\n"),
+ ino, bp->b_bn);
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Start processing for a leaf or fuller btree.
* A leaf directory is one where the attribute fork is too big for
int *repair) /* out - 1 if something was fixed */
{
xfs_attr_leafblock_t *leaf;
- xfs_dfsbno_t bno;
+ xfs_fsblock_t bno;
xfs_buf_t *bp;
xfs_dahash_t next_hashval;
int repairlinks = 0;
struct xfs_attr3_icleaf_hdr leafhdr;
+ int error;
*repair = 0;
bno = blkmap_get(blkmap, 0);
- if ( bno == NULLDFSBNO ) {
- if (dip->di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ if ( bno == NULLFSBLOCK ) {
+ if (dip->di_aformat == XFS_DINODE_FMT_EXTENTS &&
be16_to_cpu(dip->di_anextents) == 0)
return(0); /* the kernel can handle this state */
do_warn(
ino);
return(1);
}
- if (bp->b_error == EFSBADCRC)
+ if (bp->b_error == -EFSBADCRC)
(*repair)++;
+ /* is this block sane? */
+ if (__check_attr_header(mp, bp, ino)) {
+ *repair = 0;
+ libxfs_putbuf(bp);
+ return 1;
+ }
+
/* verify leaf block */
- leaf = (xfs_attr_leafblock_t *)XFS_BUF_PTR(bp);
- xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
+ leaf = bp->b_addr;
+ xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
/* check sibling pointers in leaf block or root block 0 before
* we have to release the btree block
repairlinks = 1;
leafhdr.forw = 0;
leafhdr.back = 0;
- xfs_attr3_leaf_hdr_to_disk(leaf, &leafhdr);
+ xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo,
+ leaf, &leafhdr);
} else {
do_warn(
_("would clear forw/back pointers in block 0 for attributes in inode %" PRIu64 "\n"), ino);
case XFS_ATTR3_LEAF_MAGIC:
if (process_leaf_attr_block(mp, leaf, 0, ino, blkmap,
0, &next_hashval, repair)) {
+ *repair = 0;
/* the block is bad. lose the attribute fork. */
libxfs_putbuf(bp);
return(1);
libxfs_writebuf(bp, 0);
} else
libxfs_putbuf(bp);
- return (process_node_attr(mp, ino, dip, blkmap)); /* + repair */
+ error = process_node_attr(mp, ino, dip, blkmap); /* + repair */
+ if (error)
+ *repair = 0;
+ return error;
default:
do_warn(
_("bad attribute leaf magic # %#x for dir ino %" PRIu64 "\n"),
be16_to_cpu(leaf->hdr.info.magic), ino);
libxfs_putbuf(bp);
+ *repair = 0;
return(1);
}
static int
-xfs_acl_from_disk(struct xfs_acl **aclp, struct xfs_acl_disk *dacl)
+xfs_acl_from_disk(
+ struct xfs_mount *mp,
+ struct xfs_icacl **aclp,
+ struct xfs_acl *dacl)
{
+ struct xfs_icacl *acl;
+ struct xfs_icacl_entry *ace;
+ struct xfs_acl_entry *dace;
int count;
- xfs_acl_t *acl;
- xfs_acl_entry_t *ace;
- xfs_acl_entry_disk_t *dace, *end;
+ int i;
count = be32_to_cpu(dacl->acl_cnt);
- if (count > XFS_ACL_MAX_ENTRIES) {
+ if (count > XFS_ACL_MAX_ENTRIES(mp)) {
do_warn(_("Too many ACL entries, count %d\n"), count);
*aclp = NULL;
return EINVAL;
}
- end = &dacl->acl_entry[0] + count;
- acl = malloc((int)((char *)end - (char *)dacl));
+ acl = malloc(sizeof(struct xfs_icacl) +
+ count * sizeof(struct xfs_icacl_entry));
if (!acl) {
do_warn(_("cannot malloc enough for ACL attribute\n"));
do_warn(_("SKIPPING this ACL\n"));
}
acl->acl_cnt = count;
- ace = &acl->acl_entry[0];
- for (dace = &dacl->acl_entry[0]; dace < end; ace++, dace++) {
+ for (i = 0; i < count; i++) {
+ ace = &acl->acl_entry[i];
+ dace = &dacl->acl_entry[i];
+
ace->ae_tag = be32_to_cpu(dace->ae_tag);
ace->ae_id = be32_to_cpu(dace->ae_id);
ace->ae_perm = be16_to_cpu(dace->ae_perm);
if (aformat == XFS_DINODE_FMT_LOCAL) {
ASSERT(be16_to_cpu(asf->hdr.totsize) <=
XFS_DFORK_ASIZE(dip, mp));
- err = process_shortform_attr(ino, dip, repair);
+ err = process_shortform_attr(mp, ino, dip, repair);
} else if (aformat == XFS_DINODE_FMT_EXTENTS ||
aformat == XFS_DINODE_FMT_BTREE) {
err = process_longform_attr(mp, ino, dip, blkmap,
* Validate an ACL
*/
static int
-xfs_acl_valid(xfs_acl_disk_t *daclp)
+xfs_acl_valid(
+ struct xfs_mount *mp,
+ struct xfs_acl *daclp)
{
- xfs_acl_t *aclp = NULL;
- xfs_acl_entry_t *entry, *e;
+ struct xfs_icacl *aclp = NULL;
+ struct xfs_icacl_entry *entry, *e;
int user = 0, group = 0, other = 0, mask = 0, mask_required = 0;
int i, j;
if (daclp == NULL)
goto acl_invalid;
- switch (xfs_acl_from_disk(&aclp, daclp)) {
+ switch (xfs_acl_from_disk(mp, &aclp, daclp)) {
case ENOMEM:
return 0;
case EINVAL: