(offsetof(dir_hash_tab_t, tab) + (sizeof(dir_hash_ent_t *) * (n)))
#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.
*/
return j == stale ? DIR_HASH_CK_OK : DIR_HASH_CK_BADSTALE;
}
+/*
+ * Returns 0 if the name already exists (ie. a duplicate)
+ */
+static int
+name_hash_add(
+ name_hash_tab_t *nametab,
+ uchar_t *name,
+ int namelen)
+{
+ 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));
+
+ p->next = nametab->tab[i];
+ p->hashval = hash;
+ p->name = name;
+ p->namelen = namelen;
+ nametab->tab[i] = p;
+
+ 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);
+ }
+ }
+ free(nametab);
+}
+
/*
* Version 1 or 2 directory routine wrappers
int *need_dot,
dir_stack_t *stack,
ino_tree_node_t *current_irec,
- int current_ino_offset)
+ int current_ino_offset,
+ name_hash_tab_t *nametab)
{
xfs_dir_leaf_entry_t *entry;
ino_tree_node_t *irec;
continue;
}
+ /*
+ * check for duplicate names in directory.
+ */
+ if (!name_hash_add(nametab, namest->name, entry->namelen)) {
+ do_warn(
+ _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
+ fname, lino, ino);
+ nbad++;
+ if (!no_modify) {
+ if (verbose)
+ do_warn(
+ _(", marking entry to be junked\n"));
+ else
+ do_warn("\n");
+ namest->name[0] = '/';
+ *dirty = 1;
+ } else {
+ do_warn(_(", would junk entry\n"));
+ }
+ continue;
+ }
/*
* check easy case first, regular inode, just bump
* the link count and continue
int *need_dot,
dir_stack_t *stack,
ino_tree_node_t *irec,
- int ino_offset)
+ int ino_offset,
+ name_hash_tab_t *nametab)
{
xfs_dir_leafblock_t *leaf;
xfs_buf_t *bp;
if (!skipit)
lf_block_dir_entry_check(mp, ino, leaf, &dirty,
num_illegal, need_dot, stack,
- irec, ino_offset);
+ irec, ino_offset, nametab);
ASSERT(dirty == 0 || (dirty && !no_modify));
xfs_dabuf_t **bpp,
dir_hash_tab_t *hashtab,
freetab_t **freetabp,
+ name_hash_tab_t *nametab,
xfs_dablk_t da_bno,
int isblock)
{
}
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
int *need_dot,
dir_stack_t *stack,
ino_tree_node_t *irec,
- int ino_offset)
+ int ino_offset,
+ name_hash_tab_t *nametab)
{
xfs_dir2_block_t *block;
xfs_dir2_leaf_entry_t *blp;
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, da_bno,
- isblock);
+ stack, irec, ino_offset, &bp, hashtab, &freetab,
+ nametab, da_bno, isblock);
/* it releases the buffer unless isblock is set */
}
fixit = (*num_illegal != 0) || dir2_is_badino(ino);
int *ino_dirty,
dir_stack_t *stack,
ino_tree_node_t *current_irec,
- int current_ino_offset)
+ int current_ino_offset,
+ name_hash_tab_t *nametab)
{
xfs_ino_t lino;
xfs_ino_t parent;
do_warn(_("would junk entry \"%s\"\n"),
fname);
}
+ } else if (!name_hash_add(nametab, sf_entry->name,
+ sf_entry->namelen)) {
+ /*
+ * check for duplicate names in directory.
+ */
+ do_warn(
+ _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
+ fname, lino, ino);
+ if (!no_modify) {
+ junkit = 1;
+ if (verbose)
+ do_warn(
+ _(", marking entry to be junked\n"));
+ else
+ do_warn("\n");
+ } else {
+ do_warn(_(", would junk entry\n"));
+ }
} else if (!inode_isadir(irec, ino_offset)) {
/*
* check easy case first, regular inode, just bump
int *ino_dirty,
dir_stack_t *stack,
ino_tree_node_t *current_irec,
- int current_ino_offset)
+ int current_ino_offset,
+ name_hash_tab_t *nametab)
{
xfs_ino_t lino;
xfs_ino_t parent;
do_warn(_("would junk entry \"%s\"\n"),
fname);
}
+ } else if (!name_hash_add(nametab, sfep->name, sfep->namelen)) {
+ /*
+ * check for duplicate names in directory.
+ */
+ do_warn(
+ _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
+ fname, lino, ino);
+ if (!no_modify) {
+ junkit = 1;
+ if (verbose)
+ do_warn(
+ _(", marking entry to be junked\n"));
+ else
+ do_warn("\n");
+ } else {
+ do_warn(_(", would junk entry\n"));
+ }
} else if (!inode_isadir(irec, ino_offset)) {
/*
* check easy case first, regular inode, just bump
xfs_trans_t *tp;
xfs_dahash_t hashval;
ino_tree_node_t *irec;
+ name_hash_tab_t *nametab;
int ino_offset, need_dot, committed;
int dirty, num_illegal, error, nres;
add_inode_refchecked(ino, irec, ino_offset);
+ nametab = name_hash_init(ip->i_d.di_size);
+
/*
* look for bogus entries
*/
longform_dir2_entry_check(mp, ino, ip,
&num_illegal, &need_dot,
stack, irec,
- ino_offset);
+ ino_offset,
+ nametab);
else
longform_dir_entry_check(mp, ino, ip,
&num_illegal, &need_dot,
stack, irec,
- ino_offset);
+ ino_offset,
+ nametab);
break;
case XFS_DINODE_FMT_LOCAL:
tp = libxfs_trans_alloc(mp, 0);
if (XFS_SB_VERSION_HASDIRV2(&mp->m_sb))
shortform_dir2_entry_check(mp, ino, ip, &dirty,
stack, irec,
- ino_offset);
+ ino_offset,
+ nametab);
else
shortform_dir_entry_check(mp, ino, ip, &dirty,
stack, irec,
- ino_offset);
+ ino_offset,
+ nametab);
ASSERT(dirty == 0 || (dirty && !no_modify));
if (dirty) {
default:
break;
}
+ name_hash_done(nametab);
hashval = 0;