static int ext2_htree_find_leaf(struct inode *ip, const char *name, int namelen, uint32_t *hash, uint8_t *hash_ver, struct ext2fs_htree_lookup_info *info) { struct vnode *vp; struct ext2fs *fs; struct m_ext2fs *m_fs; struct buf *bp = NULL; struct ext2fs_htree_root *rootp; struct ext2fs_htree_entry *entp, *start, *end, *middle, *found; struct ext2fs_htree_lookup_level *level_info; uint32_t hash_major = 0, hash_minor = 0; uint32_t levels, cnt; uint8_t hash_version; if (name == NULL || info == NULL) return (-1); vp = ITOV(ip); fs = ip->i_e2fs->e2fs; m_fs = ip->i_e2fs; if (ext2_blkatoff(vp, 0, NULL, &bp) != 0) return (-1); info->h_levels_num = 1; info->h_levels[0].h_bp = bp; rootp = (struct ext2fs_htree_root *)bp->b_data; if (rootp->h_info.h_hash_version != EXT2_HTREE_LEGACY && rootp->h_info.h_hash_version != EXT2_HTREE_HALF_MD4 && rootp->h_info.h_hash_version != EXT2_HTREE_TEA) goto error; hash_version = rootp->h_info.h_hash_version; if (hash_version <= EXT2_HTREE_TEA) hash_version += m_fs->e2fs_uhash; *hash_ver = hash_version; ext2_htree_hash(name, namelen, fs->e3fs_hash_seed, hash_version, &hash_major, &hash_minor); *hash = hash_major; if ((levels = rootp->h_info.h_ind_levels) > 1) goto error; entp = (struct ext2fs_htree_entry *)(((char *)&rootp->h_info) + rootp->h_info.h_info_len); if (ext2_htree_get_limit(entp) != ext2_htree_root_limit(ip, rootp->h_info.h_info_len)) goto error; while (1) { cnt = ext2_htree_get_count(entp); if (cnt == 0 || cnt > ext2_htree_get_limit(entp)) goto error; start = entp + 1; end = entp + cnt - 1; while (start <= end) { middle = start + (end - start) / 2; if (ext2_htree_get_hash(middle) > hash_major) end = middle - 1; else start = middle + 1; } found = start - 1; level_info = &(info->h_levels[info->h_levels_num - 1]); level_info->h_bp = bp; level_info->h_entries = entp; level_info->h_entry = found; if (levels == 0) return (0); levels--; if (ext2_blkatoff(vp, ext2_htree_get_block(found) * m_fs->e2fs_bsize, NULL, &bp) != 0) goto error; entp = ((struct ext2fs_htree_node *)bp->b_data)->h_entries; info->h_levels_num++; info->h_levels[info->h_levels_num - 1].h_bp = bp; } error: ext2_htree_release(info); return (-1); }
/* * Move half of entries from the old directory block to the new one. */ static int ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize, uint32_t *hash_seed, uint8_t hash_version, uint32_t *split_hash, struct ext2fs_direct_2 *entry) { int entry_cnt = 0; int size = 0; int i, k; uint32_t offset; uint16_t entry_len = 0; uint32_t entry_hash; struct ext2fs_direct_2 *ep, *last; char *dest; struct ext2fs_htree_sort_entry *sort_info; ep = (struct ext2fs_direct_2 *)block1; dest = block2; sort_info = (struct ext2fs_htree_sort_entry *) ((char *)block2 + blksize); /* * Calculate name hash value for the entry which is to be added. */ ext2_htree_hash(entry->e2d_name, entry->e2d_namlen, hash_seed, hash_version, &entry_hash, NULL); /* * Fill in directory entry sort descriptors. */ while ((char *)ep < block1 + blksize) { if (ep->e2d_ino && ep->e2d_namlen) { entry_cnt++; sort_info--; sort_info->h_size = ep->e2d_reclen; sort_info->h_offset = (char *)ep - block1; ext2_htree_hash(ep->e2d_name, ep->e2d_namlen, hash_seed, hash_version, &sort_info->h_hash, NULL); } ep = (struct ext2fs_direct_2 *) ((char *)ep + ep->e2d_reclen); } /* * Sort directory entry descriptors by name hash value. */ qsort(sort_info, entry_cnt, sizeof(struct ext2fs_htree_sort_entry), ext2_htree_cmp_sort_entry); /* * Count the number of entries to move to directory block 2. */ for (i = entry_cnt - 1; i >= 0; i--) { if (sort_info[i].h_size + size > blksize / 2) break; size += sort_info[i].h_size; } *split_hash = sort_info[i + 1].h_hash; /* * Set collision bit. */ if (*split_hash == sort_info[i].h_hash) *split_hash += 1; /* * Move half of directory entries from block 1 to block 2. */ for (k = i + 1; k < entry_cnt; k++) { ep = (struct ext2fs_direct_2 *)((char *)block1 + sort_info[k].h_offset); entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen); memcpy(dest, ep, entry_len); ((struct ext2fs_direct_2 *)dest)->e2d_reclen = entry_len; /* Mark directory entry as unused. */ ep->e2d_ino = 0; dest += entry_len; } dest -= entry_len; /* Shrink directory entries in block 1. */ last = (struct ext2fs_direct_2 *)block1; entry_len = EXT2_DIR_REC_LEN(last->e2d_namlen); for (offset = last->e2d_reclen; offset < blksize; ) { ep = (struct ext2fs_direct_2 *)(block1 + offset); offset += ep->e2d_reclen; if (last->e2d_ino) { /* Trim the existing slot */ last->e2d_reclen = entry_len; last = (struct ext2fs_direct_2 *) ((char *)last + entry_len); } entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen); memcpy((void *)last, (void *)ep, entry_len); } if (entry_hash >= *split_hash) { /* Add entry to block 2. */ ext2_append_entry(block2, blksize, (struct ext2fs_direct_2 *)dest, entry); /* Adjust length field of last entry of block 1. */ last->e2d_reclen = block1 + blksize - (char *)last; } else { /* Add entry to block 1. */ ext2_append_entry(block1, blksize, last, entry); /* Adjust length field of last entry of block 2. */ ((struct ext2fs_direct_2 *)dest)->e2d_reclen = block2 + blksize - dest; } return (0); }
static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len, const char *name) { return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version, &hinfo->hash, &hinfo->minor_hash); }