Example #1
0
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);
}
Example #2
0
/*
 * 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);
}
Example #3
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);
}