Home | History | Annotate | Line # | Download | only in ext2fs
ext2fs_htree.c revision 1.9.14.2
      1  1.9.14.2  jdolecek /*	$NetBSD: ext2fs_htree.c,v 1.9.14.2 2017/12/03 11:39:21 jdolecek Exp $	*/
      2  1.9.14.2  jdolecek 
      3  1.9.14.2  jdolecek /*-
      4  1.9.14.2  jdolecek  * Copyright (c) 2010, 2012 Zheng Liu <lz (at) freebsd.org>
      5  1.9.14.2  jdolecek  * Copyright (c) 2012, Vyacheslav Matyushin
      6  1.9.14.2  jdolecek  * All rights reserved.
      7  1.9.14.2  jdolecek  *
      8  1.9.14.2  jdolecek  * Redistribution and use in source and binary forms, with or without
      9  1.9.14.2  jdolecek  * modification, are permitted provided that the following conditions
     10  1.9.14.2  jdolecek  * are met:
     11  1.9.14.2  jdolecek  * 1. Redistributions of source code must retain the above copyright
     12  1.9.14.2  jdolecek  *    notice, this list of conditions and the following disclaimer.
     13  1.9.14.2  jdolecek  * 2. Redistributions in binary form must reproduce the above copyright
     14  1.9.14.2  jdolecek  *    notice, this list of conditions and the following disclaimer in the
     15  1.9.14.2  jdolecek  *    documentation and/or other materials provided with the distribution.
     16  1.9.14.2  jdolecek  *
     17  1.9.14.2  jdolecek  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     18  1.9.14.2  jdolecek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  1.9.14.2  jdolecek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  1.9.14.2  jdolecek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     21  1.9.14.2  jdolecek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  1.9.14.2  jdolecek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  1.9.14.2  jdolecek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  1.9.14.2  jdolecek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  1.9.14.2  jdolecek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  1.9.14.2  jdolecek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  1.9.14.2  jdolecek  * SUCH DAMAGE.
     28  1.9.14.2  jdolecek  *
     29  1.9.14.2  jdolecek  * $FreeBSD: head/sys/fs/ext2fs/ext2fs_htree.c 294653 2016-01-24 02:41:49Z pfg $
     30  1.9.14.2  jdolecek  */
     31  1.9.14.2  jdolecek #include <sys/cdefs.h>
     32  1.9.14.2  jdolecek __KERNEL_RCSID(0, "$NetBSD: ext2fs_htree.c,v 1.9.14.2 2017/12/03 11:39:21 jdolecek Exp $");
     33  1.9.14.2  jdolecek 
     34  1.9.14.2  jdolecek #include <sys/param.h>
     35  1.9.14.2  jdolecek #include <sys/systm.h>
     36  1.9.14.2  jdolecek #include <sys/resourcevar.h>
     37  1.9.14.2  jdolecek #include <sys/kernel.h>
     38  1.9.14.2  jdolecek #include <sys/file.h>
     39  1.9.14.2  jdolecek #include <sys/stat.h>
     40  1.9.14.2  jdolecek #include <sys/buf.h>
     41  1.9.14.2  jdolecek #include <sys/proc.h>
     42  1.9.14.2  jdolecek #include <sys/mount.h>
     43  1.9.14.2  jdolecek #include <sys/namei.h>
     44  1.9.14.2  jdolecek #include <sys/vnode.h>
     45  1.9.14.2  jdolecek #include <sys/lockf.h>
     46  1.9.14.2  jdolecek #include <sys/pool.h>
     47  1.9.14.2  jdolecek #include <sys/signalvar.h>
     48  1.9.14.2  jdolecek #include <sys/kauth.h>
     49  1.9.14.2  jdolecek #include <sys/malloc.h>
     50  1.9.14.2  jdolecek #include <ufs/ufs/dir.h>
     51  1.9.14.2  jdolecek 
     52  1.9.14.2  jdolecek #include <ufs/ufs/inode.h>
     53  1.9.14.2  jdolecek #include <ufs/ufs/ufsmount.h>
     54  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs.h>
     55  1.9.14.2  jdolecek 
     56  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs_extern.h>
     57  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs_dinode.h>
     58  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs_dir.h>
     59  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs_htree.h>
     60  1.9.14.2  jdolecek #include <ufs/ext2fs/ext2fs_hash.h>
     61  1.9.14.2  jdolecek 
     62  1.9.14.2  jdolecek static int ext2fs_htree_find_leaf(struct inode *, const char *, int ,
     63  1.9.14.2  jdolecek     uint32_t *, uint8_t *, struct ext2fs_htree_lookup_info *);
     64  1.9.14.2  jdolecek 
     65  1.9.14.2  jdolecek int
     66  1.9.14.2  jdolecek ext2fs_htree_has_idx(struct inode *ip)
     67  1.9.14.2  jdolecek {
     68  1.9.14.2  jdolecek 	return EXT2F_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX)
     69  1.9.14.2  jdolecek 	    && (ip->i_din.e2fs_din->e2di_flags & EXT2_INDEX);
     70  1.9.14.2  jdolecek }
     71  1.9.14.2  jdolecek 
     72  1.9.14.2  jdolecek static off_t
     73  1.9.14.2  jdolecek ext2fs_htree_get_block(struct ext2fs_htree_entry *ep)
     74  1.9.14.2  jdolecek {
     75  1.9.14.2  jdolecek 	return ep->h_blk & 0x00FFFFFF;
     76  1.9.14.2  jdolecek }
     77  1.9.14.2  jdolecek 
     78  1.9.14.2  jdolecek static void
     79  1.9.14.2  jdolecek ext2fs_htree_release(struct ext2fs_htree_lookup_info *info)
     80  1.9.14.2  jdolecek {
     81  1.9.14.2  jdolecek 	for (u_int i = 0; i < info->h_levels_num; i++) {
     82  1.9.14.2  jdolecek 		struct buf *bp = info->h_levels[i].h_bp;
     83  1.9.14.2  jdolecek 		if (bp != NULL)
     84  1.9.14.2  jdolecek 			brelse(bp, 0);
     85  1.9.14.2  jdolecek 	}
     86  1.9.14.2  jdolecek }
     87  1.9.14.2  jdolecek 
     88  1.9.14.2  jdolecek static uint16_t
     89  1.9.14.2  jdolecek ext2fs_htree_get_limit(struct ext2fs_htree_entry *ep)
     90  1.9.14.2  jdolecek {
     91  1.9.14.2  jdolecek 	return ((struct ext2fs_htree_count *)(ep))->h_entries_max;
     92  1.9.14.2  jdolecek }
     93  1.9.14.2  jdolecek 
     94  1.9.14.2  jdolecek static uint32_t
     95  1.9.14.2  jdolecek ext2fs_htree_root_limit(struct inode *ip, int len)
     96  1.9.14.2  jdolecek {
     97  1.9.14.2  jdolecek 	uint32_t space = ip->i_e2fs->e2fs_bsize - EXT2_DIR_REC_LEN(1) -
     98  1.9.14.2  jdolecek 	    EXT2_DIR_REC_LEN(2) - len;
     99  1.9.14.2  jdolecek 	return space / sizeof(struct ext2fs_htree_entry);
    100  1.9.14.2  jdolecek }
    101  1.9.14.2  jdolecek 
    102  1.9.14.2  jdolecek static uint16_t
    103  1.9.14.2  jdolecek ext2fs_htree_get_count(struct ext2fs_htree_entry *ep)
    104  1.9.14.2  jdolecek {
    105  1.9.14.2  jdolecek 	return ((struct ext2fs_htree_count *)(ep))->h_entries_num;
    106  1.9.14.2  jdolecek }
    107  1.9.14.2  jdolecek 
    108  1.9.14.2  jdolecek static uint32_t
    109  1.9.14.2  jdolecek ext2fs_htree_get_hash(struct ext2fs_htree_entry *ep)
    110  1.9.14.2  jdolecek {
    111  1.9.14.2  jdolecek 	return ep->h_hash;
    112  1.9.14.2  jdolecek }
    113  1.9.14.2  jdolecek 
    114  1.9.14.2  jdolecek 
    115  1.9.14.2  jdolecek static void
    116  1.9.14.2  jdolecek ext2fs_htree_set_block(struct ext2fs_htree_entry *ep, uint32_t blk)
    117  1.9.14.2  jdolecek {
    118  1.9.14.2  jdolecek 	ep->h_blk = blk;
    119  1.9.14.2  jdolecek }
    120  1.9.14.2  jdolecek 
    121  1.9.14.2  jdolecek static void
    122  1.9.14.2  jdolecek ext2fs_htree_set_count(struct ext2fs_htree_entry *ep, uint16_t cnt)
    123  1.9.14.2  jdolecek {
    124  1.9.14.2  jdolecek 	((struct ext2fs_htree_count *)(ep))->h_entries_num = cnt;
    125  1.9.14.2  jdolecek }
    126  1.9.14.2  jdolecek 
    127  1.9.14.2  jdolecek static void
    128  1.9.14.2  jdolecek ext2fs_htree_set_hash(struct ext2fs_htree_entry *ep, uint32_t hash)
    129  1.9.14.2  jdolecek {
    130  1.9.14.2  jdolecek 	ep->h_hash = hash;
    131  1.9.14.2  jdolecek }
    132  1.9.14.2  jdolecek 
    133  1.9.14.2  jdolecek static void
    134  1.9.14.2  jdolecek ext2fs_htree_set_limit(struct ext2fs_htree_entry *ep, uint16_t limit)
    135  1.9.14.2  jdolecek {
    136  1.9.14.2  jdolecek 	((struct ext2fs_htree_count *)(ep))->h_entries_max = limit;
    137  1.9.14.2  jdolecek }
    138  1.9.14.2  jdolecek 
    139  1.9.14.2  jdolecek static uint32_t
    140  1.9.14.2  jdolecek ext2fs_htree_node_limit(struct inode *ip)
    141  1.9.14.2  jdolecek {
    142  1.9.14.2  jdolecek 	struct m_ext2fs *fs;
    143  1.9.14.2  jdolecek 	uint32_t space;
    144  1.9.14.2  jdolecek 
    145  1.9.14.2  jdolecek 	fs = ip->i_e2fs;
    146  1.9.14.2  jdolecek 	space = fs->e2fs_bsize - EXT2_DIR_REC_LEN(0);
    147  1.9.14.2  jdolecek 
    148  1.9.14.2  jdolecek 	return space / sizeof(struct ext2fs_htree_entry);
    149  1.9.14.2  jdolecek }
    150  1.9.14.2  jdolecek 
    151  1.9.14.2  jdolecek static int
    152  1.9.14.2  jdolecek ext2fs_htree_append_block(struct vnode *vp, char *data,
    153  1.9.14.2  jdolecek     struct componentname *cnp, uint32_t blksize)
    154  1.9.14.2  jdolecek {
    155  1.9.14.2  jdolecek 	struct iovec aiov;
    156  1.9.14.2  jdolecek 	struct uio auio;
    157  1.9.14.2  jdolecek 	struct inode *dp = VTOI(vp);
    158  1.9.14.2  jdolecek 	uint64_t cursize, newsize;
    159  1.9.14.2  jdolecek 	int error;
    160  1.9.14.2  jdolecek 
    161  1.9.14.2  jdolecek 	cursize = roundup(dp->i_size, blksize);
    162  1.9.14.2  jdolecek 	newsize = cursize + blksize;
    163  1.9.14.2  jdolecek 
    164  1.9.14.2  jdolecek 	auio.uio_offset = cursize;
    165  1.9.14.2  jdolecek 	auio.uio_resid = blksize;
    166  1.9.14.2  jdolecek 	aiov.iov_len = blksize;
    167  1.9.14.2  jdolecek 	aiov.iov_base = data;
    168  1.9.14.2  jdolecek 	auio.uio_iov = &aiov;
    169  1.9.14.2  jdolecek 	auio.uio_iovcnt = 1;
    170  1.9.14.2  jdolecek 	auio.uio_rw = UIO_WRITE;
    171  1.9.14.2  jdolecek 	auio.uio_vmspace = vmspace_kernel();
    172  1.9.14.2  jdolecek 	error = VOP_WRITE(vp, &auio, IO_SYNC, cnp->cn_cred);
    173  1.9.14.2  jdolecek 	if (!error)
    174  1.9.14.2  jdolecek 		dp->i_size = newsize;
    175  1.9.14.2  jdolecek 
    176  1.9.14.2  jdolecek 	return error;
    177  1.9.14.2  jdolecek }
    178  1.9.14.2  jdolecek 
    179  1.9.14.2  jdolecek static int
    180  1.9.14.2  jdolecek ext2fs_htree_writebuf(struct ext2fs_htree_lookup_info *info)
    181  1.9.14.2  jdolecek {
    182  1.9.14.2  jdolecek 	int i, error;
    183  1.9.14.2  jdolecek 
    184  1.9.14.2  jdolecek 	for (i = 0; i < info->h_levels_num; i++) {
    185  1.9.14.2  jdolecek 		struct buf *bp = info->h_levels[i].h_bp;
    186  1.9.14.2  jdolecek 		error = bwrite(bp);
    187  1.9.14.2  jdolecek 		if (error)
    188  1.9.14.2  jdolecek 			return error;
    189  1.9.14.2  jdolecek 	}
    190  1.9.14.2  jdolecek 
    191  1.9.14.2  jdolecek 	return 0;
    192  1.9.14.2  jdolecek }
    193  1.9.14.2  jdolecek 
    194  1.9.14.2  jdolecek static void
    195  1.9.14.2  jdolecek ext2fs_htree_insert_entry_to_level(struct ext2fs_htree_lookup_level *level,
    196  1.9.14.2  jdolecek     uint32_t hash, uint32_t blk)
    197  1.9.14.2  jdolecek {
    198  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *target;
    199  1.9.14.2  jdolecek 	int entries_num;
    200  1.9.14.2  jdolecek 
    201  1.9.14.2  jdolecek 	target = level->h_entry + 1;
    202  1.9.14.2  jdolecek 	entries_num = ext2fs_htree_get_count(level->h_entries);
    203  1.9.14.2  jdolecek 
    204  1.9.14.2  jdolecek 	memmove(target + 1, target, (char *)(level->h_entries + entries_num) -
    205  1.9.14.2  jdolecek 	    (char *)target);
    206  1.9.14.2  jdolecek 	ext2fs_htree_set_block(target, blk);
    207  1.9.14.2  jdolecek 	ext2fs_htree_set_hash(target, hash);
    208  1.9.14.2  jdolecek 	ext2fs_htree_set_count(level->h_entries, entries_num + 1);
    209  1.9.14.2  jdolecek }
    210  1.9.14.2  jdolecek 
    211  1.9.14.2  jdolecek /*
    212  1.9.14.2  jdolecek  * Insert an index entry to the index node.
    213  1.9.14.2  jdolecek  */
    214  1.9.14.2  jdolecek static void
    215  1.9.14.2  jdolecek ext2fs_htree_insert_entry(struct ext2fs_htree_lookup_info *info,
    216  1.9.14.2  jdolecek     uint32_t hash, uint32_t blk)
    217  1.9.14.2  jdolecek {
    218  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_level *level;
    219  1.9.14.2  jdolecek 
    220  1.9.14.2  jdolecek 	level = &info->h_levels[info->h_levels_num - 1];
    221  1.9.14.2  jdolecek 	ext2fs_htree_insert_entry_to_level(level, hash, blk);
    222  1.9.14.2  jdolecek }
    223  1.9.14.2  jdolecek 
    224  1.9.14.2  jdolecek /*
    225  1.9.14.2  jdolecek  * Compare two entry sort descriptors by name hash value.
    226  1.9.14.2  jdolecek  * This is used together with qsort.
    227  1.9.14.2  jdolecek  */
    228  1.9.14.2  jdolecek static int
    229  1.9.14.2  jdolecek ext2fs_htree_cmp_sort_entry(const void *e1, const void *e2)
    230  1.9.14.2  jdolecek {
    231  1.9.14.2  jdolecek 	const struct ext2fs_htree_sort_entry *entry1, *entry2;
    232  1.9.14.2  jdolecek 
    233  1.9.14.2  jdolecek 	entry1 = (const struct ext2fs_htree_sort_entry *)e1;
    234  1.9.14.2  jdolecek 	entry2 = (const struct ext2fs_htree_sort_entry *)e2;
    235  1.9.14.2  jdolecek 
    236  1.9.14.2  jdolecek 	if (entry1->h_hash < entry2->h_hash)
    237  1.9.14.2  jdolecek 		return -1;
    238  1.9.14.2  jdolecek 	if (entry1->h_hash > entry2->h_hash)
    239  1.9.14.2  jdolecek 		return 1;
    240  1.9.14.2  jdolecek 	return 0;
    241  1.9.14.2  jdolecek }
    242  1.9.14.2  jdolecek 
    243  1.9.14.2  jdolecek /*
    244  1.9.14.2  jdolecek  * Append an entry to the end of the directory block.
    245  1.9.14.2  jdolecek  */
    246  1.9.14.2  jdolecek static void
    247  1.9.14.2  jdolecek ext2fs_append_entry(char *block, uint32_t blksize,
    248  1.9.14.2  jdolecek     struct ext2fs_direct *last_entry, struct ext2fs_direct *new_entry)
    249  1.9.14.2  jdolecek {
    250  1.9.14.2  jdolecek 	uint16_t entry_len;
    251  1.9.14.2  jdolecek 
    252  1.9.14.2  jdolecek 	entry_len = EXT2_DIR_REC_LEN(last_entry->e2d_namlen);
    253  1.9.14.2  jdolecek 	last_entry->e2d_reclen = entry_len;
    254  1.9.14.2  jdolecek 	last_entry = (struct ext2fs_direct *)((char *)last_entry + entry_len);
    255  1.9.14.2  jdolecek 	new_entry->e2d_reclen = block + blksize - (char *)last_entry;
    256  1.9.14.2  jdolecek 	memcpy(last_entry, new_entry, EXT2_DIR_REC_LEN(new_entry->e2d_namlen));
    257  1.9.14.2  jdolecek }
    258  1.9.14.2  jdolecek 
    259  1.9.14.2  jdolecek /*
    260  1.9.14.2  jdolecek  * Move half of entries from the old directory block to the new one.
    261  1.9.14.2  jdolecek  */
    262  1.9.14.2  jdolecek static int
    263  1.9.14.2  jdolecek ext2fs_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
    264  1.9.14.2  jdolecek     uint32_t *hash_seed, uint8_t hash_version,
    265  1.9.14.2  jdolecek     uint32_t *split_hash, struct ext2fs_direct *entry)
    266  1.9.14.2  jdolecek {
    267  1.9.14.2  jdolecek 	int entry_cnt = 0;
    268  1.9.14.2  jdolecek 	int size = 0;
    269  1.9.14.2  jdolecek 	int i, k;
    270  1.9.14.2  jdolecek 	uint32_t offset;
    271  1.9.14.2  jdolecek 	uint16_t entry_len = 0;
    272  1.9.14.2  jdolecek 	uint32_t entry_hash;
    273  1.9.14.2  jdolecek 	struct ext2fs_direct *ep, *last;
    274  1.9.14.2  jdolecek 	char *dest;
    275  1.9.14.2  jdolecek 	struct ext2fs_htree_sort_entry *sort_info, dummy;
    276  1.9.14.2  jdolecek 
    277  1.9.14.2  jdolecek 	ep = (struct ext2fs_direct *)block1;
    278  1.9.14.2  jdolecek 	dest = block2;
    279  1.9.14.2  jdolecek 	sort_info = (struct ext2fs_htree_sort_entry *)
    280  1.9.14.2  jdolecek 	    ((char *)block2 + blksize);
    281  1.9.14.2  jdolecek 
    282  1.9.14.2  jdolecek 	/*
    283  1.9.14.2  jdolecek 	 * Calculate name hash value for the entry which is to be added.
    284  1.9.14.2  jdolecek 	 */
    285  1.9.14.2  jdolecek 	ext2fs_htree_hash(entry->e2d_name, entry->e2d_namlen, hash_seed,
    286  1.9.14.2  jdolecek 	    hash_version, &entry_hash, NULL);
    287  1.9.14.2  jdolecek 
    288  1.9.14.2  jdolecek 	/*
    289  1.9.14.2  jdolecek 	 * Fill in directory entry sort descriptors.
    290  1.9.14.2  jdolecek 	 */
    291  1.9.14.2  jdolecek 	while ((char *)ep < block1 + blksize) {
    292  1.9.14.2  jdolecek 		if (ep->e2d_ino && ep->e2d_namlen) {
    293  1.9.14.2  jdolecek 			entry_cnt++;
    294  1.9.14.2  jdolecek 			sort_info--;
    295  1.9.14.2  jdolecek 			sort_info->h_size = ep->e2d_reclen;
    296  1.9.14.2  jdolecek 			sort_info->h_offset = (char *)ep - block1;
    297  1.9.14.2  jdolecek 			ext2fs_htree_hash(ep->e2d_name, ep->e2d_namlen,
    298  1.9.14.2  jdolecek 			    hash_seed, hash_version,
    299  1.9.14.2  jdolecek 			    &sort_info->h_hash, NULL);
    300  1.9.14.2  jdolecek 		}
    301  1.9.14.2  jdolecek 		ep = (struct ext2fs_direct *)
    302  1.9.14.2  jdolecek 		    ((char *)ep + ep->e2d_reclen);
    303  1.9.14.2  jdolecek 	}
    304  1.9.14.2  jdolecek 
    305  1.9.14.2  jdolecek 	/*
    306  1.9.14.2  jdolecek 	 * Sort directory entry descriptors by name hash value.
    307  1.9.14.2  jdolecek 	 */
    308  1.9.14.2  jdolecek 	kheapsort(sort_info, entry_cnt, sizeof(struct ext2fs_htree_sort_entry),
    309  1.9.14.2  jdolecek 	    ext2fs_htree_cmp_sort_entry, &dummy);
    310  1.9.14.2  jdolecek 
    311  1.9.14.2  jdolecek 	/*
    312  1.9.14.2  jdolecek 	 * Count the number of entries to move to directory block 2.
    313  1.9.14.2  jdolecek 	 */
    314  1.9.14.2  jdolecek 	for (i = entry_cnt - 1; i >= 0; i--) {
    315  1.9.14.2  jdolecek 		if (sort_info[i].h_size + size > blksize / 2)
    316  1.9.14.2  jdolecek 			break;
    317  1.9.14.2  jdolecek 		size += sort_info[i].h_size;
    318  1.9.14.2  jdolecek 	}
    319  1.9.14.2  jdolecek 
    320  1.9.14.2  jdolecek 	*split_hash = sort_info[i + 1].h_hash;
    321  1.9.14.2  jdolecek 
    322  1.9.14.2  jdolecek 	/*
    323  1.9.14.2  jdolecek 	 * Set collision bit.
    324  1.9.14.2  jdolecek 	 */
    325  1.9.14.2  jdolecek 	if (*split_hash == sort_info[i].h_hash)
    326  1.9.14.2  jdolecek 		*split_hash += 1;
    327  1.9.14.2  jdolecek 
    328  1.9.14.2  jdolecek 	/*
    329  1.9.14.2  jdolecek 	 * Move half of directory entries from block 1 to block 2.
    330  1.9.14.2  jdolecek 	 */
    331  1.9.14.2  jdolecek 	for (k = i + 1; k < entry_cnt; k++) {
    332  1.9.14.2  jdolecek 		ep = (struct ext2fs_direct *)((char *)block1 +
    333  1.9.14.2  jdolecek 		    sort_info[k].h_offset);
    334  1.9.14.2  jdolecek 		entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
    335  1.9.14.2  jdolecek 		memcpy(dest, ep, entry_len);
    336  1.9.14.2  jdolecek 		((struct ext2fs_direct *)dest)->e2d_reclen = entry_len;
    337  1.9.14.2  jdolecek 		/* Mark directory entry as unused. */
    338  1.9.14.2  jdolecek 		ep->e2d_ino = 0;
    339  1.9.14.2  jdolecek 		dest += entry_len;
    340  1.9.14.2  jdolecek 	}
    341  1.9.14.2  jdolecek 	dest -= entry_len;
    342  1.9.14.2  jdolecek 
    343  1.9.14.2  jdolecek 	/* Shrink directory entries in block 1. */
    344  1.9.14.2  jdolecek 	last = (struct ext2fs_direct *)block1;
    345  1.9.14.2  jdolecek 	entry_len = 0;
    346  1.9.14.2  jdolecek 	for (offset = 0; offset < blksize;) {
    347  1.9.14.2  jdolecek 		ep = (struct ext2fs_direct *)(block1 + offset);
    348  1.9.14.2  jdolecek 		offset += ep->e2d_reclen;
    349  1.9.14.2  jdolecek 		if (ep->e2d_ino) {
    350  1.9.14.2  jdolecek 			last = (struct ext2fs_direct *)
    351  1.9.14.2  jdolecek 			   ((char *)last + entry_len);
    352  1.9.14.2  jdolecek 			entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
    353  1.9.14.2  jdolecek 			memcpy((void *)last, (void *)ep, entry_len);
    354  1.9.14.2  jdolecek 			last->e2d_reclen = entry_len;
    355  1.9.14.2  jdolecek 		}
    356  1.9.14.2  jdolecek 	}
    357  1.9.14.2  jdolecek 
    358  1.9.14.2  jdolecek 	if (entry_hash >= *split_hash) {
    359  1.9.14.2  jdolecek 		/* Add entry to block 2. */
    360  1.9.14.2  jdolecek 		ext2fs_append_entry(block2, blksize,
    361  1.9.14.2  jdolecek 		    (struct ext2fs_direct *)dest, entry);
    362  1.9.14.2  jdolecek 
    363  1.9.14.2  jdolecek 		/* Adjust length field of last entry of block 1. */
    364  1.9.14.2  jdolecek 		last->e2d_reclen = block1 + blksize - (char *)last;
    365  1.9.14.2  jdolecek 	} else {
    366  1.9.14.2  jdolecek 		/* Add entry to block 1. */
    367  1.9.14.2  jdolecek 		ext2fs_append_entry(block1, blksize, last, entry);
    368  1.9.14.2  jdolecek 
    369  1.9.14.2  jdolecek 		/* Adjust length field of last entry of block 2. */
    370  1.9.14.2  jdolecek 		((struct ext2fs_direct *)dest)->e2d_reclen =
    371  1.9.14.2  jdolecek 		    block2 + blksize - dest;
    372  1.9.14.2  jdolecek 	}
    373  1.9.14.2  jdolecek 
    374  1.9.14.2  jdolecek 	return 0;
    375  1.9.14.2  jdolecek }
    376  1.9.14.2  jdolecek 
    377  1.9.14.2  jdolecek /*
    378  1.9.14.2  jdolecek  * Create an HTree index for a directory having entries which are no more
    379  1.9.14.2  jdolecek  * accommodable in a single dir-block.
    380  1.9.14.2  jdolecek  */
    381  1.9.14.2  jdolecek int
    382  1.9.14.2  jdolecek ext2fs_htree_create_index(struct vnode *vp, struct componentname *cnp,
    383  1.9.14.2  jdolecek     struct ext2fs_direct *new_entry)
    384  1.9.14.2  jdolecek {
    385  1.9.14.2  jdolecek 	struct buf *bp = NULL;
    386  1.9.14.2  jdolecek 	struct inode *dp;
    387  1.9.14.2  jdolecek 	struct ext2fs *fs;
    388  1.9.14.2  jdolecek 	struct m_ext2fs *m_fs;
    389  1.9.14.2  jdolecek 	struct ext2fs_direct *ep, *dotdot;
    390  1.9.14.2  jdolecek 	struct ext2fs_htree_root *root;
    391  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_info info;
    392  1.9.14.2  jdolecek 	uint32_t blksize, dirlen, split_hash;
    393  1.9.14.2  jdolecek 	uint8_t hash_version;
    394  1.9.14.2  jdolecek 	char *buf1 = NULL;
    395  1.9.14.2  jdolecek 	char *buf2 = NULL;
    396  1.9.14.2  jdolecek 	int error = 0;
    397  1.9.14.2  jdolecek 
    398  1.9.14.2  jdolecek 	dp = VTOI(vp);
    399  1.9.14.2  jdolecek 	fs = &(dp->i_e2fs->e2fs);
    400  1.9.14.2  jdolecek 	m_fs = dp->i_e2fs;
    401  1.9.14.2  jdolecek 	blksize = m_fs->e2fs_bsize;
    402  1.9.14.2  jdolecek 
    403  1.9.14.2  jdolecek 	buf1 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
    404  1.9.14.2  jdolecek 	buf2 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
    405  1.9.14.2  jdolecek 
    406  1.9.14.2  jdolecek 	if ((error = ext2fs_blkatoff(vp, 0, NULL, &bp)) != 0)
    407  1.9.14.2  jdolecek 		goto out;
    408  1.9.14.2  jdolecek 
    409  1.9.14.2  jdolecek 	root = (struct ext2fs_htree_root *)bp->b_data;
    410  1.9.14.2  jdolecek 	dotdot = (struct ext2fs_direct *)((char *)&(root->h_dotdot));
    411  1.9.14.2  jdolecek 	ep = (struct ext2fs_direct *)((char *)dotdot + dotdot->e2d_reclen);
    412  1.9.14.2  jdolecek 	dirlen = (char *)root + blksize - (char *)ep;
    413  1.9.14.2  jdolecek 	memcpy(buf1, ep, dirlen);
    414  1.9.14.2  jdolecek 	ep = (struct ext2fs_direct *)buf1;
    415  1.9.14.2  jdolecek 	while ((char *)ep < buf1 + dirlen)
    416  1.9.14.2  jdolecek 		ep = (struct ext2fs_direct *)((char *)ep + ep->e2d_reclen);
    417  1.9.14.2  jdolecek 	ep->e2d_reclen = buf1 + blksize - (char *)ep;
    418  1.9.14.2  jdolecek 	/* XXX It should be made dp->i_flag |= IN_E3INDEX; */
    419  1.9.14.2  jdolecek 	dp->i_din.e2fs_din->e2di_flags |= EXT2_INDEX;
    420  1.9.14.2  jdolecek 
    421  1.9.14.2  jdolecek 	/*
    422  1.9.14.2  jdolecek 	 * Initialize index root.
    423  1.9.14.2  jdolecek 	 */
    424  1.9.14.2  jdolecek 	dotdot->e2d_reclen = blksize - EXT2_DIR_REC_LEN(1);
    425  1.9.14.2  jdolecek 	memset(&root->h_info, 0, sizeof(root->h_info));
    426  1.9.14.2  jdolecek 	root->h_info.h_hash_version = fs->e3fs_def_hash_version;
    427  1.9.14.2  jdolecek 	root->h_info.h_info_len = sizeof(root->h_info);
    428  1.9.14.2  jdolecek 	ext2fs_htree_set_block(root->h_entries, 1);
    429  1.9.14.2  jdolecek 	ext2fs_htree_set_count(root->h_entries, 1);
    430  1.9.14.2  jdolecek 	ext2fs_htree_set_limit(root->h_entries,
    431  1.9.14.2  jdolecek 	    ext2fs_htree_root_limit(dp, sizeof(root->h_info)));
    432  1.9.14.2  jdolecek 
    433  1.9.14.2  jdolecek 	memset(&info, 0, sizeof(info));
    434  1.9.14.2  jdolecek 	info.h_levels_num = 1;
    435  1.9.14.2  jdolecek 	info.h_levels[0].h_entries = root->h_entries;
    436  1.9.14.2  jdolecek 	info.h_levels[0].h_entry = root->h_entries;
    437  1.9.14.2  jdolecek 
    438  1.9.14.2  jdolecek 	hash_version = root->h_info.h_hash_version;
    439  1.9.14.2  jdolecek 	if (hash_version <= EXT2_HTREE_TEA)
    440  1.9.14.2  jdolecek 		hash_version += m_fs->e2fs_uhash;
    441  1.9.14.2  jdolecek 	ext2fs_htree_split_dirblock(buf1, buf2, blksize, fs->e3fs_hash_seed,
    442  1.9.14.2  jdolecek 	    hash_version, &split_hash, new_entry);
    443  1.9.14.2  jdolecek 	ext2fs_htree_insert_entry(&info, split_hash, 2);
    444  1.9.14.2  jdolecek 
    445  1.9.14.2  jdolecek 	/*
    446  1.9.14.2  jdolecek 	 * Write directory block 0.
    447  1.9.14.2  jdolecek 	 */
    448  1.9.14.2  jdolecek 	if ( (vp)->v_mount->mnt_iflag & IO_SYNC)
    449  1.9.14.2  jdolecek 		(void)bwrite(bp);
    450  1.9.14.2  jdolecek 	else
    451  1.9.14.2  jdolecek 		bdwrite(bp);
    452  1.9.14.2  jdolecek 
    453  1.9.14.2  jdolecek 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
    454  1.9.14.2  jdolecek 
    455  1.9.14.2  jdolecek 	/*
    456  1.9.14.2  jdolecek 	 * Write directory block 1.
    457  1.9.14.2  jdolecek 	 */
    458  1.9.14.2  jdolecek 	error = ext2fs_htree_append_block(vp, buf1, cnp, blksize);
    459  1.9.14.2  jdolecek 	if (error)
    460  1.9.14.2  jdolecek 		goto out1;
    461  1.9.14.2  jdolecek 
    462  1.9.14.2  jdolecek 	/*
    463  1.9.14.2  jdolecek 	 * Write directory block 2.
    464  1.9.14.2  jdolecek 	 */
    465  1.9.14.2  jdolecek 	error = ext2fs_htree_append_block(vp, buf2, cnp, blksize);
    466  1.9.14.2  jdolecek 	goto out1;
    467  1.9.14.2  jdolecek out:
    468  1.9.14.2  jdolecek 	if (bp != NULL)
    469  1.9.14.2  jdolecek 		brelse(bp, 0);
    470  1.9.14.2  jdolecek out1:
    471  1.9.14.2  jdolecek 	free(buf1, M_TEMP);
    472  1.9.14.2  jdolecek 	free(buf2, M_TEMP);
    473  1.9.14.2  jdolecek 	return error;
    474  1.9.14.2  jdolecek }
    475  1.9.14.2  jdolecek 
    476  1.9.14.2  jdolecek /*
    477  1.9.14.2  jdolecek  * Add an entry to the directory using htree index.
    478  1.9.14.2  jdolecek  */
    479  1.9.14.2  jdolecek int
    480  1.9.14.2  jdolecek ext2fs_htree_add_entry(struct vnode *dvp, struct ext2fs_direct *entry,
    481  1.9.14.2  jdolecek     struct componentname *cnp, size_t newentrysize)
    482  1.9.14.2  jdolecek {
    483  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *entries, *leaf_node;
    484  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_info info;
    485  1.9.14.2  jdolecek 	struct buf *bp = NULL;
    486  1.9.14.2  jdolecek 	struct ext2fs *fs;
    487  1.9.14.2  jdolecek 	struct m_ext2fs *m_fs;
    488  1.9.14.2  jdolecek 	struct inode *ip;
    489  1.9.14.2  jdolecek 	uint16_t ent_num;
    490  1.9.14.2  jdolecek 	uint32_t dirhash, split_hash;
    491  1.9.14.2  jdolecek 	uint32_t blksize, blknum;
    492  1.9.14.2  jdolecek 	uint64_t cursize, dirsize;
    493  1.9.14.2  jdolecek 	uint8_t hash_version;
    494  1.9.14.2  jdolecek 	char *newdirblock = NULL;
    495  1.9.14.2  jdolecek 	char *newidxblock = NULL;
    496  1.9.14.2  jdolecek 	struct ext2fs_htree_node *dst_node;
    497  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *dst_entries;
    498  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *root_entires;
    499  1.9.14.2  jdolecek 	struct buf *dst_bp = NULL;
    500  1.9.14.2  jdolecek 	int error, write_bp = 0, write_dst_bp = 0, write_info = 0;
    501  1.9.14.2  jdolecek 
    502  1.9.14.2  jdolecek 	ip = VTOI(dvp);
    503  1.9.14.2  jdolecek 	m_fs = ip->i_e2fs;
    504  1.9.14.2  jdolecek 	fs = &(m_fs->e2fs);
    505  1.9.14.2  jdolecek 	blksize = m_fs->e2fs_bsize;
    506  1.9.14.2  jdolecek 
    507  1.9.14.2  jdolecek 	if (ip->i_crap.ulr_count != 0)
    508  1.9.14.2  jdolecek 		return ext2fs_add_entry(dvp, entry, &(ip->i_crap), newentrysize);
    509  1.9.14.2  jdolecek 
    510  1.9.14.2  jdolecek 	/* Target directory block is full, split it */
    511  1.9.14.2  jdolecek 	memset(&info, 0, sizeof(info));
    512  1.9.14.2  jdolecek 	error = ext2fs_htree_find_leaf(ip, entry->e2d_name, entry->e2d_namlen,
    513  1.9.14.2  jdolecek 	    &dirhash, &hash_version, &info);
    514  1.9.14.2  jdolecek 	if (error)
    515  1.9.14.2  jdolecek 		return error;
    516  1.9.14.2  jdolecek 	entries = info.h_levels[info.h_levels_num - 1].h_entries;
    517  1.9.14.2  jdolecek 	ent_num = ext2fs_htree_get_count(entries);
    518  1.9.14.2  jdolecek 	if (ent_num == ext2fs_htree_get_limit(entries)) {
    519  1.9.14.2  jdolecek 		/* Split the index node. */
    520  1.9.14.2  jdolecek 		root_entires = info.h_levels[0].h_entries;
    521  1.9.14.2  jdolecek 		newidxblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
    522  1.9.14.2  jdolecek 		dst_node = (struct ext2fs_htree_node *)newidxblock;
    523  1.9.14.2  jdolecek 		dst_entries = dst_node->h_entries;
    524  1.9.14.2  jdolecek 		memset(&dst_node->h_fake_dirent, 0,
    525  1.9.14.2  jdolecek 		    sizeof(dst_node->h_fake_dirent));
    526  1.9.14.2  jdolecek 		dst_node->h_fake_dirent.e2d_reclen = blksize;
    527  1.9.14.2  jdolecek 
    528  1.9.14.2  jdolecek 		cursize = roundup(ip->i_size, blksize);
    529  1.9.14.2  jdolecek 		dirsize = cursize + blksize;
    530  1.9.14.2  jdolecek 		blknum = dirsize / blksize - 1;
    531  1.9.14.2  jdolecek 
    532  1.9.14.2  jdolecek 		error = ext2fs_htree_append_block(dvp, newidxblock,
    533  1.9.14.2  jdolecek 		    cnp, blksize);
    534  1.9.14.2  jdolecek 		if (error)
    535  1.9.14.2  jdolecek 			goto finish;
    536  1.9.14.2  jdolecek 		error = ext2fs_blkatoff(dvp, cursize, NULL, &dst_bp);
    537  1.9.14.2  jdolecek 		if (error)
    538  1.9.14.2  jdolecek 			goto finish;
    539  1.9.14.2  jdolecek 
    540  1.9.14.2  jdolecek 		dst_node = (struct ext2fs_htree_node *)dst_bp->b_data;
    541  1.9.14.2  jdolecek 		dst_entries = dst_node->h_entries;
    542  1.9.14.2  jdolecek 
    543  1.9.14.2  jdolecek 		if (info.h_levels_num == 2) {
    544  1.9.14.2  jdolecek 			uint16_t src_ent_num, dst_ent_num;
    545  1.9.14.2  jdolecek 
    546  1.9.14.2  jdolecek 			if (ext2fs_htree_get_count(root_entires) ==
    547  1.9.14.2  jdolecek 			    ext2fs_htree_get_limit(root_entires)) {
    548  1.9.14.2  jdolecek 				/* Directory index is full */
    549  1.9.14.2  jdolecek 				error = EIO;
    550  1.9.14.2  jdolecek 				goto finish;
    551  1.9.14.2  jdolecek 			}
    552  1.9.14.2  jdolecek 
    553  1.9.14.2  jdolecek 			src_ent_num = ent_num / 2;
    554  1.9.14.2  jdolecek 			dst_ent_num = ent_num - src_ent_num;
    555  1.9.14.2  jdolecek 			split_hash = ext2fs_htree_get_hash(entries + src_ent_num);
    556  1.9.14.2  jdolecek 
    557  1.9.14.2  jdolecek 			/* Move half of index entries to the new index node */
    558  1.9.14.2  jdolecek 			memcpy(dst_entries, entries + src_ent_num,
    559  1.9.14.2  jdolecek 			    dst_ent_num * sizeof(struct ext2fs_htree_entry));
    560  1.9.14.2  jdolecek 			ext2fs_htree_set_count(entries, src_ent_num);
    561  1.9.14.2  jdolecek 			ext2fs_htree_set_count(dst_entries, dst_ent_num);
    562  1.9.14.2  jdolecek 			ext2fs_htree_set_limit(dst_entries,
    563  1.9.14.2  jdolecek 			    ext2fs_htree_node_limit(ip));
    564  1.9.14.2  jdolecek 
    565  1.9.14.2  jdolecek 			if (info.h_levels[1].h_entry >= entries + src_ent_num) {
    566  1.9.14.2  jdolecek 				struct buf *tmp = info.h_levels[1].h_bp;
    567  1.9.14.2  jdolecek 				info.h_levels[1].h_bp = dst_bp;
    568  1.9.14.2  jdolecek 				dst_bp = tmp;
    569  1.9.14.2  jdolecek 
    570  1.9.14.2  jdolecek 				info.h_levels[1].h_entry =
    571  1.9.14.2  jdolecek 				    info.h_levels[1].h_entry -
    572  1.9.14.2  jdolecek 				    (entries + src_ent_num) +
    573  1.9.14.2  jdolecek 				    dst_entries;
    574  1.9.14.2  jdolecek 				info.h_levels[1].h_entries = dst_entries;
    575  1.9.14.2  jdolecek 			}
    576  1.9.14.2  jdolecek 			ext2fs_htree_insert_entry_to_level(&info.h_levels[0],
    577  1.9.14.2  jdolecek 			    split_hash, blknum);
    578  1.9.14.2  jdolecek 
    579  1.9.14.2  jdolecek 			/* Write new index node to disk */
    580  1.9.14.2  jdolecek 			error = bwrite(dst_bp);
    581  1.9.14.2  jdolecek 			ip->i_flag |= IN_CHANGE | IN_UPDATE;
    582  1.9.14.2  jdolecek 			if (error)
    583  1.9.14.2  jdolecek 				goto finish;
    584  1.9.14.2  jdolecek 
    585  1.9.14.2  jdolecek 			write_dst_bp = 1;
    586  1.9.14.2  jdolecek 		} else {
    587  1.9.14.2  jdolecek 			/* Create second level for htree index */
    588  1.9.14.2  jdolecek 
    589  1.9.14.2  jdolecek 			struct ext2fs_htree_root *idx_root;
    590  1.9.14.2  jdolecek 
    591  1.9.14.2  jdolecek 			memcpy(dst_entries, entries,
    592  1.9.14.2  jdolecek 			    ent_num * sizeof(struct ext2fs_htree_entry));
    593  1.9.14.2  jdolecek 			ext2fs_htree_set_limit(dst_entries,
    594  1.9.14.2  jdolecek 			    ext2fs_htree_node_limit(ip));
    595  1.9.14.2  jdolecek 
    596  1.9.14.2  jdolecek 			idx_root = (struct ext2fs_htree_root *)
    597  1.9.14.2  jdolecek 			    info.h_levels[0].h_bp->b_data;
    598  1.9.14.2  jdolecek 			idx_root->h_info.h_ind_levels = 1;
    599  1.9.14.2  jdolecek 
    600  1.9.14.2  jdolecek 			ext2fs_htree_set_count(entries, 1);
    601  1.9.14.2  jdolecek 			ext2fs_htree_set_block(entries, blknum);
    602  1.9.14.2  jdolecek 
    603  1.9.14.2  jdolecek 			info.h_levels_num = 2;
    604  1.9.14.2  jdolecek 			info.h_levels[1].h_entries = dst_entries;
    605  1.9.14.2  jdolecek 			info.h_levels[1].h_entry = info.h_levels[0].h_entry -
    606  1.9.14.2  jdolecek 			    info.h_levels[0].h_entries + dst_entries;
    607  1.9.14.2  jdolecek 			info.h_levels[1].h_bp = dst_bp;
    608  1.9.14.2  jdolecek 			dst_bp = NULL;
    609  1.9.14.2  jdolecek 		}
    610  1.9.14.2  jdolecek 	}
    611  1.9.14.2  jdolecek 
    612  1.9.14.2  jdolecek 	leaf_node = info.h_levels[info.h_levels_num - 1].h_entry;
    613  1.9.14.2  jdolecek 	blknum = ext2fs_htree_get_block(leaf_node);
    614  1.9.14.2  jdolecek 	error = ext2fs_blkatoff(dvp, blknum * blksize, NULL, &bp);
    615  1.9.14.2  jdolecek 	if (error)
    616  1.9.14.2  jdolecek 		goto finish;
    617  1.9.14.2  jdolecek 
    618  1.9.14.2  jdolecek 	/* Split target directory block */
    619  1.9.14.2  jdolecek 	newdirblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
    620  1.9.14.2  jdolecek 	ext2fs_htree_split_dirblock((char *)bp->b_data, newdirblock, blksize,
    621  1.9.14.2  jdolecek 	    fs->e3fs_hash_seed, hash_version, &split_hash, entry);
    622  1.9.14.2  jdolecek 	cursize = roundup(ip->i_size, blksize);
    623  1.9.14.2  jdolecek 	dirsize = cursize + blksize;
    624  1.9.14.2  jdolecek 	blknum = dirsize / blksize - 1;
    625  1.9.14.2  jdolecek 
    626  1.9.14.2  jdolecek 	/* Add index entry for the new directory block */
    627  1.9.14.2  jdolecek 	ext2fs_htree_insert_entry(&info, split_hash, blknum);
    628  1.9.14.2  jdolecek 
    629  1.9.14.2  jdolecek 	/* Write the new directory block to the end of the directory */
    630  1.9.14.2  jdolecek 	error = ext2fs_htree_append_block(dvp, newdirblock, cnp, blksize);
    631  1.9.14.2  jdolecek 
    632  1.9.14.2  jdolecek 	if (error)
    633  1.9.14.2  jdolecek 		goto finish;
    634  1.9.14.2  jdolecek 
    635  1.9.14.2  jdolecek 	/* Write the target directory block */
    636  1.9.14.2  jdolecek 	error = bwrite(bp);
    637  1.9.14.2  jdolecek 	ip->i_flag |= IN_CHANGE | IN_UPDATE;
    638  1.9.14.2  jdolecek 
    639  1.9.14.2  jdolecek 	if (error)
    640  1.9.14.2  jdolecek 		goto finish;
    641  1.9.14.2  jdolecek 	write_bp = 1;
    642  1.9.14.2  jdolecek 
    643  1.9.14.2  jdolecek 	/* Write the index block */
    644  1.9.14.2  jdolecek 	error = ext2fs_htree_writebuf(&info);
    645  1.9.14.2  jdolecek 	if (!error)
    646  1.9.14.2  jdolecek 		write_info = 1;
    647  1.9.14.2  jdolecek 
    648  1.9.14.2  jdolecek finish:
    649  1.9.14.2  jdolecek 	if (dst_bp != NULL && !write_dst_bp)
    650  1.9.14.2  jdolecek 		brelse(dst_bp, 0);
    651  1.9.14.2  jdolecek 	if (bp != NULL && !write_bp)
    652  1.9.14.2  jdolecek 		brelse(bp, 0);
    653  1.9.14.2  jdolecek 	if (newdirblock != NULL)
    654  1.9.14.2  jdolecek 		free(newdirblock, M_TEMP);
    655  1.9.14.2  jdolecek 	if (newidxblock != NULL)
    656  1.9.14.2  jdolecek 		free(newidxblock, M_TEMP);
    657  1.9.14.2  jdolecek 	if (!write_info)
    658  1.9.14.2  jdolecek 		ext2fs_htree_release(&info);
    659  1.9.14.2  jdolecek 	return error;
    660  1.9.14.2  jdolecek }
    661  1.9.14.2  jdolecek 
    662  1.9.14.2  jdolecek static int
    663  1.9.14.2  jdolecek ext2fs_htree_check_next(struct inode *ip, uint32_t hash, const char *name,
    664  1.9.14.2  jdolecek     struct ext2fs_htree_lookup_info *info)
    665  1.9.14.2  jdolecek {
    666  1.9.14.2  jdolecek 	struct vnode *vp = ITOV(ip);
    667  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_level *level;
    668  1.9.14.2  jdolecek 	struct buf *bp;
    669  1.9.14.2  jdolecek 	uint32_t next_hash;
    670  1.9.14.2  jdolecek 	int idx = info->h_levels_num - 1;
    671  1.9.14.2  jdolecek 	int levels = 0;
    672  1.9.14.2  jdolecek 
    673  1.9.14.2  jdolecek 	for (;;) {
    674  1.9.14.2  jdolecek 		level = &info->h_levels[idx];
    675  1.9.14.2  jdolecek 		level->h_entry++;
    676  1.9.14.2  jdolecek 		if (level->h_entry < level->h_entries +
    677  1.9.14.2  jdolecek 		    ext2fs_htree_get_count(level->h_entries))
    678  1.9.14.2  jdolecek 			break;
    679  1.9.14.2  jdolecek 		if (idx == 0)
    680  1.9.14.2  jdolecek 			return 0;
    681  1.9.14.2  jdolecek 		idx--;
    682  1.9.14.2  jdolecek 		levels++;
    683  1.9.14.2  jdolecek 	}
    684  1.9.14.2  jdolecek 
    685  1.9.14.2  jdolecek 	next_hash = ext2fs_htree_get_hash(level->h_entry);
    686  1.9.14.2  jdolecek 	if ((hash & 1) == 0) {
    687  1.9.14.2  jdolecek 		if (hash != (next_hash & ~1))
    688  1.9.14.2  jdolecek 			return 0;
    689  1.9.14.2  jdolecek 	}
    690  1.9.14.2  jdolecek 
    691  1.9.14.2  jdolecek 	while (levels > 0) {
    692  1.9.14.2  jdolecek 		levels--;
    693  1.9.14.2  jdolecek 		if (ext2fs_blkatoff(vp, ext2fs_htree_get_block(level->h_entry) *
    694  1.9.14.2  jdolecek 		    ip->i_e2fs->e2fs_bsize, NULL, &bp) != 0)
    695  1.9.14.2  jdolecek 			return 0;
    696  1.9.14.2  jdolecek 		level = &info->h_levels[idx + 1];
    697  1.9.14.2  jdolecek 		brelse(level->h_bp, 0);
    698  1.9.14.2  jdolecek 		level->h_bp = bp;
    699  1.9.14.2  jdolecek 		level->h_entry = level->h_entries =
    700  1.9.14.2  jdolecek 		    ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
    701  1.9.14.2  jdolecek 	}
    702  1.9.14.2  jdolecek 
    703  1.9.14.2  jdolecek 	return 1;
    704  1.9.14.2  jdolecek }
    705  1.9.14.2  jdolecek 
    706  1.9.14.2  jdolecek static int
    707  1.9.14.2  jdolecek ext2fs_htree_find_leaf(struct inode *ip, const char *name, int namelen,
    708  1.9.14.2  jdolecek     uint32_t *hash, uint8_t *hash_ver,
    709  1.9.14.2  jdolecek     struct ext2fs_htree_lookup_info *info)
    710  1.9.14.2  jdolecek {
    711  1.9.14.2  jdolecek 	struct vnode *vp;
    712  1.9.14.2  jdolecek 	struct ext2fs *fs;/* F, G, and H are MD4 functions */
    713  1.9.14.2  jdolecek 	struct m_ext2fs *m_fs;
    714  1.9.14.2  jdolecek 	struct buf *bp = NULL;
    715  1.9.14.2  jdolecek 	struct ext2fs_htree_root *rootp;
    716  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *entp, *start, *end, *middle, *found;
    717  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_level *level_info;
    718  1.9.14.2  jdolecek 	uint32_t hash_major = 0, hash_minor = 0;
    719  1.9.14.2  jdolecek 	uint32_t levels, cnt;
    720  1.9.14.2  jdolecek 	uint8_t hash_version;
    721  1.9.14.2  jdolecek 
    722  1.9.14.2  jdolecek 	if (name == NULL || info == NULL)
    723  1.9.14.2  jdolecek 		return -1;
    724  1.9.14.2  jdolecek 
    725  1.9.14.2  jdolecek 	vp = ITOV(ip);
    726  1.9.14.2  jdolecek 	fs = &(ip->i_e2fs->e2fs);
    727  1.9.14.2  jdolecek 	m_fs = ip->i_e2fs;
    728  1.9.14.2  jdolecek 
    729  1.9.14.2  jdolecek 	if (ext2fs_blkatoff(vp, 0, NULL, &bp) != 0)
    730  1.9.14.2  jdolecek 		return -1;
    731  1.9.14.2  jdolecek 
    732  1.9.14.2  jdolecek 	info->h_levels_num = 1;
    733  1.9.14.2  jdolecek 	info->h_levels[0].h_bp = bp;
    734  1.9.14.2  jdolecek 	rootp = (struct ext2fs_htree_root *)bp->b_data;
    735  1.9.14.2  jdolecek 	if (rootp->h_info.h_hash_version != EXT2_HTREE_LEGACY &&
    736  1.9.14.2  jdolecek 	    rootp->h_info.h_hash_version != EXT2_HTREE_HALF_MD4 &&
    737  1.9.14.2  jdolecek 	    rootp->h_info.h_hash_version != EXT2_HTREE_TEA)
    738  1.9.14.2  jdolecek 		goto error;
    739  1.9.14.2  jdolecek 
    740  1.9.14.2  jdolecek 	hash_version = rootp->h_info.h_hash_version;
    741  1.9.14.2  jdolecek 	if (hash_version <= EXT2_HTREE_TEA)
    742  1.9.14.2  jdolecek 		hash_version += m_fs->e2fs_uhash;
    743  1.9.14.2  jdolecek 	*hash_ver = hash_version;
    744  1.9.14.2  jdolecek 
    745  1.9.14.2  jdolecek 	ext2fs_htree_hash(name, namelen, fs->e3fs_hash_seed,
    746  1.9.14.2  jdolecek 	    hash_version, &hash_major, &hash_minor);
    747  1.9.14.2  jdolecek 	*hash = hash_major;
    748  1.9.14.2  jdolecek 
    749  1.9.14.2  jdolecek 	if ((levels = rootp->h_info.h_ind_levels) > 1)
    750  1.9.14.2  jdolecek 		goto error;
    751  1.9.14.2  jdolecek 
    752  1.9.14.2  jdolecek 	entp = (struct ext2fs_htree_entry *)(((char *)&rootp->h_info) +
    753  1.9.14.2  jdolecek 	    rootp->h_info.h_info_len);
    754  1.9.14.2  jdolecek 
    755  1.9.14.2  jdolecek 	if (ext2fs_htree_get_limit(entp) !=
    756  1.9.14.2  jdolecek 	    ext2fs_htree_root_limit(ip, rootp->h_info.h_info_len))
    757  1.9.14.2  jdolecek 		goto error;
    758  1.9.14.2  jdolecek 
    759  1.9.14.2  jdolecek 	for (;;) {
    760  1.9.14.2  jdolecek 		cnt = ext2fs_htree_get_count(entp);
    761  1.9.14.2  jdolecek 		if (cnt == 0 || cnt > ext2fs_htree_get_limit(entp))
    762  1.9.14.2  jdolecek 			goto error;
    763  1.9.14.2  jdolecek 
    764  1.9.14.2  jdolecek 		start = entp + 1;
    765  1.9.14.2  jdolecek 		end = entp + cnt - 1;
    766  1.9.14.2  jdolecek 		while (start <= end) {
    767  1.9.14.2  jdolecek 			middle = start + (end - start) / 2;
    768  1.9.14.2  jdolecek 			if (ext2fs_htree_get_hash(middle) > hash_major)
    769  1.9.14.2  jdolecek 				end = middle - 1;
    770  1.9.14.2  jdolecek 			else
    771  1.9.14.2  jdolecek 				start = middle + 1;
    772  1.9.14.2  jdolecek 		}
    773  1.9.14.2  jdolecek 		found = start - 1;
    774  1.9.14.2  jdolecek 
    775  1.9.14.2  jdolecek 		level_info = &(info->h_levels[info->h_levels_num - 1]);
    776  1.9.14.2  jdolecek 		level_info->h_bp = bp;
    777  1.9.14.2  jdolecek 		level_info->h_entries = entp;
    778  1.9.14.2  jdolecek 		level_info->h_entry = found;
    779  1.9.14.2  jdolecek 		if (levels == 0)
    780  1.9.14.2  jdolecek 			return 0;
    781  1.9.14.2  jdolecek 		levels--;
    782  1.9.14.2  jdolecek 		if (ext2fs_blkatoff(vp,
    783  1.9.14.2  jdolecek 		    ext2fs_htree_get_block(found) * m_fs->e2fs_bsize,
    784  1.9.14.2  jdolecek 		    NULL, &bp) != 0)
    785  1.9.14.2  jdolecek 			goto error;
    786  1.9.14.2  jdolecek 		entp = ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
    787  1.9.14.2  jdolecek 		info->h_levels_num++;
    788  1.9.14.2  jdolecek 		info->h_levels[info->h_levels_num - 1].h_bp = bp;
    789  1.9.14.2  jdolecek 	}
    790  1.9.14.2  jdolecek 
    791  1.9.14.2  jdolecek error:
    792  1.9.14.2  jdolecek 	ext2fs_htree_release(info);
    793  1.9.14.2  jdolecek 	return -1;
    794  1.9.14.2  jdolecek }
    795  1.9.14.2  jdolecek 
    796  1.9.14.2  jdolecek /*
    797  1.9.14.2  jdolecek  * Try to lookup a directory entry in HTree index
    798  1.9.14.2  jdolecek  */
    799  1.9.14.2  jdolecek int
    800  1.9.14.2  jdolecek ext2fs_htree_lookup(struct inode *ip, const char *name, int namelen,
    801  1.9.14.2  jdolecek     struct buf **bpp, int *entryoffp, doff_t *offp,
    802  1.9.14.2  jdolecek     doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ss)
    803  1.9.14.2  jdolecek {
    804  1.9.14.2  jdolecek 	struct vnode *vp;
    805  1.9.14.2  jdolecek 	struct ext2fs_htree_lookup_info info;
    806  1.9.14.2  jdolecek 	struct ext2fs_htree_entry *leaf_node;
    807  1.9.14.2  jdolecek 	struct m_ext2fs *m_fs;
    808  1.9.14.2  jdolecek 	struct buf *bp;
    809  1.9.14.2  jdolecek 	uint32_t blk;
    810  1.9.14.2  jdolecek 	uint32_t dirhash;
    811  1.9.14.2  jdolecek 	uint32_t bsize;
    812  1.9.14.2  jdolecek 	uint8_t hash_version;
    813  1.9.14.2  jdolecek 	int search_next;
    814  1.9.14.2  jdolecek 
    815  1.9.14.2  jdolecek 	m_fs = ip->i_e2fs;
    816  1.9.14.2  jdolecek 	bsize = m_fs->e2fs_bsize;
    817  1.9.14.2  jdolecek 	vp = ITOV(ip);
    818  1.9.14.2  jdolecek 
    819  1.9.14.2  jdolecek 	/* TODO: print error msg because we don't lookup '.' and '..' */
    820  1.9.14.2  jdolecek 
    821  1.9.14.2  jdolecek 	memset(&info, 0, sizeof(info));
    822  1.9.14.2  jdolecek 	if (ext2fs_htree_find_leaf(ip, name, namelen, &dirhash,
    823  1.9.14.2  jdolecek 	    &hash_version, &info)) {
    824  1.9.14.2  jdolecek 		return -1;
    825  1.9.14.2  jdolecek 	}
    826  1.9.14.2  jdolecek 
    827  1.9.14.2  jdolecek 	do {
    828  1.9.14.2  jdolecek 		leaf_node = info.h_levels[info.h_levels_num - 1].h_entry;
    829  1.9.14.2  jdolecek 		blk = ext2fs_htree_get_block(leaf_node);
    830  1.9.14.2  jdolecek 		if (ext2fs_blkatoff(vp, blk * bsize, NULL, &bp) != 0) {
    831  1.9.14.2  jdolecek 			ext2fs_htree_release(&info);
    832  1.9.14.2  jdolecek 			return -1;
    833  1.9.14.2  jdolecek 		}
    834  1.9.14.2  jdolecek 
    835  1.9.14.2  jdolecek 		*offp = blk * bsize;
    836  1.9.14.2  jdolecek 		*entryoffp = 0;
    837  1.9.14.2  jdolecek 		*prevoffp = blk * bsize;
    838  1.9.14.2  jdolecek 		*endusefulp = blk * bsize;
    839  1.9.14.2  jdolecek 
    840  1.9.14.2  jdolecek 		if (ss->slotstatus == NONE) {
    841  1.9.14.2  jdolecek 			ss->slotoffset = -1;
    842  1.9.14.2  jdolecek 			ss->slotfreespace = 0;
    843  1.9.14.2  jdolecek 		}
    844  1.9.14.2  jdolecek 
    845  1.9.14.2  jdolecek 		int found;
    846  1.9.14.2  jdolecek 		if (ext2fs_search_dirblock(ip, bp->b_data, &found,
    847  1.9.14.2  jdolecek 		    name, namelen, entryoffp, offp, prevoffp,
    848  1.9.14.2  jdolecek 		    endusefulp, ss) != 0) {
    849  1.9.14.2  jdolecek 			brelse(bp, 0);
    850  1.9.14.2  jdolecek 			ext2fs_htree_release(&info);
    851  1.9.14.2  jdolecek 			return -1;
    852  1.9.14.2  jdolecek 		}
    853  1.9.14.2  jdolecek 
    854  1.9.14.2  jdolecek 		if (found) {
    855  1.9.14.2  jdolecek 			*bpp = bp;
    856  1.9.14.2  jdolecek 			ext2fs_htree_release(&info);
    857  1.9.14.2  jdolecek 			return 0;
    858  1.9.14.2  jdolecek 		}
    859  1.9.14.2  jdolecek 
    860  1.9.14.2  jdolecek 		brelse(bp, 0);
    861  1.9.14.2  jdolecek 		search_next = ext2fs_htree_check_next(ip, dirhash, name, &info);
    862  1.9.14.2  jdolecek 	} while (search_next);
    863  1.9.14.2  jdolecek 
    864  1.9.14.2  jdolecek 	ext2fs_htree_release(&info);
    865  1.9.14.2  jdolecek 	return ENOENT;
    866  1.9.14.2  jdolecek }
    867