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