This patch addresses the following xfs_repair issues:
- Can rebuild most corrupted directories. Some will be
beyond repair and the contents of those will end up
in lost+found.
- Fixed potential problems with the duplicate name
checking by properly referencing the buffers where
the names are stored.
- Unified the two hash lists used in directory checks.
- Fixed a case where incorrectly reference counted and
dirty buffers were never written to disk (most
common observation of this is an inaccessible
lost+found directory after a repair).
For those that are keen to fix the 16777216 problem, give this patch a go.
===========================================================================
xfsprogs/include/cache.h
===========================================================================
--- a/xfsprogs/include/cache.h 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/include/cache.h 2006-07-27 16:17:47.804986322 +1000
@@ -30,6 +30,7 @@ struct cache_node;
typedef void *cache_key_t;
typedef void (*cache_walk_t)(struct cache_node *);
typedef struct cache_node * (*cache_node_alloc_t)(void);
+typedef void (*cache_node_flush_t)(struct cache_node *);
typedef void (*cache_node_relse_t)(struct cache_node *);
typedef unsigned int (*cache_node_hash_t)(cache_key_t, unsigned int);
typedef int (*cache_node_compare_t)(struct cache_node *, cache_key_t);
@@ -38,6 +39,7 @@ typedef unsigned int (*cache_bulk_relse_
struct cache_operations {
cache_node_hash_t hash;
cache_node_alloc_t alloc;
+ cache_node_flush_t flush;
cache_node_relse_t relse;
cache_node_compare_t compare;
cache_bulk_relse_t bulkrelse; /* optional */
@@ -49,6 +51,7 @@ struct cache {
pthread_mutex_t c_mutex; /* node count mutex */
cache_node_hash_t hash; /* node hash function */
cache_node_alloc_t alloc; /* allocation function */
+ cache_node_flush_t flush; /* flush dirty data function */
cache_node_relse_t relse; /* memory free function */
cache_node_compare_t compare; /* comparison routine */
cache_bulk_relse_t bulkrelse; /* bulk release routine */
@@ -75,6 +78,7 @@ struct cache *cache_init(unsigned int, s
void cache_destroy(struct cache *);
void cache_walk(struct cache *, cache_walk_t);
void cache_purge(struct cache *);
+void cache_flush(struct cache *);
int cache_node_get(struct cache *, cache_key_t, struct cache_node **);
void cache_node_put(struct cache_node *);
===========================================================================
xfsprogs/include/libxfs.h
===========================================================================
--- a/xfsprogs/include/libxfs.h 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/include/libxfs.h 2006-07-27 16:34:45.308344014 +1000
@@ -257,6 +257,7 @@ extern int libxfs_writebuf_int (xfs_buf_
extern struct cache *libxfs_bcache;
extern struct cache_operations libxfs_bcache_operations;
extern void libxfs_bcache_purge (void);
+extern void libxfs_bcache_flush (void);
extern xfs_buf_t *libxfs_getbuf (dev_t, xfs_daddr_t, int);
extern void libxfs_putbuf (xfs_buf_t *);
extern void libxfs_purgebuf (xfs_buf_t *);
@@ -467,6 +468,8 @@ extern int libxfs_bmap_finish (xfs_trans
xfs_fsblock_t, int *);
extern int libxfs_bmap_next_offset (xfs_trans_t *, xfs_inode_t *,
xfs_fileoff_t *, int);
+extern int libxfs_bmap_last_offset(xfs_trans_t *, xfs_inode_t *,
+ xfs_fileoff_t *, int);
extern int libxfs_bunmapi (xfs_trans_t *, xfs_inode_t *, xfs_fileoff_t,
xfs_filblks_t, int, xfs_extnum_t,
xfs_fsblock_t *, xfs_bmap_free_t *, int *);
===========================================================================
xfsprogs/libxfs/cache.c
===========================================================================
--- a/xfsprogs/libxfs/cache.c 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/cache.c 2006-07-27 17:42:43.812685388 +1000
@@ -60,6 +60,7 @@ cache_init(
cache->c_hashsize = hashsize;
cache->hash = cache_operations->hash;
cache->alloc = cache_operations->alloc;
+ cache->flush = cache_operations->flush;
cache->relse = cache_operations->relse;
cache->compare = cache_operations->compare;
cache->bulkrelse = cache_operations->bulkrelse ?
@@ -422,6 +423,39 @@ cache_purge(
cache_abort();
}
#endif
+ /* flush any remaining nodes to disk */
+ cache_flush(cache);
+}
+
+/*
+ * Flush all nodes in the cache to disk.
+ */
+void
+cache_flush(
+ struct cache * cache)
+{
+ struct cache_hash * hash;
+ struct list_head * head;
+ struct list_head * pos;
+ struct cache_node * node;
+ int i;
+
+ if (!cache->flush)
+ return;
+
+ for (i = 0; i < cache->c_hashsize; i++) {
+ hash = &cache->c_hash[i];
+
+ pthread_mutex_lock(&hash->ch_mutex);
+ head = &hash->ch_list;
+ for (pos = head->next; pos != head; pos = pos->next) {
+ node = (struct cache_node *)pos;
+ pthread_mutex_lock(&node->cn_mutex);
+ cache->flush(node);
+ pthread_mutex_unlock(&node->cn_mutex);
+ }
+ pthread_mutex_unlock(&hash->ch_mutex);
+ }
}
#define HASH_REPORT (3*HASH_CACHE_RATIO)
===========================================================================
xfsprogs/libxfs/rdwr.c
===========================================================================
--- a/xfsprogs/libxfs/rdwr.c 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/rdwr.c 2006-07-27 16:40:56.612373938 +1000
@@ -416,6 +416,15 @@ libxfs_iomove(xfs_buf_t *bp, uint boff,
}
static void
+libxfs_bflush(struct cache_node *node)
+{
+ xfs_buf_t *bp = (xfs_buf_t *)node;
+
+ if ((bp != NULL) && (bp->b_flags & LIBXFS_B_DIRTY))
+ libxfs_writebufr(bp);
+}
+
+static void
libxfs_brelse(struct cache_node *node)
{
xfs_buf_t *bp = (xfs_buf_t *)node;
@@ -442,9 +451,16 @@ libxfs_bcache_purge(void)
cache_purge(libxfs_bcache);
}
+void
+libxfs_bcache_flush(void)
+{
+ cache_flush(libxfs_bcache);
+}
+
struct cache_operations libxfs_bcache_operations = {
/* .hash */ libxfs_bhash,
/* .alloc */ libxfs_balloc,
+ /* .flush */ libxfs_bflush,
/* .relse */ libxfs_brelse,
/* .compare */ libxfs_bcompare,
/* .bulkrelse */ NULL /* TODO: lio_listio64 interface? */
@@ -649,6 +665,7 @@ libxfs_icache_purge(void)
struct cache_operations libxfs_icache_operations = {
/* .hash */ libxfs_ihash,
/* .alloc */ libxfs_ialloc,
+ /* .flush */ NULL,
/* .relse */ libxfs_irelse,
/* .compare */ libxfs_icompare,
/* .bulkrelse */ NULL
===========================================================================
xfsprogs/libxfs/xfs.h
===========================================================================
--- a/xfsprogs/libxfs/xfs.h 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/xfs.h 2006-07-25 12:05:31.917896892 +1000
@@ -98,6 +98,7 @@
#define xfs_bmapi_single libxfs_bmapi_single
#define xfs_bmap_finish libxfs_bmap_finish
#define xfs_bmap_del_free libxfs_bmap_del_free
+#define xfs_bmap_last_offset libxfs_bmap_last_offset
#define xfs_bunmapi libxfs_bunmapi
#define xfs_free_extent libxfs_free_extent
#define xfs_rtfree_extent libxfs_rtfree_extent
===========================================================================
xfsprogs/repair/phase6.c
===========================================================================
--- a/xfsprogs/repair/phase6.c 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/repair/phase6.c 2006-07-28 11:30:50.033905530 +1000
@@ -36,43 +36,35 @@ static int orphanage_entered;
/*
* Data structures and routines to keep track of directory entries
- * and whether their leaf entry has been seen
+ * and whether their leaf entry has been seen. Also used for name
+ * duplicate checking and rebuilding step if required.
*/
typedef struct dir_hash_ent {
- struct dir_hash_ent *next; /* pointer to next entry */
+ struct dir_hash_ent *nextbyaddr;/* pointer to next entry */
+ struct dir_hash_ent *nextbyhash;
+ struct dir_hash_ent *nextbyorder;
xfs_dir2_leaf_entry_t ent; /* address and hash value */
+ xfs_ino_t inum; /* inode of name */
short junkit; /* name starts with / */
short seen; /* have seen leaf entry */
+ int namelen;/* length of name */
+ uchar_t *name; /* pointer to name (no NULL) */
} dir_hash_ent_t;
typedef struct dir_hash_tab {
int size; /* size of hash table */
- dir_hash_ent_t *tab[1];/* actual hash table, variable size */
+ int names_duped;
+ dir_hash_ent_t *first;
+ dir_hash_ent_t *last;
+ dir_hash_ent_t **byhash;/* actual hash table, variable size */
+ dir_hash_ent_t **byaddr;/* actual hash table, variable size */
} dir_hash_tab_t;
+
#define DIR_HASH_TAB_SIZE(n) \
- (offsetof(dir_hash_tab_t, tab) + (sizeof(dir_hash_ent_t *) * (n)))
+ (sizeof(dir_hash_tab_t) + (sizeof(dir_hash_ent_t *) * (n) * 2))
#define DIR_HASH_FUNC(t,a) ((a) % (t)->size)
/*
- * Track names to check for duplicates in a directory.
- */
-
-typedef struct name_hash_ent {
- struct name_hash_ent *next; /* pointer to next entry */
- xfs_dahash_t hashval;/* hash value of name */
- int namelen;/* length of name */
- uchar_t *name; /* pointer to name (no NULL) */
-} name_hash_ent_t;
-
-typedef struct name_hash_tab {
- int size; /* size of hash table */
- name_hash_ent_t *tab[1];/* actual hash table, variable size */
-} name_hash_tab_t;
-#define NAME_HASH_TAB_SIZE(n) \
- (offsetof(name_hash_tab_t, tab) + (sizeof(name_hash_ent_t *) * (n)))
-#define NAME_HASH_FUNC(t,a) ((a) % (t)->size)
-
-/*
* Track the contents of the freespace table in a directory.
*/
typedef struct freetab {
@@ -94,28 +86,75 @@ typedef struct freetab {
#define DIR_HASH_CK_BADSTALE 5
#define DIR_HASH_CK_TOTAL 6
-static void
+/*
+ * Returns 0 if the name already exists (ie. a duplicate)
+ */
+static int
dir_hash_add(
dir_hash_tab_t *hashtab,
- xfs_dahash_t hash,
- xfs_dir2_dataptr_t addr,
- int junk)
-{
- int i;
+ xfs_dir2_dataptr_t addr,
+ xfs_ino_t inum,
+ int namelen,
+ uchar_t *name)
+{
+ xfs_dahash_t hash = 0;
+ int byaddr;
+ int byhash = 0;
dir_hash_ent_t *p;
-
- i = DIR_HASH_FUNC(hashtab, addr);
+ int dup;
+ short junk;
+
+ junk = name[0] == '/';
+ byaddr = DIR_HASH_FUNC(hashtab, addr);
+ dup = 0;
+
+ if (!junk) {
+ hash = libxfs_da_hashname(name, namelen);
+ byhash = DIR_HASH_FUNC(hashtab, hash);
+
+ /*
+ * search hash bucket for existing name.
+ */
+ for (p = hashtab->byhash[byhash]; p; p = p->nextbyhash) {
+ if (p->ent.hashval == hash && p->namelen == namelen) {
+ if (memcmp(p->name, name, namelen) == 0) {
+ dup = 1;
+ break;
+ }
+ }
+ }
+ }
+
if ((p = malloc(sizeof(*p))) == NULL)
do_error(_("malloc failed in dir_hash_add (%u bytes)\n"),
sizeof(*p));
- p->next = hashtab->tab[i];
- hashtab->tab[i] = p;
- if (!(p->junkit = junk))
+
+ p->nextbyaddr = hashtab->byaddr[byaddr];
+ hashtab->byaddr[byaddr] = p;
+ if (hashtab->last)
+ hashtab->last->nextbyorder = p;
+ else
+ hashtab->first = p;
+ p->nextbyorder = NULL;
+ hashtab->last = p;
+
+ if (!(p->junkit = junk)) {
p->ent.hashval = hash;
+ p->nextbyhash = hashtab->byhash[byhash];
+ hashtab->byhash[byhash] = p;
+ }
p->ent.address = addr;
+ p->inum = inum;
p->seen = 0;
+ p->namelen = namelen;
+ p->name = name;
+
+ return !dup;
}
+/*
+ * checks to see if any data entries are not in the leaf blocks
+ */
static int
dir_hash_unseen(
dir_hash_tab_t *hashtab)
@@ -124,7 +163,7 @@ dir_hash_unseen(
dir_hash_ent_t *p;
for (i = 0; i < hashtab->size; i++) {
- for (p = hashtab->tab[i]; p; p = p->next) {
+ for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
if (p->seen == 0)
return 1;
}
@@ -173,8 +212,10 @@ dir_hash_done(
dir_hash_ent_t *p;
for (i = 0; i < hashtab->size; i++) {
- for (p = hashtab->tab[i]; p; p = n) {
- n = p->next;
+ for (p = hashtab->byaddr[i]; p; p = n) {
+ n = p->nextbyaddr;
+ if (hashtab->names_duped)
+ free(p->name);
free(p);
}
}
@@ -196,6 +237,10 @@ dir_hash_init(
if ((hashtab = calloc(DIR_HASH_TAB_SIZE(hsize), 1)) == NULL)
do_error(_("calloc failed in dir_hash_init\n"));
hashtab->size = hsize;
+ hashtab->byhash = (dir_hash_ent_t**)((char *)hashtab +
+ sizeof(dir_hash_tab_t));
+ hashtab->byaddr = (dir_hash_ent_t**)((char *)hashtab +
+ sizeof(dir_hash_tab_t) + sizeof(dir_hash_ent_t*) * hsize);
return hashtab;
}
@@ -209,7 +254,7 @@ dir_hash_see(
dir_hash_ent_t *p;
i = DIR_HASH_FUNC(hashtab, addr);
- for (p = hashtab->tab[i]; p; p = p->next) {
+ for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
if (p->ent.address != addr)
continue;
if (p->seen)
@@ -222,6 +267,10 @@ dir_hash_see(
return DIR_HASH_CK_NODATA;
}
+/*
+ * checks to make sure leafs match a data entry, and that the stale
+ * count is valid.
+ */
static int
dir_hash_see_all(
dir_hash_tab_t *hashtab,
@@ -246,81 +295,25 @@ dir_hash_see_all(
}
/*
- * Returns 0 if the name already exists (ie. a duplicate)
+ * Convert name pointers into locally allocated memory
*/
-static int
-name_hash_add(
- name_hash_tab_t *nametab,
- uchar_t *name,
- int namelen)
+static void
+dir_hash_dup_names(dir_hash_tab_t *hashtab)
{
- xfs_dahash_t hash;
- int i;
- name_hash_ent_t *p;
-
- hash = libxfs_da_hashname(name, namelen);
-
- i = NAME_HASH_FUNC(nametab, hash);
-
- /*
- * search hash bucket for existing name.
- */
- for (p = nametab->tab[i]; p; p = p->next) {
- if (p->hashval == hash && p->namelen == namelen) {
- if (memcmp(p->name, name, namelen) == 0)
- return 0; /* exists */
- }
- }
-
- if ((p = malloc(sizeof(*p))) == NULL)
- do_error(_("malloc failed in name_hash_add (%u bytes)\n"),
- sizeof(*p));
+ uchar_t *name;
+ dir_hash_ent_t *p;
- p->next = nametab->tab[i];
- p->hashval = hash;
- p->name = name;
- p->namelen = namelen;
- nametab->tab[i] = p;
+ if (hashtab->names_duped)
+ return;
- return 1; /* success, no duplicate */
-}
-
-static name_hash_tab_t *
-name_hash_init(
- xfs_fsize_t size)
-{
- name_hash_tab_t *nametab;
- int hsize;
-
- hsize = size / (16 * 4);
- if (hsize > 1024)
- hsize = 1024;
- else if (hsize < 16)
- hsize = 16;
- if ((nametab = calloc(NAME_HASH_TAB_SIZE(hsize), 1)) == NULL)
- do_error(_("calloc failed in name_hash_init\n"));
- nametab->size = hsize;
- return nametab;
-}
-
-static void
-name_hash_done(
- name_hash_tab_t *nametab)
-{
- int i;
- name_hash_ent_t *n;
- name_hash_ent_t *p;
-
- for (i = 0; i < nametab->size; i++) {
- for (p = nametab->tab[i]; p; p = n) {
- n = p->next;
- free(p);
- }
+ for (p = hashtab->first; p; p = p->nextbyorder) {
+ name = malloc(p->namelen);
+ memcpy(name, p->name, p->namelen);
+ p->name = name;
}
- free(nametab);
+ hashtab->names_duped = 1;
}
-
/*
* Version 1 or 2 directory routine wrappers
*/
@@ -1385,7 +1378,8 @@ lf_block_dir_entry_check(xfs_mount_t *m
dir_stack_t *stack,
ino_tree_node_t *current_irec,
int current_ino_offset,
- name_hash_tab_t *nametab)
+ dir_hash_tab_t *hashtab,
+ xfs_dablk_t da_bno)
{
xfs_dir_leaf_entry_t *entry;
ino_tree_node_t *irec;
@@ -1545,7 +1539,9 @@ lf_block_dir_entry_check(xfs_mount_t *m
/*
* check for duplicate names in directory.
*/
- if (!name_hash_add(nametab, namest->name, entry->namelen)) {
+ if (!dir_hash_add(hashtab, (da_bno << mp->m_sb.sb_blocklog) +
+ entry->nameidx,
+ lino, entry->namelen, namest->name)) {
do_warn(
_("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
fname, lino, ino);
@@ -1635,7 +1631,7 @@ longform_dir_entry_check(xfs_mount_t *mp
dir_stack_t *stack,
ino_tree_node_t *irec,
int ino_offset,
- name_hash_tab_t *nametab)
+ dir_hash_tab_t *hashtab)
{
xfs_dir_leafblock_t *leaf;
xfs_buf_t *bp;
@@ -1677,8 +1673,6 @@ longform_dir_entry_check(xfs_mount_t *mp
leaf = (xfs_dir_leafblock_t *)XFS_BUF_PTR(bp);
- da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
-
if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) !=
XFS_DIR_LEAF_MAGIC) {
if (!no_modify) {
@@ -1699,9 +1693,11 @@ _("bad magic # (0x%x) for dir ino %llu l
}
if (!skipit)
- lf_block_dir_entry_check(mp, ino, leaf, &dirty,
- num_illegal, need_dot, stack,
- irec, ino_offset, nametab);
+ lf_block_dir_entry_check(mp, ino, leaf, &dirty,
+ num_illegal, need_dot, stack, irec,
+ ino_offset, hashtab, da_bno);
+
+ da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
ASSERT(dirty == 0 || (dirty && !no_modify));
@@ -1745,6 +1741,127 @@ _("can't map leaf block %d in dir %llu,
}
/*
+ * Unexpected failure during the rebuild will leave the entries in
+ * lost+found on the next run
+ */
+
+static void
+longform_dir2_rebuild(
+ xfs_mount_t *mp,
+ xfs_ino_t ino,
+ xfs_inode_t *ip,
+ dir_hash_tab_t *hashtab)
+{
+ int error;
+ int nres;
+ xfs_trans_t *tp;
+ xfs_fileoff_t lastblock;
+ xfs_fsblock_t firstblock;
+ xfs_bmap_free_t flist;
+ xfs_ino_t parentino;
+ xfs_inode_t *pip;
+ int byhash;
+ dir_hash_ent_t *p;
+ int committed;
+ int done;
+
+ /*
+ * trash directory completely and rebuild from scratch using the
+ * name/inode pairs in the hash table
+ */
+
+ do_warn(_("rebuilding directory inode %llu\n"), ino);
+
+ /*
+ * first attempt to locate the parent inode, if it can't be found,
+ * we'll use the lost+found inode
+ */
+ byhash = DIR_HASH_FUNC(hashtab, libxfs_da_hashname((uchar_t*)"..", 2));
+ parentino = orphanage_ino;
+ for (p = hashtab->byhash[byhash]; p; p = p->nextbyhash) {
+ if (p->namelen == 2 && p->name[0] == '.' && p->name[1] == '.') {
+ parentino = p->inum;
+ break;
+ }
+ }
+
+ XFS_BMAP_INIT(&flist, &firstblock);
+
+ tp = libxfs_trans_alloc(mp, 0);
+ nres = XFS_REMOVE_SPACE_RES(mp);
+ error = libxfs_trans_reserve(tp, nres, XFS_REMOVE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
+ if (error)
+ res_failed(error);
+ libxfs_trans_ijoin(tp, ip, 0);
+ libxfs_trans_ihold(tp, ip);
+
+ if ((error = libxfs_bmap_last_offset(tp, ip, &lastblock,
+ XFS_DATA_FORK)))
+ do_error(_("xfs_bmap_last_offset failed -- error - %d\n"),
+ error);
+
+ /* re-init the directory to shortform */
+ if ((error = libxfs_trans_iget(mp, tp, parentino, 0, 0, &pip)))
+ do_error(
+ _("couldn't iget parent inode %llu -- error - %d\n"),
+ parentino, error);
+
+ /* free all data, leaf, node and freespace blocks */
+
+ if ((error = libxfs_bunmapi(tp, ip, 0, lastblock,
+ XFS_BMAPI_METADATA, 0, &firstblock, &flist,
+ &done)))
+ do_error(_("xfs_bunmapi failed -- error - %d\n"), error);
+ ASSERT(done);
+
+ libxfs_dir2_init(tp, ip, pip);
+
+ error = libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
+
+ libxfs_trans_commit(tp,
+ XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC, 0);
+
+ /* go through the hash list and re-add the inodes */
+
+ for (p = hashtab->first; p; p = p->nextbyorder) {
+
+ if (p->name[0] == '/' || (p->name[0] == '.' && (p->namelen == 1
+ || (p->namelen == 2 && p->name[1] == '.'))))
+ continue;
+
+ tp = libxfs_trans_alloc(mp, 0);
+ nres = XFS_CREATE_SPACE_RES(mp, p->namelen);
+ if ((error = libxfs_trans_reserve(tp, nres,
+ XFS_CREATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT)))
+ do_error(
+ _("space reservation failed (%d), filesystem may be out of space\n"),
+ error);
+
+ libxfs_trans_ijoin(tp, ip, 0);
+ libxfs_trans_ihold(tp, ip);
+
+ XFS_BMAP_INIT(&flist, &firstblock);
+ if ((error = libxfs_dir2_createname(tp, ip, (char*)p->name,
+ p->namelen, p->inum, &firstblock, &flist,
nres)))
+ do_error(
+_("name create failed in ino %llu (%d), filesystem may be out of space\n"),
+ ino, error);
+
+ if ((error = libxfs_bmap_finish(&tp, &flist, firstblock,
+ &committed)))
+ do_error(
+ _("bmap finish failed (%d), filesystem may be out of space\n"),
+ error);
+
+ libxfs_trans_commit(tp,
+ XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC, 0);
+ }
+}
+
+
+/*
* Kill a block in a version 2 inode.
* Makes its own transaction.
*/
@@ -1807,7 +1924,6 @@ longform_dir2_entry_check_data(
xfs_dabuf_t **bpp,
dir_hash_tab_t *hashtab,
freetab_t **freetabp,
- name_hash_tab_t *nametab,
xfs_dablk_t da_bno,
int isblock)
{
@@ -1828,6 +1944,7 @@ longform_dir2_entry_check_data(
freetab_t *freetab;
int i;
int ino_offset;
+ xfs_ino_t inum;
ino_tree_node_t *irec;
int junkit;
int lastfree;
@@ -1956,8 +2073,7 @@ longform_dir2_entry_check_data(
libxfs_trans_ijoin(tp, ip, 0);
libxfs_trans_ihold(tp, ip);
libxfs_da_bjoin(tp, bp);
- if (isblock)
- libxfs_da_bhold(tp, bp);
+ libxfs_da_bhold(tp, bp);
XFS_BMAP_INIT(&flist, &firstblock);
if (INT_GET(d->hdr.magic, ARCH_CONVERT) != wantmagic) {
do_warn(_("bad directory block magic # %#x for directory inode "
@@ -1987,7 +2103,7 @@ longform_dir2_entry_check_data(
while (ptr < endptr) {
dup = (xfs_dir2_data_unused_t *)ptr;
if (INT_GET(dup->freetag, ARCH_CONVERT) ==
- XFS_DIR2_DATA_FREE_TAG) {
+ XFS_DIR2_DATA_FREE_TAG) {
if (lastfree) {
do_warn(_("directory inode %llu block %u has "
"consecutive free entries: "),
@@ -2011,10 +2127,24 @@ longform_dir2_entry_check_data(
addr = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, db, ptr - (char *)d);
dep = (xfs_dir2_data_entry_t *)ptr;
ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ inum = INT_GET(dep->inumber, ARCH_CONVERT);
lastfree = 0;
- dir_hash_add(hashtab,
- libxfs_da_hashname((uchar_t *)dep->name, dep->namelen),
- addr, dep->name[0] == '/');
+ if (!dir_hash_add(hashtab, addr, inum, dep->namelen,
+ dep->name)) {
+ do_warn(
+ _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
+ fname, inum, ip->i_ino);
+ if (!no_modify) {
+ if (verbose)
+ do_warn(
+ _(", marking entry to be junked\n"));
+ else
+ do_warn("\n");
+ } else {
+ do_warn(_(", would junk entry\n"));
+ }
+ dep->name[0] = '/';
+ }
/*
* skip bogus entries (leading '/'). they'll be deleted
* later. must still log it, else we leak references to
@@ -2029,7 +2159,7 @@ longform_dir2_entry_check_data(
junkit = 0;
bcopy(dep->name, fname, dep->namelen);
fname[dep->namelen] = '\0';
- ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) != NULLFSINO);
+ ASSERT(inum != NULLFSINO);
/*
* skip the '..' entry since it's checked when the
* directory is reached by something else. if it never
@@ -2039,7 +2169,7 @@ longform_dir2_entry_check_data(
if (dep->namelen == 2 && dep->name[0] == '.' &&
dep->name[1] == '.')
continue;
- ASSERT(no_modify || !verify_inum(mp, INT_GET(dep->inumber,
ARCH_CONVERT)));
+ ASSERT(no_modify || !verify_inum(mp, inum));
/*
* special case the . entry. we know there's only one
* '.' and only '.' points to itself because bogus entries
@@ -2049,7 +2179,7 @@ longform_dir2_entry_check_data(
* '..' is already accounted for or will be taken care
* of when directory is moved to orphanage.
*/
- if (ip->i_ino == INT_GET(dep->inumber, ARCH_CONVERT)) {
+ if (ip->i_ino == inum) {
ASSERT(dep->name[0] == '.' && dep->namelen == 1);
add_inode_ref(current_irec, current_ino_offset);
*need_dot = 0;
@@ -2062,23 +2192,18 @@ longform_dir2_entry_check_data(
* just skip it. no need to process it and it's ..
* link is already accounted for.
*/
- if (INT_GET(dep->inumber, ARCH_CONVERT) == orphanage_ino &&
- strcmp(fname, ORPHANAGE) == 0)
+ if (inum == orphanage_ino && strcmp(fname, ORPHANAGE) == 0)
continue;
/*
* skip entries with bogus inumbers if we're in no modify mode
*/
- if (no_modify &&
- verify_inum(mp, INT_GET(dep->inumber, ARCH_CONVERT)))
+ if (no_modify && verify_inum(mp, inum))
continue;
/*
* ok, now handle the rest of the cases besides '.' and '..'
*/
- irec = find_inode_rec(
- XFS_INO_TO_AGNO(mp,
- INT_GET(dep->inumber, ARCH_CONVERT)),
- XFS_INO_TO_AGINO(mp,
- INT_GET(dep->inumber, ARCH_CONVERT)));
+ irec = find_inode_rec(XFS_INO_TO_AGNO(mp, inum),
+ XFS_INO_TO_AGINO(mp, inum));
if (irec == NULL) {
nbad++;
do_warn(_("entry \"%s\" in directory inode %llu points "
@@ -2093,9 +2218,7 @@ longform_dir2_entry_check_data(
}
continue;
}
- ino_offset = XFS_INO_TO_AGINO(mp,
- INT_GET(dep->inumber, ARCH_CONVERT)) -
- irec->ino_startnum;
+ ino_offset = XFS_INO_TO_AGINO(mp, inum) - irec->ino_startnum;
/*
* if it's a free inode, blow out the entry.
* by now, any inode that we think is free
@@ -2106,18 +2229,13 @@ longform_dir2_entry_check_data(
* don't complain if this entry points to the old
* and now-free lost+found inode
*/
- if (verbose || no_modify ||
- INT_GET(dep->inumber, ARCH_CONVERT) !=
- old_orphanage_ino)
+ if (verbose || no_modify || inum != old_orphanage_ino)
do_warn(
_("entry \"%s\" in directory inode %llu points to free inode %llu"),
- fname, ip->i_ino,
- INT_GET(dep->inumber, ARCH_CONVERT));
+ fname, ip->i_ino, inum);
nbad++;
if (!no_modify) {
- if (verbose ||
- INT_GET(dep->inumber, ARCH_CONVERT) !=
- old_orphanage_ino)
+ if (verbose || inum != old_orphanage_ino)
do_warn(
_(", marking entry to be junked\n"));
else
@@ -2130,28 +2248,6 @@ longform_dir2_entry_check_data(
continue;
}
/*
- * check for duplicate names in directory.
- */
- if (!name_hash_add(nametab, dep->name, dep->namelen)) {
- do_warn(
- _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
- fname, INT_GET(dep->inumber, ARCH_CONVERT),
- ip->i_ino);
- nbad++;
- if (!no_modify) {
- if (verbose)
- do_warn(
- _(", marking entry to be junked\n"));
- else
- do_warn("\n");
- dep->name[0] = '/';
- libxfs_dir2_data_log_entry(tp, bp, dep);
- } else {
- do_warn(_(", would junk entry\n"));
- }
- continue;
- }
- /*
* check easy case first, regular inode, just bump
* the link count and continue
*/
@@ -2172,22 +2268,17 @@ longform_dir2_entry_check_data(
junkit = 1;
do_warn(
_("entry \"%s\" in dir %llu points to an already connected directory inode
%llu,\n"),
- fname, ip->i_ino,
- INT_GET(dep->inumber, ARCH_CONVERT));
+ fname, ip->i_ino, inum);
} else if (parent == ip->i_ino) {
add_inode_reached(irec, ino_offset);
add_inode_ref(current_irec, current_ino_offset);
- if (!is_inode_refchecked(
- INT_GET(dep->inumber, ARCH_CONVERT), irec,
- ino_offset))
- push_dir(stack,
- INT_GET(dep->inumber, ARCH_CONVERT));
+ if (!is_inode_refchecked(inum, irec, ino_offset))
+ push_dir(stack, inum);
} else {
junkit = 1;
do_warn(
_("entry \"%s\" in dir inode %llu inconsistent with .. value (%llu) in ino
%llu,\n"),
- fname, ip->i_ino, parent,
- INT_GET(dep->inumber, ARCH_CONVERT));
+ fname, ip->i_ino, parent, inum);
}
if (junkit) {
junkit = 0;
@@ -2195,9 +2286,7 @@ _("entry \"%s\" in dir inode %llu incons
if (!no_modify) {
dep->name[0] = '/';
libxfs_dir2_data_log_entry(tp, bp, dep);
- if (verbose ||
- INT_GET(dep->inumber, ARCH_CONVERT) !=
- old_orphanage_ino)
+ if (verbose || inum != old_orphanage_ino)
do_warn(
_("\twill clear entry \"%s\"\n"),
fname);
@@ -2212,8 +2301,6 @@ _("entry \"%s\" in dir inode %llu incons
libxfs_dir2_data_freescan(mp, d, &needlog, NULL);
if (needlog)
libxfs_dir2_data_log_header(tp, bp);
- else if (!isblock && !nbad)
- libxfs_da_brelse(tp, bp);
libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
libxfs_trans_commit(tp, 0, 0);
freetab->ents[db].v = INT_GET(d->hdr.bestfree[0].length, ARCH_CONVERT);
@@ -2306,19 +2393,19 @@ longform_dir2_check_node(
xfs_fileoff_t next_da_bno;
int seeval = 0;
int used;
-
+
for (da_bno = mp->m_dirleafblk, next_da_bno = 0;
next_da_bno != NULLFILEOFF && da_bno < mp->m_dirfreeblk;
da_bno = (xfs_dablk_t)next_da_bno) {
next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
- if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
+ if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
break;
if (libxfs_da_read_bufr(NULL, ip, da_bno, -1, &bp,
XFS_DATA_FORK)) {
- do_error(
- _("can't read block %u for directory inode %llu\n"),
+ do_warn(
+ _("can't read leaf block %u for directory inode
%llu\n"),
da_bno, ip->i_ino);
- /* NOTREACHED */
+ return 1;
}
leaf = bp->data;
if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) !=
@@ -2348,23 +2435,24 @@ longform_dir2_check_node(
seeval = dir_hash_see_all(hashtab, leaf->ents,
INT_GET(leaf->hdr.count, ARCH_CONVERT),
INT_GET(leaf->hdr.stale, ARCH_CONVERT));
libxfs_da_brelse(NULL, bp);
- if (seeval != DIR_HASH_CK_OK)
+ if (seeval != DIR_HASH_CK_OK)
return 1;
}
- if (dir_hash_check(hashtab, ip, seeval))
+ if (dir_hash_check(hashtab, ip, seeval))
return 1;
+
for (da_bno = mp->m_dirfreeblk, next_da_bno = 0;
next_da_bno != NULLFILEOFF;
da_bno = (xfs_dablk_t)next_da_bno) {
next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
- if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
+ if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
break;
if (libxfs_da_read_bufr(NULL, ip, da_bno, -1, &bp,
XFS_DATA_FORK)) {
- do_error(_("can't read block %u for directory inode "
- "%llu\n"),
+ do_warn(
+ _("can't read freespace block %u for directory inode %llu\n"),
da_bno, ip->i_ino);
- /* NOTREACHED */
+ return 1;
}
free = bp->data;
fdb = XFS_DIR2_DA_TO_DB(mp, da_bno);
@@ -2418,388 +2506,9 @@ longform_dir2_check_node(
}
/*
- * Rebuild a directory: set up.
- * Turn it into a node-format directory with no contents in the
- * upper area. Also has correct freespace blocks.
- */
-void
-longform_dir2_rebuild_setup(
- xfs_mount_t *mp,
- xfs_ino_t ino,
- xfs_inode_t *ip,
- freetab_t *freetab)
-{
- xfs_da_args_t args;
- int committed;
- xfs_dir2_data_t *data = NULL;
- xfs_dabuf_t *dbp;
- int error;
- xfs_dir2_db_t fbno;
- xfs_dabuf_t *fbp;
- xfs_fsblock_t firstblock;
- xfs_bmap_free_t flist;
- xfs_dir2_free_t *free;
- int i;
- int j;
- xfs_dablk_t lblkno;
- xfs_dabuf_t *lbp;
- xfs_dir2_leaf_t *leaf;
- int nres;
- xfs_trans_t *tp;
-
- /* read first directory block */
- tp = libxfs_trans_alloc(mp, 0);
- nres = XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK);
- error = libxfs_trans_reserve(tp,
- nres, XFS_CREATE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES,
- XFS_CREATE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- XFS_BMAP_INIT(&flist, &firstblock);
- if (libxfs_da_read_buf(tp, ip, mp->m_dirdatablk, -2, &dbp,
- XFS_DATA_FORK)) {
- do_error(_("can't read block %u for directory inode %llu\n"),
- mp->m_dirdatablk, ino);
- /* NOTREACHED */
- }
-
- if (dbp)
- data = dbp->data;
-
- /* check for block format directory */
- if (data &&
- INT_GET((data)->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
- xfs_dir2_block_t *block;
- xfs_dir2_leaf_entry_t *blp;
- xfs_dir2_block_tail_t *btp;
- int needlog;
- int needscan;
-
- /* convert directory block from block format to data format */
- INT_SET(data->hdr.magic, ARCH_CONVERT, XFS_DIR2_DATA_MAGIC);
-
- /* construct freelist */
- block = (xfs_dir2_block_t *)data;
- btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
- blp = XFS_DIR2_BLOCK_LEAF_P(btp);
- needlog = needscan = 0;
- libxfs_dir2_data_make_free(tp, dbp, (char *)blp - (char *)block,
- (char *)block + mp->m_dirblksize - (char *)blp,
- &needlog, &needscan);
- if (needscan)
- libxfs_dir2_data_freescan(mp, data, &needlog, NULL);
- libxfs_da_log_buf(tp, dbp, 0, mp->m_dirblksize - 1);
- } else if (dbp) {
- libxfs_da_brelse(tp, dbp);
- }
-
- /* allocate blocks for btree */
- bzero(&args, sizeof(args));
- args.trans = tp;
- args.dp = ip;
- args.whichfork = XFS_DATA_FORK;
- args.firstblock = &firstblock;
- args.flist = &flist;
- args.total = nres;
- if ((error = libxfs_da_grow_inode(&args, &lblkno)) ||
- (error = libxfs_da_get_buf(tp, ip, lblkno, -1, &lbp,
XFS_DATA_FORK))) {
- do_error(_("can't add btree block to directory inode %llu\n"),
- ino);
- /* NOTREACHED */
- }
- leaf = lbp->data;
- bzero(leaf, mp->m_dirblksize);
- INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, XFS_DIR2_LEAFN_MAGIC);
- libxfs_da_log_buf(tp, lbp, 0, mp->m_dirblksize - 1);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
-
- for (i = 0; i < freetab->nents; i += XFS_DIR2_MAX_FREE_BESTS(mp)) {
- tp = libxfs_trans_alloc(mp, 0);
- nres = XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK);
- error = libxfs_trans_reserve(tp,
- nres, XFS_CREATE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES,
- XFS_CREATE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- XFS_BMAP_INIT(&flist, &firstblock);
- bzero(&args, sizeof(args));
- args.trans = tp;
- args.dp = ip;
- args.whichfork = XFS_DATA_FORK;
- args.firstblock = &firstblock;
- args.flist = &flist;
- args.total = nres;
- if ((error = libxfs_dir2_grow_inode(&args, XFS_DIR2_FREE_SPACE,
- &fbno)) ||
- (error = libxfs_da_get_buf(tp, ip, XFS_DIR2_DB_TO_DA(mp,
fbno),
- -1, &fbp, XFS_DATA_FORK))) {
- do_error(_("can't add free block to directory inode "
- "%llu\n"),
- ino);
- /* NOTREACHED */
- }
- free = fbp->data;
- bzero(free, mp->m_dirblksize);
- INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC);
- INT_SET(free->hdr.firstdb, ARCH_CONVERT, i);
- INT_SET(free->hdr.nvalid, ARCH_CONVERT,
XFS_DIR2_MAX_FREE_BESTS(mp));
- if (i + INT_GET(free->hdr.nvalid, ARCH_CONVERT) >
freetab->nents)
- INT_SET(free->hdr.nvalid, ARCH_CONVERT, freetab->nents
- i);
- for (j = 0; j < INT_GET(free->hdr.nvalid, ARCH_CONVERT); j++) {
- INT_SET(free->bests[j], ARCH_CONVERT, freetab->ents[i +
j].v);
- if (INT_GET(free->bests[j], ARCH_CONVERT) !=
NULLDATAOFF)
- INT_MOD(free->hdr.nused, ARCH_CONVERT, +1);
- }
- libxfs_da_log_buf(tp, fbp, 0, mp->m_dirblksize - 1);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
- }
-}
-
-/*
- * Rebuild the entries from a single data block.
- */
-void
-longform_dir2_rebuild_data(
- xfs_mount_t *mp,
- xfs_ino_t ino,
- xfs_inode_t *ip,
- xfs_dablk_t da_bno)
-{
- xfs_dabuf_t *bp;
- xfs_dir2_block_tail_t *btp;
- int committed;
- xfs_dir2_data_t *data;
- xfs_dir2_db_t dbno;
- xfs_dir2_data_entry_t *dep;
- xfs_dir2_data_unused_t *dup;
- char *endptr;
- int error;
- xfs_dir2_free_t *fblock;
- xfs_dabuf_t *fbp;
- xfs_dir2_db_t fdb;
- int fi;
- xfs_fsblock_t firstblock;
- xfs_bmap_free_t flist;
- int needlog;
- int needscan;
- int nres;
- char *ptr;
- xfs_trans_t *tp;
-
- if (libxfs_da_read_buf(NULL, ip, da_bno, da_bno == 0 ? -2 : -1, &bp,
- XFS_DATA_FORK)) {
- do_error(_("can't read block %u for directory inode %llu\n"),
- da_bno, ino);
- /* NOTREACHED */
- }
- if (da_bno == 0 && bp == NULL)
- /*
- * The block was punched out.
- */
- return;
- ASSERT(bp);
- dbno = XFS_DIR2_DA_TO_DB(mp, da_bno);
- fdb = XFS_DIR2_DB_TO_FDB(mp, dbno);
- if (libxfs_da_read_buf(NULL, ip, XFS_DIR2_DB_TO_DA(mp, fdb), -1, &fbp,
- XFS_DATA_FORK)) {
- do_error(_("can't read block %u for directory inode %llu\n"),
- XFS_DIR2_DB_TO_DA(mp, fdb), ino);
- /* NOTREACHED */
- }
- data = malloc(mp->m_dirblksize);
- if (!data) {
- do_error(
- _("malloc failed in longform_dir2_rebuild_data (%u bytes)\n"),
- mp->m_dirblksize);
- exit(1);
- }
- bcopy(bp->data, data, mp->m_dirblksize);
- ptr = (char *)data->u;
- if (INT_GET(data->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
- btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)data);
- endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp);
- } else
- endptr = (char *)data + mp->m_dirblksize;
- fblock = fbp->data;
- fi = XFS_DIR2_DB_TO_FDINDEX(mp, dbno);
- tp = libxfs_trans_alloc(mp, 0);
- error = libxfs_trans_reserve(tp, 0, XFS_CREATE_LOG_RES(mp), 0,
- XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- libxfs_da_bjoin(tp, bp);
- libxfs_da_bhold(tp, bp);
- libxfs_da_bjoin(tp, fbp);
- libxfs_da_bhold(tp, fbp);
- XFS_BMAP_INIT(&flist, &firstblock);
- needlog = needscan = 0;
- bzero(((xfs_dir2_data_t *)(bp->data))->hdr.bestfree,
- sizeof(data->hdr.bestfree));
- libxfs_dir2_data_make_free(tp, bp,
(xfs_dir2_data_aoff_t)sizeof(data->hdr),
- mp->m_dirblksize - sizeof(data->hdr), &needlog, &needscan);
- ASSERT(needscan == 0);
- libxfs_dir2_data_log_header(tp, bp);
- INT_SET(fblock->bests[fi], ARCH_CONVERT,
- INT_GET(((xfs_dir2_data_t
*)(bp->data))->hdr.bestfree[0].length, ARCH_CONVERT));
- libxfs_dir2_free_log_bests(tp, fbp, fi, fi);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
-
- while (ptr < endptr) {
- dup = (xfs_dir2_data_unused_t *)ptr;
- if (INT_GET(dup->freetag, ARCH_CONVERT) ==
XFS_DIR2_DATA_FREE_TAG) {
- ptr += INT_GET(dup->length, ARCH_CONVERT);
- continue;
- }
- dep = (xfs_dir2_data_entry_t *)ptr;
- ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
- if (dep->name[0] == '/')
- continue;
- tp = libxfs_trans_alloc(mp, 0);
- nres = XFS_CREATE_SPACE_RES(mp, dep->namelen);
- error = libxfs_trans_reserve(tp, nres, XFS_CREATE_LOG_RES(mp),
0,
- XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- libxfs_da_bjoin(tp, bp);
- libxfs_da_bhold(tp, bp);
- libxfs_da_bjoin(tp, fbp);
- libxfs_da_bhold(tp, fbp);
- XFS_BMAP_INIT(&flist, &firstblock);
- error = dir_createname(mp, tp, ip, (char *)dep->name,
- dep->namelen, INT_GET(dep->inumber, ARCH_CONVERT),
- &firstblock, &flist, nres);
- ASSERT(error == 0);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
- }
- libxfs_da_brelse(NULL, bp);
- libxfs_da_brelse(NULL, fbp);
- free(data);
-}
-
-/*
- * Finish the rebuild of a directory.
- * Stuff / in and then remove it, this forces the directory to end
- * up in the right format.
- */
-void
-longform_dir2_rebuild_finish(
- xfs_mount_t *mp,
- xfs_ino_t ino,
- xfs_inode_t *ip)
-{
- int committed;
- int error;
- xfs_fsblock_t firstblock;
- xfs_bmap_free_t flist;
- int nres;
- xfs_trans_t *tp;
-
- tp = libxfs_trans_alloc(mp, 0);
- nres = XFS_CREATE_SPACE_RES(mp, 1);
- error = libxfs_trans_reserve(tp, nres, XFS_CREATE_LOG_RES(mp), 0,
- XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- XFS_BMAP_INIT(&flist, &firstblock);
- error = dir_createname(mp, tp, ip, "/", 1, ino,
- &firstblock, &flist, nres);
- ASSERT(error == 0);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
-
- /* could kill trailing empty data blocks here */
-
- tp = libxfs_trans_alloc(mp, 0);
- nres = XFS_REMOVE_SPACE_RES(mp);
- error = libxfs_trans_reserve(tp, nres, XFS_REMOVE_LOG_RES(mp), 0,
- XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
- if (error)
- res_failed(error);
- libxfs_trans_ijoin(tp, ip, 0);
- libxfs_trans_ihold(tp, ip);
- XFS_BMAP_INIT(&flist, &firstblock);
- error = dir_removename(mp, tp, ip, "/", 1, ino,
- &firstblock, &flist, nres);
- ASSERT(error == 0);
- libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
- libxfs_trans_commit(tp, 0, 0);
-}
-
-/*
- * Rebuild a directory.
- * Remove all the non-data blocks.
- * Re-initialize to (empty) node form.
- * Loop over the data blocks reinserting each entry.
- * Force the directory into the right format.
- */
-void
-longform_dir2_rebuild(
- xfs_mount_t *mp,
- xfs_ino_t ino,
- xfs_inode_t *ip,
- int *num_illegal,
- freetab_t *freetab,
- int isblock)
-{
- xfs_dabuf_t *bp;
- xfs_dablk_t da_bno;
- xfs_fileoff_t next_da_bno;
-
- do_warn(_("rebuilding directory inode %llu\n"), ino);
-
- /* kill leaf blocks */
- for (da_bno = mp->m_dirleafblk, next_da_bno = isblock ? NULLFILEOFF : 0;
- next_da_bno != NULLFILEOFF;
- da_bno = (xfs_dablk_t)next_da_bno) {
- next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
- if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
- break;
- if (libxfs_da_get_buf(NULL, ip, da_bno, -1, &bp,
XFS_DATA_FORK)) {
- do_error(_("can't get block %u for directory inode "
- "%llu\n"),
- da_bno, ino);
- /* NOTREACHED */
- }
- dir2_kill_block(mp, ip, da_bno, bp);
- }
-
- /* rebuild empty btree and freelist */
- longform_dir2_rebuild_setup(mp, ino, ip, freetab);
-
- /* rebuild directory */
- for (da_bno = mp->m_dirdatablk, next_da_bno = 0;
- da_bno < mp->m_dirleafblk && next_da_bno != NULLFILEOFF;
- da_bno = (xfs_dablk_t)next_da_bno) {
- next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
- if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
- break;
- longform_dir2_rebuild_data(mp, ino, ip, da_bno);
- }
-
- /* put the directory in the appropriate on-disk format */
- longform_dir2_rebuild_finish(mp, ino, ip);
- *num_illegal = 0;
-}
-
-/*
- * succeeds or dies, inode never gets dirtied since all changes
- * happen in file blocks. the inode size and other core info
- * is already correct, it's just the leaf entries that get altered.
- * XXX above comment is wrong for v2 - need to see why it matters
+ * If a directory is corrupt, we need to read in as many entries as possible,
+ * destroy the entry and create a new one with recovered name/inode pairs.
+ * (ie. get libxfs to do all the grunt work)
*/
void
longform_dir2_entry_check(xfs_mount_t *mp,
@@ -2810,15 +2519,14 @@ longform_dir2_entry_check(xfs_mount_t *m
dir_stack_t *stack,
ino_tree_node_t *irec,
int ino_offset,
- name_hash_tab_t *nametab)
+ dir_hash_tab_t *hashtab)
{
xfs_dir2_block_t *block;
xfs_dir2_leaf_entry_t *blp;
- xfs_dabuf_t *bp;
+ xfs_dabuf_t **bplist;
xfs_dir2_block_tail_t *btp;
xfs_dablk_t da_bno;
freetab_t *freetab;
- dir_hash_tab_t *hashtab;
int i;
int isblock;
int isleaf;
@@ -2840,6 +2548,7 @@ longform_dir2_entry_check(xfs_mount_t *m
freetab->ents[i].v = NULLDATAOFF;
freetab->ents[i].s = 0;
}
+ bplist = calloc(freetab->naents, sizeof(xfs_dabuf_t*));
/* is this a block, leaf, or node directory? */
libxfs_dir2_isblock(NULL, ip, &isblock);
libxfs_dir2_isleaf(NULL, ip, &isleaf);
@@ -2847,50 +2556,58 @@ longform_dir2_entry_check(xfs_mount_t *m
if (do_prefetch && !isblock)
prefetch_p6_dir2(mp, ip);
- /* check directory data */
- hashtab = dir_hash_init(ip->i_d.di_size);
+ /* check directory "data" blocks (ie. name/inode pairs) */
for (da_bno = 0, next_da_bno = 0;
next_da_bno != NULLFILEOFF && da_bno < mp->m_dirleafblk;
da_bno = (xfs_dablk_t)next_da_bno) {
next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
+ ASSERT(XFS_DIR2_DA_TO_DB(mp, da_bno) < freetab->naents);
if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno,
XFS_DATA_FORK))
break;
- if (libxfs_da_read_bufr(NULL, ip, da_bno,
- da_bno == 0 ? -2 : -1, &bp, XFS_DATA_FORK)) {
- do_error(_("can't read block %u for directory inode "
- "%llu\n"),
+ if (libxfs_da_read_bufr(NULL, ip, da_bno, -1,
+ &bplist[XFS_DIR2_DA_TO_DB(mp, da_bno)],
+ XFS_DATA_FORK)) {
+ do_warn(_(
+ "can't read data block %u for directory inode %llu\n"),
da_bno, ino);
- /* NOTREACHED */
+ *num_illegal++;
+ continue; /* try and read all "data" blocks */
}
- /* is there a hole at the start? */
- if (da_bno == 0 && bp == NULL)
- continue;
longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
- stack, irec, ino_offset, &bp, hashtab, &freetab,
- nametab, da_bno, isblock);
- /* it releases the buffer unless isblock is set */
+ stack, irec, ino_offset,
+ &bplist[XFS_DIR2_DA_TO_DB(mp, da_bno)],
hashtab,
+ &freetab, da_bno, isblock);
}
fixit = (*num_illegal != 0) || dir2_is_badino(ino);
/* check btree and freespace */
if (isblock) {
- ASSERT(bp);
- block = bp->data;
+ block = bplist[0]->data;
btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
blp = XFS_DIR2_BLOCK_LEAF_P(btp);
- seeval = dir_hash_see_all(hashtab, blp, INT_GET(btp->count,
ARCH_CONVERT), INT_GET(btp->stale,
ARCH_CONVERT));
+ seeval = dir_hash_see_all(hashtab, blp,
+ INT_GET(btp->count, ARCH_CONVERT),
+ INT_GET(btp->stale, ARCH_CONVERT));
if (dir_hash_check(hashtab, ip, seeval))
fixit |= 1;
- libxfs_da_brelse(NULL, bp);
} else if (isleaf) {
fixit |= longform_dir2_check_leaf(mp, ip, hashtab, freetab);
} else {
fixit |= longform_dir2_check_node(mp, ip, hashtab, freetab);
}
- dir_hash_done(hashtab);
- if (!no_modify && fixit)
- longform_dir2_rebuild(mp, ino, ip, num_illegal, freetab,
- isblock);
+ if (!no_modify && fixit) {
+ dir_hash_dup_names(hashtab);
+ for (i = 0; i < freetab->naents; i++)
+ if (bplist[i])
+ libxfs_da_brelse(NULL, bplist[i]);
+ longform_dir2_rebuild(mp, ino, ip, hashtab);
+ *num_illegal = 0;
+ } else {
+ for (i = 0; i < freetab->naents; i++)
+ if (bplist[i])
+ libxfs_da_brelse(NULL, bplist[i]);
+ }
+
free(freetab);
}
@@ -2906,7 +2623,7 @@ shortform_dir_entry_check(xfs_mount_t *m
dir_stack_t *stack,
ino_tree_node_t *current_irec,
int current_ino_offset,
- name_hash_tab_t *nametab)
+ dir_hash_tab_t *hashtab)
{
xfs_ino_t lino;
xfs_ino_t parent;
@@ -3044,7 +2761,7 @@ _("entry \"%s\" in shortform dir %llu re
ASSERT(irec != NULL);
ino_offset = XFS_INO_TO_AGINO(mp, lino) - irec->ino_startnum;
-
+
/*
* if it's a free inode, blow out the entry.
* by now, any inode that we think is free
@@ -3066,8 +2783,9 @@ _("entry \"%s\" in shortform dir inode %
do_warn(_("would junk entry \"%s\"\n"),
fname);
}
- } else if (!name_hash_add(nametab, sf_entry->name,
- sf_entry->namelen)) {
+ } else if (!dir_hash_add(hashtab,
+ (xfs_dir2_dataptr_t)(sf_entry - &sf->list[0]),
+ lino, sf_entry->namelen, sf_entry->name)) {
/*
* check for duplicate names in directory.
*/
@@ -3311,7 +3029,7 @@ shortform_dir2_entry_check(xfs_mount_t *
dir_stack_t *stack,
ino_tree_node_t *current_irec,
int current_ino_offset,
- name_hash_tab_t *nametab)
+ dir_hash_tab_t *hashtab)
{
xfs_ino_t lino;
xfs_ino_t parent;
@@ -3484,7 +3202,9 @@ shortform_dir2_entry_check(xfs_mount_t *
do_warn(_("would junk entry \"%s\"\n"),
fname);
}
- } else if (!name_hash_add(nametab, sfep->name, sfep->namelen)) {
+ } else if (!dir_hash_add(hashtab, (xfs_dir2_dataptr_t)
+ (sfep - XFS_DIR2_SF_FIRSTENTRY(sfp)),
+ lino, sfep->namelen, sfep->name)) {
/*
* check for duplicate names in directory.
*/
@@ -3650,7 +3370,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
xfs_trans_t *tp;
xfs_dahash_t hashval;
ino_tree_node_t *irec;
- name_hash_tab_t *nametab;
+ dir_hash_tab_t *hashtab;
int ino_offset, need_dot, committed;
int dirty, num_illegal, error, nres;
@@ -3731,7 +3451,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
add_inode_refchecked(ino, irec, ino_offset);
- nametab = name_hash_init(ip->i_d.di_size);
+ hashtab = dir_hash_init(ip->i_d.di_size);
/*
* look for bogus entries
@@ -3750,13 +3470,13 @@ process_dirstack(xfs_mount_t *mp, dir_st
&num_illegal, &need_dot,
stack, irec,
ino_offset,
- nametab);
+ hashtab);
else
longform_dir_entry_check(mp, ino, ip,
&num_illegal, &need_dot,
stack, irec,
ino_offset,
- nametab);
+ hashtab);
break;
case XFS_DINODE_FMT_LOCAL:
tp = libxfs_trans_alloc(mp, 0);
@@ -3781,12 +3501,12 @@ process_dirstack(xfs_mount_t *mp, dir_st
shortform_dir2_entry_check(mp, ino, ip, &dirty,
stack, irec,
ino_offset,
- nametab);
+ hashtab);
else
shortform_dir_entry_check(mp, ino, ip, &dirty,
stack, irec,
ino_offset,
- nametab);
+ hashtab);
ASSERT(dirty == 0 || (dirty && !no_modify));
if (dirty) {
@@ -3801,7 +3521,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
default:
break;
}
- name_hash_done(nametab);
+ dir_hash_done(hashtab);
hashval = 0;
|