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 * Determine if the specified directory is empty. Returns 0 on success. 65 */ 66 static 67 int 68 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex) 69 { 70 hammer2_chain_t *parent; 71 hammer2_chain_t *chain; 72 hammer2_key_t key_next; 73 hammer2_key_t inum; 74 int error; 75 76 error = 0; 77 chain = hammer2_chain_lookup_init(ochain, 0); 78 79 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { 80 if (oparent) 81 hammer2_chain_unlock(oparent); 82 inum = chain->bref.embed.dirent.inum; 83 parent = NULL; 84 error = hammer2_chain_inode_find(chain->pmp, inum, 85 clindex, 0, 86 &parent, &chain); 87 if (parent) { 88 hammer2_chain_unlock(parent); 89 hammer2_chain_drop(parent); 90 } 91 if (oparent) { 92 hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS); 93 if (ochain->parent != oparent) { 94 if (chain) { 95 hammer2_chain_unlock(chain); 96 hammer2_chain_drop(chain); 97 } 98 return HAMMER2_ERROR_EAGAIN; 99 } 100 } 101 } 102 103 /* 104 * Determine if the directory is empty or not by checking its 105 * visible namespace (the area which contains directory entries). 106 */ 107 parent = chain; 108 chain = NULL; 109 if (parent) { 110 chain = hammer2_chain_lookup(&parent, &key_next, 111 HAMMER2_DIRHASH_VISIBLE, 112 HAMMER2_KEY_MAX, 113 &error, 0); 114 } 115 if (chain) { 116 error = HAMMER2_ERROR_ENOTEMPTY; 117 hammer2_chain_unlock(chain); 118 hammer2_chain_drop(chain); 119 } 120 hammer2_chain_lookup_done(parent); 121 122 return error; 123 } 124 125 /* 126 * Backend for hammer2_vfs_root() 127 * 128 * This is called when a newly mounted PFS has not yet synchronized 129 * to the inode_tid and modify_tid. 130 */ 131 void 132 hammer2_xop_ipcluster(hammer2_thread_t *thr, hammer2_xop_t *arg) 133 { 134 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster; 135 hammer2_chain_t *chain; 136 int error; 137 138 chain = hammer2_inode_chain(xop->head.ip1, thr->clindex, 139 HAMMER2_RESOLVE_ALWAYS | 140 HAMMER2_RESOLVE_SHARED); 141 if (chain) 142 error = chain->error; 143 else 144 error = HAMMER2_ERROR_EIO; 145 146 hammer2_xop_feed(&xop->head, chain, thr->clindex, error); 147 if (chain) { 148 hammer2_chain_unlock(chain); 149 hammer2_chain_drop(chain); 150 } 151 } 152 153 /* 154 * Backend for hammer2_vop_readdir() 155 */ 156 void 157 hammer2_xop_readdir(hammer2_thread_t *thr, hammer2_xop_t *arg) 158 { 159 hammer2_xop_readdir_t *xop = &arg->xop_readdir; 160 hammer2_chain_t *parent; 161 hammer2_chain_t *chain; 162 hammer2_key_t key_next; 163 hammer2_key_t lkey; 164 int error = 0; 165 166 lkey = xop->lkey; 167 if (hammer2_debug & 0x0020) 168 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey); 169 170 /* 171 * The inode's chain is the iterator. If we cannot acquire it our 172 * contribution ends here. 173 */ 174 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 175 HAMMER2_RESOLVE_ALWAYS | 176 HAMMER2_RESOLVE_SHARED); 177 if (parent == NULL) { 178 kprintf("xop_readdir: NULL parent\n"); 179 goto done; 180 } 181 182 /* 183 * Directory scan [re]start and loop, the feed inherits the chain's 184 * lock so do not unlock it on the iteration. 185 */ 186 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey, 187 &error, HAMMER2_LOOKUP_SHARED); 188 if (chain == NULL) { 189 chain = hammer2_chain_lookup(&parent, &key_next, 190 lkey, HAMMER2_KEY_MAX, 191 &error, HAMMER2_LOOKUP_SHARED); 192 } 193 while (chain) { 194 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0); 195 if (error) 196 goto break2; 197 chain = hammer2_chain_next(&parent, chain, &key_next, 198 key_next, HAMMER2_KEY_MAX, 199 &error, HAMMER2_LOOKUP_SHARED); 200 } 201 break2: 202 if (chain) { 203 hammer2_chain_unlock(chain); 204 hammer2_chain_drop(chain); 205 } 206 hammer2_chain_unlock(parent); 207 hammer2_chain_drop(parent); 208 done: 209 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error); 210 } 211 212 /* 213 * Backend for hammer2_vop_nresolve() 214 */ 215 void 216 hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg) 217 { 218 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve; 219 hammer2_chain_t *parent; 220 hammer2_chain_t *chain; 221 const char *name; 222 size_t name_len; 223 hammer2_key_t key_next; 224 hammer2_key_t lhc; 225 int error; 226 227 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 228 HAMMER2_RESOLVE_ALWAYS | 229 HAMMER2_RESOLVE_SHARED); 230 if (parent == NULL) { 231 kprintf("xop_nresolve: NULL parent\n"); 232 chain = NULL; 233 error = HAMMER2_ERROR_EIO; 234 goto done; 235 } 236 name = xop->head.name1; 237 name_len = xop->head.name1_len; 238 239 /* 240 * Lookup the directory entry 241 */ 242 lhc = hammer2_dirhash(name, name_len); 243 chain = hammer2_chain_lookup(&parent, &key_next, 244 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 245 &error, 246 HAMMER2_LOOKUP_ALWAYS | 247 HAMMER2_LOOKUP_SHARED); 248 while (chain) { 249 if (hammer2_chain_dirent_test(chain, name, name_len)) 250 break; 251 chain = hammer2_chain_next(&parent, chain, &key_next, 252 key_next, 253 lhc + HAMMER2_DIRHASH_LOMASK, 254 &error, 255 HAMMER2_LOOKUP_ALWAYS | 256 HAMMER2_LOOKUP_SHARED); 257 } 258 259 /* 260 * If the entry is a hardlink pointer, resolve it. 261 */ 262 if (chain && chain->error == 0) { 263 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { 264 lhc = chain->bref.embed.dirent.inum; 265 error = hammer2_chain_inode_find(chain->pmp, 266 lhc, 267 thr->clindex, 268 HAMMER2_LOOKUP_SHARED, 269 &parent, 270 &chain); 271 } 272 } else if (chain && error == 0) { 273 error = chain->error; 274 } 275 done: 276 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, error); 277 if (chain) { 278 hammer2_chain_unlock(chain); 279 hammer2_chain_drop(chain); 280 } 281 if (parent) { 282 hammer2_chain_unlock(parent); 283 hammer2_chain_drop(parent); 284 } 285 } 286 287 /* 288 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and 289 * backend for pfs_delete. 290 * 291 * This function locates and removes a directory entry, and will lookup 292 * and return the underlying inode. For directory entries the underlying 293 * inode is not removed. If the directory entry is the actual inode itself, 294 * it may be conditonally removed and returned. 295 * 296 * WARNING! Any target inode's nlinks may not be synchronized to the 297 * in-memory inode. The frontend's hammer2_inode_unlink_finisher() 298 * is responsible for the final disposition of the actual inode. 299 */ 300 void 301 hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg) 302 { 303 hammer2_xop_unlink_t *xop = &arg->xop_unlink; 304 hammer2_chain_t *parent; 305 hammer2_chain_t *chain; 306 const char *name; 307 size_t name_len; 308 hammer2_key_t key_next; 309 hammer2_key_t lhc; 310 int error; 311 312 again: 313 /* 314 * Requires exclusive lock 315 */ 316 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 317 HAMMER2_RESOLVE_ALWAYS); 318 chain = NULL; 319 if (parent == NULL) { 320 kprintf("xop_nresolve: NULL parent\n"); 321 error = HAMMER2_ERROR_EIO; 322 goto done; 323 } 324 name = xop->head.name1; 325 name_len = xop->head.name1_len; 326 327 /* 328 * Lookup the directory entry 329 */ 330 lhc = hammer2_dirhash(name, name_len); 331 chain = hammer2_chain_lookup(&parent, &key_next, 332 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 333 &error, HAMMER2_LOOKUP_ALWAYS); 334 while (chain) { 335 if (hammer2_chain_dirent_test(chain, name, name_len)) 336 break; 337 chain = hammer2_chain_next(&parent, chain, &key_next, 338 key_next, 339 lhc + HAMMER2_DIRHASH_LOMASK, 340 &error, HAMMER2_LOOKUP_ALWAYS); 341 } 342 343 /* 344 * The directory entry will either be a BREF_TYPE_DIRENT or a 345 * BREF_TYPE_INODE. We always permanently delete DIRENTs, but 346 * must go by xop->dopermanent for BREF_TYPE_INODE. 347 * 348 * Note that the target chain's nlinks may not be synchronized with 349 * the in-memory hammer2_inode_t structure, so we don't try to do 350 * anything fancy here. The frontend deals with nlinks 351 * synchronization. 352 */ 353 if (chain && chain->error == 0) { 354 int dopermanent = xop->dopermanent & 1; 355 int doforce = xop->dopermanent & 2; 356 uint8_t type; 357 358 /* 359 * If the directory entry is the actual inode then use its 360 * type for the directory typing tests, otherwise if it is 361 * a directory entry, pull the type field from the entry. 362 * 363 * Directory entries are always permanently deleted 364 * (because they aren't the actual inode). 365 */ 366 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { 367 type = chain->bref.embed.dirent.type; 368 dopermanent |= HAMMER2_DELETE_PERMANENT; 369 } else { 370 type = chain->data->ipdata.meta.type; 371 } 372 373 /* 374 * Check directory typing and delete the entry. Note that 375 * nlinks adjustments are made on the real inode by the 376 * frontend, not here. 377 * 378 * Unfortunately, checkdirempty() may have to unlock (parent). 379 * If it no longer matches chain->parent after re-locking, 380 * EAGAIN is returned. 381 */ 382 if (type == HAMMER2_OBJTYPE_DIRECTORY && doforce) { 383 /* 384 * If doforce then execute the operation even if 385 * the directory is not empty or errored. 386 */ 387 /* ignore chain->error */ 388 error = hammer2_chain_delete(parent, chain, 389 xop->head.mtid, dopermanent); 390 } else if (type == HAMMER2_OBJTYPE_DIRECTORY && 391 (error = checkdirempty(parent, chain, thr->clindex)) != 0) { 392 /* 393 * error may be EAGAIN or ENOTEMPTY 394 */ 395 if (error == HAMMER2_ERROR_EAGAIN) { 396 hammer2_chain_unlock(chain); 397 hammer2_chain_drop(chain); 398 hammer2_chain_unlock(parent); 399 hammer2_chain_drop(parent); 400 goto again; 401 } 402 } else if (type == HAMMER2_OBJTYPE_DIRECTORY && 403 xop->isdir == 0) { 404 error = HAMMER2_ERROR_ENOTDIR; 405 } else if (type != HAMMER2_OBJTYPE_DIRECTORY && 406 xop->isdir >= 1) { 407 error = HAMMER2_ERROR_EISDIR; 408 } else { 409 /* 410 * Delete the directory entry. chain might also 411 * be a directly-embedded inode. 412 */ 413 error = chain->error; 414 hammer2_chain_delete(parent, chain, 415 xop->head.mtid, dopermanent); 416 } 417 } else { 418 if (chain && error == 0) 419 error = chain->error; 420 } 421 422 /* 423 * If chain is a directory entry we must resolve it. We do not try 424 * to manipulate the contents as it might not be synchronized with 425 * the frontend hammer2_inode_t, nor do we try to lookup the 426 * frontend hammer2_inode_t here (we are the backend!). 427 */ 428 if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { 429 int error2; 430 431 lhc = chain->bref.embed.dirent.inum; 432 433 error2 = hammer2_chain_inode_find(chain->pmp, lhc, 434 thr->clindex, 0, 435 &parent, &chain); 436 if (error2) { 437 kprintf("inode_find: %016jx %p failed\n", 438 lhc, chain); 439 error2 = 0; /* silently ignore */ 440 } 441 if (error == 0) 442 error = error2; 443 } 444 445 /* 446 * Return the inode target for further action. Typically used by 447 * hammer2_inode_unlink_finisher(). 448 */ 449 done: 450 hammer2_xop_feed(&xop->head, chain, thr->clindex, error); 451 if (chain) { 452 hammer2_chain_unlock(chain); 453 hammer2_chain_drop(chain); 454 chain = NULL; 455 } 456 if (parent) { 457 hammer2_chain_unlock(parent); 458 hammer2_chain_drop(parent); 459 parent = NULL; 460 } 461 } 462 463 /* 464 * Backend for hammer2_vop_nrename() 465 * 466 * This handles the backend rename operation. Typically this renames 467 * directory entries but can also be used to rename embedded inodes. 468 * 469 * NOTE! The frontend is responsible for updating the inode meta-data in 470 * the file being renamed and for decrementing the target-replaced 471 * inode's nlinks, if present. 472 */ 473 void 474 hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) 475 { 476 hammer2_xop_nrename_t *xop = &arg->xop_nrename; 477 hammer2_pfs_t *pmp; 478 hammer2_chain_t *parent; 479 hammer2_chain_t *chain; 480 hammer2_chain_t *tmp; 481 hammer2_inode_t *ip; 482 hammer2_key_t key_next; 483 int error; 484 485 /* 486 * We need the precise parent chain to issue the deletion. 487 * 488 * If this is a directory entry we must locate the underlying 489 * inode. If it is an embedded inode we can act directly on it. 490 */ 491 ip = xop->head.ip2; 492 pmp = ip->pmp; 493 chain = NULL; 494 error = 0; 495 496 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { 497 /* 498 * Find ip's direct parent chain. 499 */ 500 chain = hammer2_inode_chain(ip, thr->clindex, 501 HAMMER2_RESOLVE_ALWAYS); 502 if (chain == NULL) { 503 error = HAMMER2_ERROR_EIO; 504 parent = NULL; 505 goto done; 506 } 507 parent = hammer2_chain_getparent(chain, HAMMER2_RESOLVE_ALWAYS); 508 if (parent == NULL) { 509 error = HAMMER2_ERROR_EIO; 510 goto done; 511 } 512 } else { 513 /* 514 * The directory entry for the head.ip1 inode 515 * is in fdip, do a namespace search. 516 */ 517 hammer2_key_t lhc; 518 const char *name; 519 size_t name_len; 520 521 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 522 HAMMER2_RESOLVE_ALWAYS); 523 if (parent == NULL) { 524 kprintf("xop_nrename: NULL parent\n"); 525 error = HAMMER2_ERROR_EIO; 526 goto done; 527 } 528 name = xop->head.name1; 529 name_len = xop->head.name1_len; 530 531 /* 532 * Lookup the directory entry 533 */ 534 lhc = hammer2_dirhash(name, name_len); 535 chain = hammer2_chain_lookup(&parent, &key_next, 536 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 537 &error, HAMMER2_LOOKUP_ALWAYS); 538 while (chain) { 539 if (hammer2_chain_dirent_test(chain, name, name_len)) 540 break; 541 chain = hammer2_chain_next(&parent, chain, &key_next, 542 key_next, 543 lhc + HAMMER2_DIRHASH_LOMASK, 544 &error, 545 HAMMER2_LOOKUP_ALWAYS); 546 } 547 } 548 549 if (chain == NULL) { 550 /* XXX shouldn't happen, but does under fsstress */ 551 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n", 552 xop->head.name1, 553 xop->head.name2); 554 if (error == 0) 555 error = HAMMER2_ERROR_ENOENT; 556 goto done; 557 } 558 559 if (chain->error) { 560 error = chain->error; 561 goto done; 562 } 563 564 /* 565 * Delete it, then create it in the new namespace. 566 */ 567 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 568 hammer2_chain_unlock(parent); 569 hammer2_chain_drop(parent); 570 parent = NULL; /* safety */ 571 572 /* 573 * Adjust fields in the deleted chain appropriate for the rename 574 * operation. 575 * 576 * NOTE! For embedded inodes, the frontend will officially replicate 577 * the field adjustments, but we also do it here to maintain 578 * consistency in case of a crash. 579 */ 580 if (chain->bref.key != xop->lhc || 581 xop->head.name1_len != xop->head.name2_len || 582 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) { 583 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) { 584 hammer2_inode_data_t *wipdata; 585 586 error = hammer2_chain_modify(chain, xop->head.mtid, 587 0, 0); 588 if (error == 0) { 589 wipdata = &chain->data->ipdata; 590 591 bzero(wipdata->filename, 592 sizeof(wipdata->filename)); 593 bcopy(xop->head.name2, 594 wipdata->filename, 595 xop->head.name2_len); 596 wipdata->meta.name_key = xop->lhc; 597 wipdata->meta.name_len = xop->head.name2_len; 598 } 599 } 600 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { 601 if (xop->head.name2_len <= 602 sizeof(chain->bref.check.buf)) { 603 /* 604 * Remove any related data buffer, we can 605 * embed the filename in the bref itself. 606 */ 607 error = hammer2_chain_resize( 608 chain, xop->head.mtid, 0, 0, 0); 609 if (error == 0) { 610 error = hammer2_chain_modify( 611 chain, xop->head.mtid, 612 0, 0); 613 } 614 if (error == 0) { 615 bzero(chain->bref.check.buf, 616 sizeof(chain->bref.check.buf)); 617 bcopy(xop->head.name2, 618 chain->bref.check.buf, 619 xop->head.name2_len); 620 } 621 } else { 622 /* 623 * Associate a data buffer with the bref. 624 * Zero it for consistency. Note that the 625 * data buffer is not 64KB so use chain->bytes 626 * instead of sizeof(). 627 */ 628 error = hammer2_chain_resize( 629 chain, xop->head.mtid, 0, 630 hammer2_getradix(HAMMER2_ALLOC_MIN), 0); 631 if (error == 0) { 632 error = hammer2_chain_modify( 633 chain, xop->head.mtid, 634 0, 0); 635 } 636 if (error == 0) { 637 bzero(chain->data->buf, chain->bytes); 638 bcopy(xop->head.name2, 639 chain->data->buf, 640 xop->head.name2_len); 641 } 642 } 643 if (error == 0) { 644 chain->bref.embed.dirent.namlen = 645 xop->head.name2_len; 646 } 647 } 648 } 649 650 /* 651 * The frontend will replicate this operation and is the real final 652 * authority, but adjust the inode's iparent field too if the inode 653 * is embedded in the directory. 654 */ 655 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 656 chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) { 657 hammer2_inode_data_t *wipdata; 658 659 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 660 if (error == 0) { 661 wipdata = &chain->data->ipdata; 662 wipdata->meta.iparent = xop->head.ip3->meta.inum; 663 } 664 } 665 666 /* 667 * Destroy any matching target(s) before creating the new entry. 668 * This will result in some ping-ponging of the directory key 669 * iterator but that is ok. 670 */ 671 parent = hammer2_inode_chain(xop->head.ip3, thr->clindex, 672 HAMMER2_RESOLVE_ALWAYS); 673 if (parent == NULL) { 674 error = HAMMER2_ERROR_EIO; 675 goto done; 676 } 677 678 if (error == 0) { 679 tmp = hammer2_chain_lookup(&parent, &key_next, 680 xop->lhc & ~HAMMER2_DIRHASH_LOMASK, 681 xop->lhc | HAMMER2_DIRHASH_LOMASK, 682 &error, 683 HAMMER2_LOOKUP_ALWAYS); 684 while (tmp) { 685 if (hammer2_chain_dirent_test(tmp, xop->head.name2, 686 xop->head.name2_len)) { 687 hammer2_chain_delete(parent, tmp, 688 xop->head.mtid, 0); 689 } 690 tmp = hammer2_chain_next(&parent, tmp, &key_next, 691 key_next, 692 xop->lhc | 693 HAMMER2_DIRHASH_LOMASK, 694 &error, 695 HAMMER2_LOOKUP_ALWAYS); 696 } 697 } 698 if (error == 0) { 699 /* 700 * A relookup is required before the create to properly 701 * position the parent chain. 702 */ 703 tmp = hammer2_chain_lookup(&parent, &key_next, 704 xop->lhc, xop->lhc, 705 &error, 0); 706 KKASSERT(tmp == NULL); 707 error = hammer2_chain_create(&parent, &chain, 708 pmp, HAMMER2_METH_DEFAULT, 709 xop->lhc, 0, 710 HAMMER2_BREF_TYPE_INODE, 711 HAMMER2_INODE_BYTES, 712 xop->head.mtid, 0, 0); 713 } 714 done: 715 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error); 716 if (parent) { 717 hammer2_chain_unlock(parent); 718 hammer2_chain_drop(parent); 719 } 720 if (chain) { 721 hammer2_chain_unlock(chain); 722 hammer2_chain_drop(chain); 723 } 724 } 725 726 /* 727 * Directory collision resolver scan helper (backend, threaded). 728 * 729 * Used by the inode create code to locate an unused lhc. 730 */ 731 void 732 hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *arg) 733 { 734 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 735 hammer2_chain_t *parent; 736 hammer2_chain_t *chain; 737 hammer2_key_t key_next; 738 int error = 0; 739 740 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 741 HAMMER2_RESOLVE_ALWAYS | 742 HAMMER2_RESOLVE_SHARED); 743 if (parent == NULL) { 744 kprintf("xop_nresolve: NULL parent\n"); 745 chain = NULL; 746 error = HAMMER2_ERROR_EIO; 747 goto done; 748 } 749 750 /* 751 * Lookup all possibly conflicting directory entries, the feed 752 * inherits the chain's lock so do not unlock it on the iteration. 753 */ 754 chain = hammer2_chain_lookup(&parent, &key_next, 755 xop->lhc, 756 xop->lhc + HAMMER2_DIRHASH_LOMASK, 757 &error, 758 HAMMER2_LOOKUP_ALWAYS | 759 HAMMER2_LOOKUP_SHARED); 760 while (chain) { 761 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0); 762 if (error) { 763 hammer2_chain_unlock(chain); 764 hammer2_chain_drop(chain); 765 chain = NULL; /* safety */ 766 goto done; 767 } 768 chain = hammer2_chain_next(&parent, chain, &key_next, 769 key_next, 770 xop->lhc + HAMMER2_DIRHASH_LOMASK, 771 &error, 772 HAMMER2_LOOKUP_ALWAYS | 773 HAMMER2_LOOKUP_SHARED); 774 } 775 done: 776 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error); 777 if (parent) { 778 hammer2_chain_unlock(parent); 779 hammer2_chain_drop(parent); 780 } 781 } 782 783 /* 784 * Generic lookup of a specific key. 785 * 786 * Used by the inode hidden directory code to find the hidden directory. 787 */ 788 void 789 hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *arg) 790 { 791 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 792 hammer2_chain_t *parent; 793 hammer2_chain_t *chain; 794 hammer2_key_t key_next; 795 int error = 0; 796 797 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 798 HAMMER2_RESOLVE_ALWAYS | 799 HAMMER2_RESOLVE_SHARED); 800 chain = NULL; 801 if (parent == NULL) { 802 error = HAMMER2_ERROR_EIO; 803 goto done; 804 } 805 806 /* 807 * Lookup all possibly conflicting directory entries, the feed 808 * inherits the chain's lock so do not unlock it on the iteration. 809 */ 810 chain = hammer2_chain_lookup(&parent, &key_next, 811 xop->lhc, xop->lhc, 812 &error, 813 HAMMER2_LOOKUP_ALWAYS | 814 HAMMER2_LOOKUP_SHARED); 815 if (error == 0) { 816 if (chain) 817 error = chain->error; 818 else 819 error = HAMMER2_ERROR_ENOENT; 820 } 821 hammer2_xop_feed(&xop->head, chain, thr->clindex, error); 822 823 done: 824 if (chain) { 825 hammer2_chain_unlock(chain); 826 hammer2_chain_drop(chain); 827 } 828 if (parent) { 829 hammer2_chain_unlock(parent); 830 hammer2_chain_drop(parent); 831 } 832 } 833 834 /* 835 * Generic scan 836 * 837 * WARNING! Fed chains must be locked shared so ownership can be transfered 838 * and to prevent frontend/backend stalls that would occur with an 839 * exclusive lock. The shared lock also allows chain->data to be 840 * retained. 841 */ 842 void 843 hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *arg) 844 { 845 hammer2_xop_scanall_t *xop = &arg->xop_scanall; 846 hammer2_chain_t *parent; 847 hammer2_chain_t *chain; 848 hammer2_key_t key_next; 849 int error = 0; 850 851 /* 852 * Assert required flags. 853 */ 854 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED); 855 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED); 856 857 /* 858 * The inode's chain is the iterator. If we cannot acquire it our 859 * contribution ends here. 860 */ 861 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, 862 xop->resolve_flags); 863 if (parent == NULL) { 864 kprintf("xop_readdir: NULL parent\n"); 865 goto done; 866 } 867 868 /* 869 * Generic scan of exact records. Note that indirect blocks are 870 * automatically recursed and will not be returned. 871 */ 872 chain = hammer2_chain_lookup(&parent, &key_next, 873 xop->key_beg, xop->key_end, 874 &error, xop->lookup_flags); 875 while (chain) { 876 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0); 877 if (error) 878 goto break2; 879 chain = hammer2_chain_next(&parent, chain, &key_next, 880 key_next, xop->key_end, 881 &error, xop->lookup_flags); 882 } 883 break2: 884 if (chain) { 885 hammer2_chain_unlock(chain); 886 hammer2_chain_drop(chain); 887 } 888 hammer2_chain_unlock(parent); 889 hammer2_chain_drop(parent); 890 done: 891 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error); 892 } 893