1 /* 2 * Copyright (c) 2011-2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * by Daniel Flores (GSOC 2013 - mentored by Matthew Dillon, compression) 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 /* 37 * Per-node backend for kernel filesystem interface. 38 * 39 * This executes a VOP concurrently on multiple nodes, each node via its own 40 * thread, and competes to advance the original request. The original 41 * request is retired the moment all requirements are met, even if the 42 * operation is still in-progress on some nodes. 43 */ 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/fcntl.h> 48 #include <sys/buf.h> 49 #include <sys/proc.h> 50 #include <sys/namei.h> 51 #include <sys/mount.h> 52 #include <sys/vnode.h> 53 #include <sys/mountctl.h> 54 #include <sys/dirent.h> 55 #include <sys/uio.h> 56 #include <sys/objcache.h> 57 #include <sys/event.h> 58 #include <sys/file.h> 59 #include <vfs/fifofs/fifo.h> 60 61 #include "hammer2.h" 62 63 /* 64 * Backend for hammer2_vfs_root() 65 * 66 * This is called when a newly mounted PFS has not yet synchronized 67 * to the inode_tid and modify_tid. 68 */ 69 void 70 hammer2_xop_vfsroot(hammer2_xop_t *arg, int clindex) 71 { 72 hammer2_xop_vfsroot_t *xop = &arg->xop_vfsroot; 73 hammer2_chain_t *chain; 74 int error; 75 76 chain = hammer2_inode_chain(xop->head.ip, clindex, 77 HAMMER2_RESOLVE_ALWAYS | 78 HAMMER2_RESOLVE_SHARED); 79 if (chain) 80 error = chain->error; 81 else 82 error = EIO; 83 84 hammer2_xop_feed(&xop->head, chain, clindex, error); 85 if (chain) 86 hammer2_chain_drop(chain); 87 } 88 89 /* 90 * Backend for hammer2_vop_readdir() 91 */ 92 void 93 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex) 94 { 95 hammer2_xop_readdir_t *xop = &arg->xop_readdir; 96 hammer2_chain_t *parent; 97 hammer2_chain_t *chain; 98 hammer2_key_t key_next; 99 hammer2_key_t lkey; 100 int cache_index = -1; 101 int error = 0; 102 103 lkey = xop->head.lkey; 104 if (hammer2_debug & 0x0020) 105 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey); 106 107 /* 108 * The inode's chain is the iterator. If we cannot acquire it our 109 * contribution ends here. 110 */ 111 parent = hammer2_inode_chain(xop->head.ip, clindex, 112 HAMMER2_RESOLVE_ALWAYS | 113 HAMMER2_RESOLVE_SHARED); 114 if (parent == NULL) { 115 kprintf("xop_readdir: NULL parent\n"); 116 goto done; 117 } 118 119 /* 120 * Directory scan [re]start and loop, the feed inherits the chain's 121 * lock so do not unlock it on the iteration. 122 */ 123 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey, 124 &cache_index, HAMMER2_LOOKUP_SHARED); 125 if (chain == NULL) { 126 chain = hammer2_chain_lookup(&parent, &key_next, 127 lkey, (hammer2_key_t)-1, 128 &cache_index, 129 HAMMER2_LOOKUP_SHARED); 130 } 131 while (chain) { 132 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 133 if (error) 134 break; 135 chain = hammer2_chain_next(&parent, chain, &key_next, 136 key_next, (hammer2_key_t)-1, 137 &cache_index, 138 HAMMER2_LOOKUP_SHARED | 139 HAMMER2_LOOKUP_NOUNLOCK); 140 } 141 if (chain) 142 hammer2_chain_drop(chain); 143 hammer2_chain_unlock(parent); 144 hammer2_chain_drop(parent); 145 done: 146 hammer2_xop_feed(&xop->head, NULL, clindex, error); 147 } 148 149 /* 150 * Backend for hammer2_vop_nresolve() 151 */ 152 void 153 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex) 154 { 155 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve; 156 hammer2_chain_t *parent; 157 hammer2_chain_t *chain; 158 const hammer2_inode_data_t *ripdata; 159 const char *name; 160 size_t name_len; 161 hammer2_key_t key_next; 162 hammer2_key_t lhc; 163 int cache_index = -1; /* XXX */ 164 int error; 165 166 parent = hammer2_inode_chain(xop->head.ip, clindex, 167 HAMMER2_RESOLVE_ALWAYS | 168 HAMMER2_RESOLVE_SHARED); 169 if (parent == NULL) { 170 kprintf("xop_nresolve: NULL parent\n"); 171 chain = NULL; 172 error = EIO; 173 goto done; 174 } 175 name = xop->head.name; 176 name_len = xop->head.name_len; 177 178 /* 179 * Lookup the directory entry 180 */ 181 lhc = hammer2_dirhash(name, name_len); 182 chain = hammer2_chain_lookup(&parent, &key_next, 183 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 184 &cache_index, 185 HAMMER2_LOOKUP_ALWAYS | 186 HAMMER2_LOOKUP_SHARED); 187 while (chain) { 188 ripdata = &chain->data->ipdata; 189 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 190 ripdata->meta.name_len == name_len && 191 bcmp(ripdata->filename, name, name_len) == 0) { 192 break; 193 } 194 chain = hammer2_chain_next(&parent, chain, &key_next, 195 key_next, 196 lhc + HAMMER2_DIRHASH_LOMASK, 197 &cache_index, 198 HAMMER2_LOOKUP_ALWAYS | 199 HAMMER2_LOOKUP_SHARED); 200 } 201 202 /* 203 * If the entry is a hardlink pointer, resolve it. 204 */ 205 error = 0; 206 if (chain) { 207 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 208 error = hammer2_chain_hardlink_find( 209 xop->head.ip, 210 &parent, &chain, 211 HAMMER2_RESOLVE_SHARED); 212 } 213 } 214 done: 215 error = hammer2_xop_feed(&xop->head, chain, clindex, error); 216 if (chain) { 217 /* leave lock intact for feed */ 218 hammer2_chain_drop(chain); 219 } 220 if (parent) { 221 hammer2_chain_unlock(parent); 222 hammer2_chain_drop(parent); 223 } 224 } 225 226 /* 227 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper 228 * for hammer2_vop_nrename(). 229 * 230 * This function does locates and removes the directory entry. If the 231 * entry is a hardlink pointer, this function will also remove the 232 * hardlink target if the target's nlinks is 1. 233 * 234 * The frontend is responsible for moving open inodes to the hidden directory 235 * and for decrementing nlinks. 236 */ 237 void 238 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex) 239 { 240 hammer2_xop_unlink_t *xop = &arg->xop_unlink; 241 hammer2_chain_t *parent; 242 hammer2_chain_t *chain; 243 const hammer2_inode_data_t *ripdata; 244 const char *name; 245 size_t name_len; 246 uint8_t type; 247 hammer2_key_t key_next; 248 hammer2_key_t lhc; 249 int cache_index = -1; /* XXX */ 250 int error; 251 252 /* 253 * Requires exclusive lock 254 */ 255 parent = hammer2_inode_chain(xop->head.ip, clindex, 256 HAMMER2_RESOLVE_ALWAYS); 257 if (parent == NULL) { 258 kprintf("xop_nresolve: NULL parent\n"); 259 chain = NULL; 260 error = EIO; 261 goto done; 262 } 263 name = xop->head.name; 264 name_len = xop->head.name_len; 265 266 /* 267 * Lookup the directory entry 268 */ 269 lhc = hammer2_dirhash(name, name_len); 270 chain = hammer2_chain_lookup(&parent, &key_next, 271 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 272 &cache_index, 273 HAMMER2_LOOKUP_ALWAYS); 274 while (chain) { 275 ripdata = &chain->data->ipdata; 276 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 277 ripdata->meta.name_len == name_len && 278 bcmp(ripdata->filename, name, name_len) == 0) { 279 break; 280 } 281 chain = hammer2_chain_next(&parent, chain, &key_next, 282 key_next, 283 lhc + HAMMER2_DIRHASH_LOMASK, 284 &cache_index, 285 HAMMER2_LOOKUP_ALWAYS); 286 } 287 288 /* 289 * If the directory entry is a HARDLINK pointer then obtain the 290 * underlying file type for the directory typing tests and delete 291 * the HARDLINK pointer chain permanently. The frontend is left 292 * responsible for handling nlinks on and deleting the actual inode. 293 * 294 * If the directory entry is the actual inode then use its type 295 * for the directory typing tests and delete the chain, permanency 296 * depends on whether the inode is open or not. 297 * 298 * Check directory typing and delete the entry. Note that 299 * nlinks adjustments are made on the real inode by the frontend, 300 * not here. 301 */ 302 error = 0; 303 if (chain) { 304 int dopermanent = xop->dopermanent; 305 306 type = chain->data->ipdata.meta.type; 307 if (type == HAMMER2_OBJTYPE_HARDLINK) { 308 type = chain->data->ipdata.meta.target_type; 309 dopermanent |= HAMMER2_DELETE_PERMANENT; 310 } 311 if (type == HAMMER2_OBJTYPE_DIRECTORY && 312 xop->isdir == 0) { 313 error = ENOTDIR; 314 } else 315 if (type != HAMMER2_OBJTYPE_DIRECTORY && 316 xop->isdir >= 1) { 317 error = EISDIR; 318 } else { 319 hammer2_chain_delete(parent, chain, xop->dopermanent); 320 } 321 } 322 323 /* 324 * If the entry is a hardlink pointer, resolve it. If this is the 325 * last link, delete it. We aren't the frontend so we can't adjust 326 * nlinks. 327 */ 328 if (chain) { 329 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 330 error = hammer2_chain_hardlink_find( 331 xop->head.ip, 332 &parent, &chain, 333 0); 334 if (chain && 335 (int64_t)chain->data->ipdata.meta.nlinks <= 1) { 336 hammer2_chain_delete(parent, chain, 337 xop->dopermanent); 338 } 339 } 340 } 341 342 /* 343 * Chains passed to feed are expected to be locked shared. 344 */ 345 if (chain) { 346 hammer2_chain_unlock(chain); 347 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS | 348 HAMMER2_RESOLVE_SHARED); 349 } 350 351 /* 352 * We always return the hardlink target (the real inode) for 353 * further action. 354 */ 355 done: 356 hammer2_xop_feed(&xop->head, chain, clindex, error); 357 if (chain) 358 hammer2_chain_drop(chain); 359 if (parent) { 360 hammer2_chain_unlock(parent); 361 hammer2_chain_drop(parent); 362 } 363 } 364 365 /* 366 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename() 367 * 368 * Convert the target {dip,ip} to a hardlink target and replace 369 * the original namespace with a hardlink pointer. 370 */ 371 void 372 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex) 373 { 374 hammer2_xop_nlink_t *xop = &arg->xop_nlink; 375 hammer2_pfs_t *pmp; 376 hammer2_inode_data_t *wipdata; 377 hammer2_chain_t *parent; 378 hammer2_chain_t *chain; 379 hammer2_chain_t *tmp; 380 hammer2_inode_t *ip; 381 hammer2_key_t key_dummy; 382 int cache_index = -1; 383 int error; 384 385 /* 386 * We need the precise parent chain to issue the deletion. 387 */ 388 ip = xop->head.ip2; 389 pmp = ip->pmp; 390 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 391 if (parent) 392 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS); 393 if (parent == NULL) { 394 chain = NULL; 395 error = EIO; 396 goto done; 397 } 398 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 399 if (chain == NULL) { 400 error = EIO; 401 goto done; 402 } 403 hammer2_chain_delete(parent, chain, 0); 404 405 /* 406 * Replace the namespace with a hardlink pointer if the chain being 407 * moved is not already a hardlink target. 408 */ 409 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) { 410 tmp = NULL; 411 error = hammer2_chain_create(&parent, &tmp, pmp, 412 chain->bref.key, 0, 413 HAMMER2_BREF_TYPE_INODE, 414 HAMMER2_INODE_BYTES, 415 0); 416 if (error) 417 goto done; 418 hammer2_chain_modify(tmp, 0); 419 wipdata = &tmp->data->ipdata; 420 bzero(wipdata, sizeof(*wipdata)); 421 wipdata->meta.name_key = chain->data->ipdata.meta.name_key; 422 wipdata->meta.name_len = chain->data->ipdata.meta.name_len; 423 bcopy(chain->data->ipdata.filename, wipdata->filename, 424 chain->data->ipdata.meta.name_len); 425 wipdata->meta.target_type = chain->data->ipdata.meta.type; 426 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; 427 wipdata->meta.inum = ip->meta.inum; 428 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; 429 wipdata->meta.nlinks = 1; 430 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; 431 432 hammer2_chain_unlock(tmp); 433 hammer2_chain_drop(tmp); 434 } 435 436 hammer2_chain_unlock(parent); 437 hammer2_chain_drop(parent); 438 439 /* 440 * Ok, back to the deleted chain. We must reconnect this chain 441 * as a hardlink target to cdir (ip3). 442 * 443 * WARNING! Frontend assumes filename length is 18 bytes. 444 */ 445 hammer2_chain_modify(chain, 0); 446 wipdata = &chain->data->ipdata; 447 ksnprintf(wipdata->filename, sizeof(wipdata->filename), 448 "0x%016jx", (intmax_t)ip->meta.inum); 449 wipdata->meta.name_len = strlen(wipdata->filename); 450 wipdata->meta.name_key = ip->meta.inum; 451 452 /* 453 * We must seek parent properly for the create. 454 */ 455 parent = hammer2_inode_chain(xop->head.ip3, clindex, 456 HAMMER2_RESOLVE_ALWAYS); 457 if (parent == NULL) { 458 error = EIO; 459 goto done; 460 } 461 tmp = hammer2_chain_lookup(&parent, &key_dummy, 462 ip->meta.inum, ip->meta.inum, 463 &cache_index, 0); 464 if (tmp) { 465 hammer2_chain_unlock(tmp); 466 hammer2_chain_drop(tmp); 467 error = EEXIST; 468 goto done; 469 } 470 error = hammer2_chain_create(&parent, &chain, pmp, 471 wipdata->meta.name_key, 0, 472 HAMMER2_BREF_TYPE_INODE, 473 HAMMER2_INODE_BYTES, 474 0); 475 /* 476 * To avoid having to scan the collision space we can simply 477 * reuse the inode's original name_key. But ip->meta.name_key 478 * may have already been updated by the front-end, so use xop->lhc. 479 * 480 * (frontend is responsible for fixing up ip->pip). 481 */ 482 done: 483 hammer2_xop_feed(&xop->head, NULL, clindex, error); 484 if (parent) { 485 hammer2_chain_unlock(parent); 486 hammer2_chain_drop(parent); 487 } 488 if (chain) { 489 hammer2_chain_unlock(chain); 490 hammer2_chain_drop(chain); 491 } 492 } 493 494 /* 495 * Backend for hammer2_vop_nrename() 496 * 497 * This handles the final step of renaming, either renaming the 498 * actual inode or renaming the hardlink pointer. 499 */ 500 void 501 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex) 502 { 503 hammer2_xop_nrename_t *xop = &arg->xop_nrename; 504 hammer2_pfs_t *pmp; 505 hammer2_chain_t *parent; 506 hammer2_chain_t *chain; 507 hammer2_chain_t *tmp; 508 hammer2_inode_t *ip; 509 hammer2_key_t key_dummy; 510 int cache_index = -1; 511 int error; 512 513 /* 514 * We need the precise parent chain to issue the deletion. 515 * 516 * If this is not a hardlink target we can act on the inode, 517 * otherwise we have to locate the hardlink pointer. 518 */ 519 ip = xop->head.ip2; 520 pmp = ip->pmp; 521 chain = NULL; 522 523 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { 524 /* 525 * Find ip's direct parent chain. 526 */ 527 parent = hammer2_inode_chain(ip, clindex, 528 HAMMER2_RESOLVE_ALWAYS); 529 if (parent) 530 hammer2_chain_getparent(&parent, 531 HAMMER2_RESOLVE_ALWAYS); 532 if (parent == NULL) { 533 error = EIO; 534 goto done; 535 } 536 chain = hammer2_inode_chain(ip, clindex, 537 HAMMER2_RESOLVE_ALWAYS); 538 if (chain == NULL) { 539 error = EIO; 540 goto done; 541 } 542 } else { 543 /* 544 * head.ip is fdip, do a namespace search. 545 */ 546 const hammer2_inode_data_t *ripdata; 547 hammer2_key_t lhc; 548 hammer2_key_t key_next; 549 const char *name; 550 size_t name_len; 551 552 parent = hammer2_inode_chain(xop->head.ip, clindex, 553 HAMMER2_RESOLVE_ALWAYS | 554 HAMMER2_RESOLVE_SHARED); 555 if (parent == NULL) { 556 kprintf("xop_nrename: NULL parent\n"); 557 error = EIO; 558 goto done; 559 } 560 name = xop->head.name; 561 name_len = xop->head.name_len; 562 563 /* 564 * Lookup the directory entry 565 */ 566 lhc = hammer2_dirhash(name, name_len); 567 chain = hammer2_chain_lookup(&parent, &key_next, 568 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 569 &cache_index, 570 HAMMER2_LOOKUP_ALWAYS); 571 while (chain) { 572 ripdata = &chain->data->ipdata; 573 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 574 ripdata->meta.name_len == name_len && 575 bcmp(ripdata->filename, name, name_len) == 0) { 576 break; 577 } 578 chain = hammer2_chain_next(&parent, chain, &key_next, 579 key_next, 580 lhc + HAMMER2_DIRHASH_LOMASK, 581 &cache_index, 582 HAMMER2_LOOKUP_ALWAYS); 583 } 584 } 585 586 /* 587 * Delete it, then create it in the new namespace. 588 */ 589 hammer2_chain_delete(parent, chain, 0); 590 hammer2_chain_unlock(parent); 591 hammer2_chain_drop(parent); 592 parent = NULL; /* safety */ 593 594 595 /* 596 * Ok, back to the deleted chain. We must reconnect this chain 597 * to tdir (ip3). The chain (a real inode or a hardlink pointer) 598 * is not otherwise modified. 599 * 600 * Frontend is expected to replicate the same inode meta data 601 * modifications. 602 * 603 * NOTE! This chain may not represent the actual inode, it 604 * can be a hardlink pointer. 605 * 606 * XXX in-inode parent directory specification? 607 */ 608 if (chain->data->ipdata.meta.name_key != xop->lhc || 609 xop->head.name_len != xop->head.name2_len || 610 bcmp(xop->head.name, xop->head.name2, xop->head.name_len) != 0) { 611 hammer2_inode_data_t *wipdata; 612 613 hammer2_chain_modify(chain, 0); 614 wipdata = &chain->data->ipdata; 615 616 bzero(wipdata->filename, sizeof(wipdata->filename)); 617 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); 618 wipdata->meta.name_key = xop->lhc; 619 wipdata->meta.name_len = xop->head.name2_len; 620 } 621 622 /* 623 * We must seek parent properly for the create. 624 */ 625 parent = hammer2_inode_chain(xop->head.ip3, clindex, 626 HAMMER2_RESOLVE_ALWAYS); 627 if (parent == NULL) { 628 error = EIO; 629 goto done; 630 } 631 tmp = hammer2_chain_lookup(&parent, &key_dummy, 632 xop->lhc, xop->lhc, 633 &cache_index, 0); 634 if (tmp) { 635 hammer2_chain_unlock(tmp); 636 hammer2_chain_drop(tmp); 637 error = EEXIST; 638 goto done; 639 } 640 641 error = hammer2_chain_create(&parent, &chain, pmp, 642 xop->lhc, 0, 643 HAMMER2_BREF_TYPE_INODE, 644 HAMMER2_INODE_BYTES, 645 0); 646 /* 647 * (frontend is responsible for fixing up ip->pip). 648 */ 649 done: 650 hammer2_xop_feed(&xop->head, NULL, clindex, error); 651 if (parent) { 652 hammer2_chain_unlock(parent); 653 hammer2_chain_drop(parent); 654 } 655 if (chain) { 656 hammer2_chain_unlock(chain); 657 hammer2_chain_drop(chain); 658 } 659 } 660 661 /* 662 * Directory collision resolver scan helper (backend, threaded). 663 * 664 * Used by the inode create code to locate an unused lhc. 665 */ 666 void 667 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex) 668 { 669 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 670 hammer2_chain_t *parent; 671 hammer2_chain_t *chain; 672 hammer2_key_t key_next; 673 int cache_index = -1; /* XXX */ 674 int error = 0; 675 676 parent = hammer2_inode_chain(xop->head.ip, clindex, 677 HAMMER2_RESOLVE_ALWAYS | 678 HAMMER2_RESOLVE_SHARED); 679 if (parent == NULL) { 680 kprintf("xop_nresolve: NULL parent\n"); 681 chain = NULL; 682 error = EIO; 683 goto done; 684 } 685 686 /* 687 * Lookup all possibly conflicting directory entries, the feed 688 * inherits the chain's lock so do not unlock it on the iteration. 689 */ 690 chain = hammer2_chain_lookup(&parent, &key_next, 691 xop->lhc, 692 xop->lhc + HAMMER2_DIRHASH_LOMASK, 693 &cache_index, 694 HAMMER2_LOOKUP_ALWAYS | 695 HAMMER2_LOOKUP_SHARED); 696 while (chain) { 697 error = hammer2_xop_feed(&xop->head, chain, clindex, 698 chain->error); 699 if (error) { 700 hammer2_chain_drop(chain); 701 chain = NULL; /* safety */ 702 break; 703 } 704 chain = hammer2_chain_next(&parent, chain, &key_next, 705 key_next, 706 xop->lhc + HAMMER2_DIRHASH_LOMASK, 707 &cache_index, 708 HAMMER2_LOOKUP_ALWAYS | 709 HAMMER2_LOOKUP_SHARED | 710 HAMMER2_LOOKUP_NOUNLOCK); 711 } 712 done: 713 hammer2_xop_feed(&xop->head, NULL, clindex, error); 714 if (parent) { 715 hammer2_chain_unlock(parent); 716 hammer2_chain_drop(parent); 717 } 718 } 719 720 /* 721 * Lookup a specific key. 722 * 723 * Used by the inode hidden directory code to find the hidden directory. 724 */ 725 void 726 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex) 727 { 728 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 729 hammer2_chain_t *parent; 730 hammer2_chain_t *chain; 731 hammer2_key_t key_next; 732 int cache_index = -1; /* XXX */ 733 int error = 0; 734 735 parent = hammer2_inode_chain(xop->head.ip, clindex, 736 HAMMER2_RESOLVE_ALWAYS | 737 HAMMER2_RESOLVE_SHARED); 738 chain = NULL; 739 if (parent == NULL) { 740 error = EIO; 741 goto done; 742 } 743 744 /* 745 * Lookup all possibly conflicting directory entries, the feed 746 * inherits the chain's lock so do not unlock it on the iteration. 747 */ 748 chain = hammer2_chain_lookup(&parent, &key_next, 749 xop->lhc, xop->lhc, 750 &cache_index, 751 HAMMER2_LOOKUP_ALWAYS | 752 HAMMER2_LOOKUP_SHARED); 753 if (chain) 754 hammer2_xop_feed(&xop->head, chain, clindex, chain->error); 755 else 756 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT); 757 758 done: 759 if (chain) { 760 /* leave lock intact for feed */ 761 hammer2_chain_drop(chain); 762 } 763 if (parent) { 764 hammer2_chain_unlock(parent); 765 hammer2_chain_drop(parent); 766 } 767 } 768