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_ipcluster(hammer2_xop_t *arg, int clindex) 71 { 72 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster; 73 hammer2_chain_t *chain; 74 int error; 75 76 chain = hammer2_inode_chain(xop->head.ip1, 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->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.ip1, 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_MAX, 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_MAX, 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.ip1, 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.name1; 176 name_len = xop->head.name1_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.ip1, 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.ip1, 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.name1; 264 name_len = xop->head.name1_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, 320 xop->head.mtid, xop->dopermanent); 321 } 322 } 323 324 /* 325 * If the entry is a hardlink pointer, resolve it. If this is the 326 * last link, delete it. We aren't the frontend so we can't adjust 327 * nlinks. 328 */ 329 if (chain) { 330 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 331 error = hammer2_chain_hardlink_find( 332 xop->head.ip1, 333 &parent, &chain, 334 0); 335 if (chain && 336 (int64_t)chain->data->ipdata.meta.nlinks <= 1) { 337 hammer2_chain_delete(parent, chain, 338 xop->head.mtid, 339 xop->dopermanent); 340 } 341 } 342 } 343 344 /* 345 * Chains passed to feed are expected to be locked shared. 346 */ 347 if (chain) { 348 hammer2_chain_unlock(chain); 349 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS | 350 HAMMER2_RESOLVE_SHARED); 351 } 352 353 /* 354 * We always return the hardlink target (the real inode) for 355 * further action. 356 */ 357 done: 358 hammer2_xop_feed(&xop->head, chain, clindex, error); 359 if (chain) 360 hammer2_chain_drop(chain); 361 if (parent) { 362 hammer2_chain_unlock(parent); 363 hammer2_chain_drop(parent); 364 } 365 } 366 367 /* 368 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename() 369 * 370 * Convert the target {dip,ip} to a hardlink target and replace 371 * the original namespace with a hardlink pointer. 372 */ 373 void 374 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex) 375 { 376 hammer2_xop_nlink_t *xop = &arg->xop_nlink; 377 hammer2_pfs_t *pmp; 378 hammer2_inode_data_t *wipdata; 379 hammer2_chain_t *parent; 380 hammer2_chain_t *chain; 381 hammer2_chain_t *tmp; 382 hammer2_inode_t *ip; 383 hammer2_key_t key_dummy; 384 int cache_index = -1; 385 int error; 386 387 /* 388 * We need the precise parent chain to issue the deletion. 389 */ 390 ip = xop->head.ip2; 391 pmp = ip->pmp; 392 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 393 if (parent) 394 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS); 395 if (parent == NULL) { 396 chain = NULL; 397 error = EIO; 398 goto done; 399 } 400 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 401 if (chain == NULL) { 402 error = EIO; 403 goto done; 404 } 405 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 406 407 /* 408 * Replace the namespace with a hardlink pointer if the chain being 409 * moved is not already a hardlink target. 410 */ 411 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) { 412 tmp = NULL; 413 error = hammer2_chain_create(&parent, &tmp, pmp, 414 chain->bref.key, 0, 415 HAMMER2_BREF_TYPE_INODE, 416 HAMMER2_INODE_BYTES, 417 xop->head.mtid, 0, 0); 418 if (error) 419 goto done; 420 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0); 421 wipdata = &tmp->data->ipdata; 422 bzero(wipdata, sizeof(*wipdata)); 423 wipdata->meta.name_key = chain->data->ipdata.meta.name_key; 424 wipdata->meta.name_len = chain->data->ipdata.meta.name_len; 425 bcopy(chain->data->ipdata.filename, wipdata->filename, 426 chain->data->ipdata.meta.name_len); 427 wipdata->meta.target_type = chain->data->ipdata.meta.type; 428 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; 429 wipdata->meta.inum = ip->meta.inum; 430 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; 431 wipdata->meta.nlinks = 1; 432 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; 433 434 hammer2_chain_unlock(tmp); 435 hammer2_chain_drop(tmp); 436 } 437 438 hammer2_chain_unlock(parent); 439 hammer2_chain_drop(parent); 440 441 /* 442 * Ok, back to the deleted chain. We must reconnect this chain 443 * as a hardlink target to cdir (ip3). 444 * 445 * WARNING! Frontend assumes filename length is 18 bytes. 446 */ 447 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 448 wipdata = &chain->data->ipdata; 449 ksnprintf(wipdata->filename, sizeof(wipdata->filename), 450 "0x%016jx", (intmax_t)ip->meta.inum); 451 wipdata->meta.name_len = strlen(wipdata->filename); 452 wipdata->meta.name_key = ip->meta.inum; 453 454 /* 455 * We must seek parent properly for the create. 456 */ 457 parent = hammer2_inode_chain(xop->head.ip3, clindex, 458 HAMMER2_RESOLVE_ALWAYS); 459 if (parent == NULL) { 460 error = EIO; 461 goto done; 462 } 463 tmp = hammer2_chain_lookup(&parent, &key_dummy, 464 ip->meta.inum, ip->meta.inum, 465 &cache_index, 0); 466 if (tmp) { 467 hammer2_chain_unlock(tmp); 468 hammer2_chain_drop(tmp); 469 error = EEXIST; 470 goto done; 471 } 472 error = hammer2_chain_create(&parent, &chain, pmp, 473 wipdata->meta.name_key, 0, 474 HAMMER2_BREF_TYPE_INODE, 475 HAMMER2_INODE_BYTES, 476 xop->head.mtid, 0, 0); 477 /* 478 * To avoid having to scan the collision space we can simply 479 * reuse the inode's original name_key. But ip->meta.name_key 480 * may have already been updated by the front-end, so use xop->lhc. 481 * 482 * (frontend is responsible for fixing up ip->pip). 483 */ 484 done: 485 hammer2_xop_feed(&xop->head, NULL, clindex, error); 486 if (parent) { 487 hammer2_chain_unlock(parent); 488 hammer2_chain_drop(parent); 489 } 490 if (chain) { 491 hammer2_chain_unlock(chain); 492 hammer2_chain_drop(chain); 493 } 494 } 495 496 /* 497 * Backend for hammer2_vop_nrename() 498 * 499 * This handles the final step of renaming, either renaming the 500 * actual inode or renaming the hardlink pointer. 501 */ 502 void 503 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex) 504 { 505 hammer2_xop_nrename_t *xop = &arg->xop_nrename; 506 hammer2_pfs_t *pmp; 507 hammer2_chain_t *parent; 508 hammer2_chain_t *chain; 509 hammer2_chain_t *tmp; 510 hammer2_inode_t *ip; 511 hammer2_key_t key_dummy; 512 int cache_index = -1; 513 int error; 514 515 /* 516 * We need the precise parent chain to issue the deletion. 517 * 518 * If this is not a hardlink target we can act on the inode, 519 * otherwise we have to locate the hardlink pointer. 520 */ 521 ip = xop->head.ip2; 522 pmp = ip->pmp; 523 chain = NULL; 524 525 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { 526 /* 527 * Find ip's direct parent chain. 528 */ 529 parent = hammer2_inode_chain(ip, clindex, 530 HAMMER2_RESOLVE_ALWAYS); 531 if (parent) 532 hammer2_chain_getparent(&parent, 533 HAMMER2_RESOLVE_ALWAYS); 534 if (parent == NULL) { 535 error = EIO; 536 goto done; 537 } 538 chain = hammer2_inode_chain(ip, clindex, 539 HAMMER2_RESOLVE_ALWAYS); 540 if (chain == NULL) { 541 error = EIO; 542 goto done; 543 } 544 } else { 545 /* 546 * head.ip1 is fdip, do a namespace search. 547 */ 548 const hammer2_inode_data_t *ripdata; 549 hammer2_key_t lhc; 550 hammer2_key_t key_next; 551 const char *name; 552 size_t name_len; 553 554 parent = hammer2_inode_chain(xop->head.ip1, clindex, 555 HAMMER2_RESOLVE_ALWAYS | 556 HAMMER2_RESOLVE_SHARED); 557 if (parent == NULL) { 558 kprintf("xop_nrename: NULL parent\n"); 559 error = EIO; 560 goto done; 561 } 562 name = xop->head.name1; 563 name_len = xop->head.name1_len; 564 565 /* 566 * Lookup the directory entry 567 */ 568 lhc = hammer2_dirhash(name, name_len); 569 chain = hammer2_chain_lookup(&parent, &key_next, 570 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 571 &cache_index, 572 HAMMER2_LOOKUP_ALWAYS); 573 while (chain) { 574 ripdata = &chain->data->ipdata; 575 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 576 ripdata->meta.name_len == name_len && 577 bcmp(ripdata->filename, name, name_len) == 0) { 578 break; 579 } 580 chain = hammer2_chain_next(&parent, chain, &key_next, 581 key_next, 582 lhc + HAMMER2_DIRHASH_LOMASK, 583 &cache_index, 584 HAMMER2_LOOKUP_ALWAYS); 585 } 586 } 587 588 /* 589 * Delete it, then create it in the new namespace. 590 */ 591 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 592 hammer2_chain_unlock(parent); 593 hammer2_chain_drop(parent); 594 parent = NULL; /* safety */ 595 596 597 /* 598 * Ok, back to the deleted chain. We must reconnect this chain 599 * to tdir (ip3). The chain (a real inode or a hardlink pointer) 600 * is not otherwise modified. 601 * 602 * Frontend is expected to replicate the same inode meta data 603 * modifications. 604 * 605 * NOTE! This chain may not represent the actual inode, it 606 * can be a hardlink pointer. 607 * 608 * XXX in-inode parent directory specification? 609 */ 610 if (chain->data->ipdata.meta.name_key != xop->lhc || 611 xop->head.name1_len != xop->head.name2_len || 612 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) { 613 hammer2_inode_data_t *wipdata; 614 615 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 616 wipdata = &chain->data->ipdata; 617 618 bzero(wipdata->filename, sizeof(wipdata->filename)); 619 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); 620 wipdata->meta.name_key = xop->lhc; 621 wipdata->meta.name_len = xop->head.name2_len; 622 } 623 624 /* 625 * We must seek parent properly for the create. 626 */ 627 parent = hammer2_inode_chain(xop->head.ip3, clindex, 628 HAMMER2_RESOLVE_ALWAYS); 629 if (parent == NULL) { 630 error = EIO; 631 goto done; 632 } 633 tmp = hammer2_chain_lookup(&parent, &key_dummy, 634 xop->lhc, xop->lhc, 635 &cache_index, 0); 636 if (tmp) { 637 hammer2_chain_unlock(tmp); 638 hammer2_chain_drop(tmp); 639 error = EEXIST; 640 goto done; 641 } 642 643 error = hammer2_chain_create(&parent, &chain, pmp, 644 xop->lhc, 0, 645 HAMMER2_BREF_TYPE_INODE, 646 HAMMER2_INODE_BYTES, 647 xop->head.mtid, 0, 0); 648 /* 649 * (frontend is responsible for fixing up ip->pip). 650 */ 651 done: 652 hammer2_xop_feed(&xop->head, NULL, clindex, error); 653 if (parent) { 654 hammer2_chain_unlock(parent); 655 hammer2_chain_drop(parent); 656 } 657 if (chain) { 658 hammer2_chain_unlock(chain); 659 hammer2_chain_drop(chain); 660 } 661 } 662 663 /* 664 * Directory collision resolver scan helper (backend, threaded). 665 * 666 * Used by the inode create code to locate an unused lhc. 667 */ 668 void 669 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex) 670 { 671 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 672 hammer2_chain_t *parent; 673 hammer2_chain_t *chain; 674 hammer2_key_t key_next; 675 int cache_index = -1; /* XXX */ 676 int error = 0; 677 678 parent = hammer2_inode_chain(xop->head.ip1, clindex, 679 HAMMER2_RESOLVE_ALWAYS | 680 HAMMER2_RESOLVE_SHARED); 681 if (parent == NULL) { 682 kprintf("xop_nresolve: NULL parent\n"); 683 chain = NULL; 684 error = EIO; 685 goto done; 686 } 687 688 /* 689 * Lookup all possibly conflicting directory entries, the feed 690 * inherits the chain's lock so do not unlock it on the iteration. 691 */ 692 chain = hammer2_chain_lookup(&parent, &key_next, 693 xop->lhc, 694 xop->lhc + HAMMER2_DIRHASH_LOMASK, 695 &cache_index, 696 HAMMER2_LOOKUP_ALWAYS | 697 HAMMER2_LOOKUP_SHARED); 698 while (chain) { 699 error = hammer2_xop_feed(&xop->head, chain, clindex, 700 chain->error); 701 if (error) { 702 hammer2_chain_drop(chain); 703 chain = NULL; /* safety */ 704 break; 705 } 706 chain = hammer2_chain_next(&parent, chain, &key_next, 707 key_next, 708 xop->lhc + HAMMER2_DIRHASH_LOMASK, 709 &cache_index, 710 HAMMER2_LOOKUP_ALWAYS | 711 HAMMER2_LOOKUP_SHARED | 712 HAMMER2_LOOKUP_NOUNLOCK); 713 } 714 done: 715 hammer2_xop_feed(&xop->head, NULL, clindex, error); 716 if (parent) { 717 hammer2_chain_unlock(parent); 718 hammer2_chain_drop(parent); 719 } 720 } 721 722 /* 723 * Generic lookup of a specific key. 724 * 725 * Used by the inode hidden directory code to find the hidden directory. 726 */ 727 void 728 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex) 729 { 730 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 731 hammer2_chain_t *parent; 732 hammer2_chain_t *chain; 733 hammer2_key_t key_next; 734 int cache_index = -1; /* XXX */ 735 int error = 0; 736 737 parent = hammer2_inode_chain(xop->head.ip1, clindex, 738 HAMMER2_RESOLVE_ALWAYS | 739 HAMMER2_RESOLVE_SHARED); 740 chain = NULL; 741 if (parent == NULL) { 742 error = EIO; 743 goto done; 744 } 745 746 /* 747 * Lookup all possibly conflicting directory entries, the feed 748 * inherits the chain's lock so do not unlock it on the iteration. 749 */ 750 chain = hammer2_chain_lookup(&parent, &key_next, 751 xop->lhc, xop->lhc, 752 &cache_index, 753 HAMMER2_LOOKUP_ALWAYS | 754 HAMMER2_LOOKUP_SHARED); 755 if (chain) 756 hammer2_xop_feed(&xop->head, chain, clindex, chain->error); 757 else 758 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT); 759 760 done: 761 if (chain) { 762 /* leave lock intact for feed */ 763 hammer2_chain_drop(chain); 764 } 765 if (parent) { 766 hammer2_chain_unlock(parent); 767 hammer2_chain_drop(parent); 768 } 769 } 770 771 /* 772 * Generic scan 773 */ 774 void 775 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex) 776 { 777 hammer2_xop_scanall_t *xop = &arg->xop_scanall; 778 hammer2_chain_t *parent; 779 hammer2_chain_t *chain; 780 hammer2_key_t key_next; 781 int cache_index = -1; 782 int error = 0; 783 784 /* 785 * The inode's chain is the iterator. If we cannot acquire it our 786 * contribution ends here. 787 */ 788 parent = hammer2_inode_chain(xop->head.ip1, clindex, 789 HAMMER2_RESOLVE_ALWAYS | 790 HAMMER2_RESOLVE_SHARED); 791 if (parent == NULL) { 792 kprintf("xop_readdir: NULL parent\n"); 793 goto done; 794 } 795 796 /* 797 * Generic scan of exact records. Note that indirect blocks are 798 * automatically recursed and will not be returned. 799 */ 800 chain = hammer2_chain_lookup(&parent, &key_next, 801 xop->key_beg, xop->key_end, 802 &cache_index, HAMMER2_LOOKUP_SHARED | 803 HAMMER2_LOOKUP_NODIRECT); 804 while (chain) { 805 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 806 if (error) 807 break; 808 chain = hammer2_chain_next(&parent, chain, &key_next, 809 key_next, xop->key_end, 810 &cache_index, 811 HAMMER2_LOOKUP_SHARED | 812 HAMMER2_LOOKUP_NODIRECT | 813 HAMMER2_LOOKUP_NOUNLOCK); 814 } 815 if (chain) 816 hammer2_chain_drop(chain); 817 hammer2_chain_unlock(parent); 818 hammer2_chain_drop(parent); 819 done: 820 hammer2_xop_feed(&xop->head, NULL, clindex, error); 821 } 822