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 *orig_parent) 69 { 70 hammer2_chain_t *parent; 71 hammer2_chain_t *chain; 72 hammer2_key_t key_next; 73 int cache_index = -1; 74 int error; 75 76 parent = hammer2_chain_lookup_init(orig_parent, 0); 77 chain = hammer2_chain_lookup(&parent, &key_next, 78 HAMMER2_DIRHASH_VISIBLE, 79 HAMMER2_KEY_MAX, 80 &cache_index, 0); 81 error = chain ? ENOTEMPTY : 0; 82 if (chain) { 83 hammer2_chain_unlock(chain); 84 hammer2_chain_drop(chain); 85 } 86 hammer2_chain_lookup_done(parent); 87 88 return error; 89 } 90 91 /* 92 * Backend for hammer2_vfs_root() 93 * 94 * This is called when a newly mounted PFS has not yet synchronized 95 * to the inode_tid and modify_tid. 96 */ 97 void 98 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex) 99 { 100 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster; 101 hammer2_chain_t *chain; 102 int error; 103 104 chain = hammer2_inode_chain(xop->head.ip1, clindex, 105 HAMMER2_RESOLVE_ALWAYS | 106 HAMMER2_RESOLVE_SHARED); 107 if (chain) 108 error = chain->error; 109 else 110 error = EIO; 111 112 hammer2_xop_feed(&xop->head, chain, clindex, error); 113 if (chain) { 114 hammer2_chain_unlock(chain); 115 hammer2_chain_drop(chain); 116 } 117 } 118 119 /* 120 * Backend for hammer2_vop_readdir() 121 */ 122 void 123 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex) 124 { 125 hammer2_xop_readdir_t *xop = &arg->xop_readdir; 126 hammer2_chain_t *parent; 127 hammer2_chain_t *chain; 128 hammer2_key_t key_next; 129 hammer2_key_t lkey; 130 int cache_index = -1; 131 int error = 0; 132 133 lkey = xop->lkey; 134 if (hammer2_debug & 0x0020) 135 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey); 136 137 /* 138 * The inode's chain is the iterator. If we cannot acquire it our 139 * contribution ends here. 140 */ 141 parent = hammer2_inode_chain(xop->head.ip1, clindex, 142 HAMMER2_RESOLVE_ALWAYS | 143 HAMMER2_RESOLVE_SHARED); 144 if (parent == NULL) { 145 kprintf("xop_readdir: NULL parent\n"); 146 goto done; 147 } 148 149 /* 150 * Directory scan [re]start and loop, the feed inherits the chain's 151 * lock so do not unlock it on the iteration. 152 */ 153 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey, 154 &cache_index, HAMMER2_LOOKUP_SHARED); 155 if (chain == NULL) { 156 chain = hammer2_chain_lookup(&parent, &key_next, 157 lkey, HAMMER2_KEY_MAX, 158 &cache_index, 159 HAMMER2_LOOKUP_SHARED); 160 } 161 while (chain) { 162 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 163 if (error) 164 break; 165 chain = hammer2_chain_next(&parent, chain, &key_next, 166 key_next, HAMMER2_KEY_MAX, 167 &cache_index, 168 HAMMER2_LOOKUP_SHARED); 169 } 170 if (chain) { 171 hammer2_chain_unlock(chain); 172 hammer2_chain_drop(chain); 173 } 174 hammer2_chain_unlock(parent); 175 hammer2_chain_drop(parent); 176 done: 177 hammer2_xop_feed(&xop->head, NULL, clindex, error); 178 } 179 180 /* 181 * Backend for hammer2_vop_nresolve() 182 */ 183 void 184 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex) 185 { 186 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve; 187 hammer2_chain_t *parent; 188 hammer2_chain_t *chain; 189 const hammer2_inode_data_t *ripdata; 190 const char *name; 191 size_t name_len; 192 hammer2_key_t key_next; 193 hammer2_key_t lhc; 194 int cache_index = -1; /* XXX */ 195 int error; 196 197 parent = hammer2_inode_chain(xop->head.ip1, clindex, 198 HAMMER2_RESOLVE_ALWAYS | 199 HAMMER2_RESOLVE_SHARED); 200 if (parent == NULL) { 201 kprintf("xop_nresolve: NULL parent\n"); 202 chain = NULL; 203 error = EIO; 204 goto done; 205 } 206 name = xop->head.name1; 207 name_len = xop->head.name1_len; 208 209 /* 210 * Lookup the directory entry 211 */ 212 lhc = hammer2_dirhash(name, name_len); 213 chain = hammer2_chain_lookup(&parent, &key_next, 214 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 215 &cache_index, 216 HAMMER2_LOOKUP_ALWAYS | 217 HAMMER2_LOOKUP_SHARED); 218 while (chain) { 219 ripdata = &chain->data->ipdata; 220 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 221 ripdata->meta.name_len == name_len && 222 bcmp(ripdata->filename, name, name_len) == 0) { 223 break; 224 } 225 chain = hammer2_chain_next(&parent, chain, &key_next, 226 key_next, 227 lhc + HAMMER2_DIRHASH_LOMASK, 228 &cache_index, 229 HAMMER2_LOOKUP_ALWAYS | 230 HAMMER2_LOOKUP_SHARED); 231 } 232 233 /* 234 * If the entry is a hardlink pointer, resolve it. 235 */ 236 error = 0; 237 if (chain) { 238 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 239 error = hammer2_chain_hardlink_find( 240 xop->head.ip1, 241 &parent, &chain, 242 HAMMER2_LOOKUP_SHARED); 243 } 244 } 245 done: 246 error = hammer2_xop_feed(&xop->head, chain, clindex, error); 247 if (chain) { 248 hammer2_chain_unlock(chain); 249 hammer2_chain_drop(chain); 250 } 251 if (parent) { 252 hammer2_chain_unlock(parent); 253 hammer2_chain_drop(parent); 254 } 255 } 256 257 /* 258 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper 259 * for hammer2_vop_nrename(). 260 * 261 * This function locates and removes the directory entry. If the 262 * entry is a hardlink pointer, this function will also remove the 263 * hardlink target if the target's nlinks is 1. 264 * 265 * The frontend is responsible for moving open inodes to the hidden directory 266 * and for decrementing nlinks. 267 */ 268 void 269 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex) 270 { 271 hammer2_xop_unlink_t *xop = &arg->xop_unlink; 272 hammer2_chain_t *parent; 273 hammer2_chain_t *chain; 274 const hammer2_inode_data_t *ripdata; 275 const char *name; 276 size_t name_len; 277 hammer2_key_t key_next; 278 hammer2_key_t lhc; 279 int cache_index = -1; /* XXX */ 280 int error; 281 282 /* 283 * Requires exclusive lock 284 */ 285 parent = hammer2_inode_chain(xop->head.ip1, clindex, 286 HAMMER2_RESOLVE_ALWAYS); 287 chain = NULL; 288 if (parent == NULL) { 289 kprintf("xop_nresolve: NULL parent\n"); 290 error = EIO; 291 goto done; 292 } 293 name = xop->head.name1; 294 name_len = xop->head.name1_len; 295 296 /* 297 * Lookup the directory entry 298 */ 299 lhc = hammer2_dirhash(name, name_len); 300 chain = hammer2_chain_lookup(&parent, &key_next, 301 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 302 &cache_index, 303 HAMMER2_LOOKUP_ALWAYS); 304 while (chain) { 305 ripdata = &chain->data->ipdata; 306 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 307 ripdata->meta.name_len == name_len && 308 bcmp(ripdata->filename, name, name_len) == 0) { 309 break; 310 } 311 chain = hammer2_chain_next(&parent, chain, &key_next, 312 key_next, 313 lhc + HAMMER2_DIRHASH_LOMASK, 314 &cache_index, 315 HAMMER2_LOOKUP_ALWAYS); 316 } 317 318 /* 319 * If the directory entry is a HARDLINK pointer then obtain the 320 * underlying file type for the directory typing tests and delete 321 * the HARDLINK pointer chain permanently. The frontend is left 322 * responsible for handling nlinks on and deleting the actual inode. 323 * 324 * If the directory entry is the actual inode then use its type 325 * for the directory typing tests and delete the chain, permanency 326 * depends on whether the inode is open or not. 327 * 328 * Check directory typing and delete the entry. Note that 329 * nlinks adjustments are made on the real inode by the frontend, 330 * not here. 331 */ 332 error = 0; 333 if (chain) { 334 int dopermanent = xop->dopermanent; 335 uint8_t type; 336 337 type = chain->data->ipdata.meta.type; 338 if (type == HAMMER2_OBJTYPE_HARDLINK) { 339 type = chain->data->ipdata.meta.target_type; 340 dopermanent |= HAMMER2_DELETE_PERMANENT; 341 } 342 343 if (type == HAMMER2_OBJTYPE_DIRECTORY && 344 checkdirempty(chain) != 0) { 345 error = ENOTEMPTY; 346 } else if (type == HAMMER2_OBJTYPE_DIRECTORY && 347 xop->isdir == 0) { 348 error = ENOTDIR; 349 } else 350 if (type != HAMMER2_OBJTYPE_DIRECTORY && 351 xop->isdir >= 1) { 352 error = EISDIR; 353 } else { 354 /* 355 * This deletes the directory entry itself, which is 356 * also the inode when nlinks == 1. Hardlink targets 357 * are handled in the next conditional. 358 */ 359 error = chain->error; 360 hammer2_chain_delete(parent, chain, 361 xop->head.mtid, dopermanent); 362 } 363 } 364 365 /* 366 * If the entry is a hardlink pointer, resolve it. If this is the 367 * last link, delete it. The frontend has the master copy of nlinks 368 * but we still have to make adjustments here to synchronize with it. 369 * 370 * On delete / adjust nlinks if there is no error. But we still need 371 * to resolve the hardlink to feed the inode's real chain back to 372 * the frontend. 373 * 374 * XXX we are basically tracking the nlinks count by doing a delta 375 * adjustment instead of having the frontend pass the absolute 376 * value down. We really need to have the frontend pass the 377 * absolute value down (difficult because there might not be 378 * an 'ip'). See also hammer2_xop_nlink(). 379 */ 380 if (chain && 381 chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 382 int error2; 383 384 error2 = hammer2_chain_hardlink_find(xop->head.ip1, 385 &parent, &chain, 0); 386 if (chain && error == 0 && error2 == 0 && 387 (int64_t)chain->data->ipdata.meta.nlinks <= 1) { 388 hammer2_chain_delete(parent, chain, 389 xop->head.mtid, 390 xop->dopermanent); 391 } else if (chain && error == 0 && error2 == 0) { 392 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 393 --chain->data->ipdata.meta.nlinks; 394 } 395 if (error == 0) 396 error = error2; 397 } 398 399 /* 400 * Chains passed to feed are expected to be locked shared. 401 */ 402 if (chain) { 403 hammer2_chain_unlock(chain); 404 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS | 405 HAMMER2_RESOLVE_SHARED); 406 } 407 408 /* 409 * We always return the hardlink target (the real inode) for 410 * further action. 411 */ 412 done: 413 hammer2_xop_feed(&xop->head, chain, clindex, error); 414 if (chain) { 415 hammer2_chain_unlock(chain); 416 hammer2_chain_drop(chain); 417 chain = NULL; 418 } 419 if (parent) { 420 hammer2_chain_unlock(parent); 421 hammer2_chain_drop(parent); 422 parent = NULL; 423 } 424 } 425 426 /* 427 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename() 428 * 429 * ip1 - fdip 430 * ip2 - ip 431 * ip3 - cdip 432 * 433 * If a hardlink pointer: 434 * The existing hardlink target {fdip,ip} must be moved to another 435 * directory {cdip,ip} 436 * 437 * If not a hardlink pointer: 438 * Convert the target {fdip,ip} to a hardlink target {cdip,ip} and 439 * replace the original namespace {fdip,name} with a hardlink pointer. 440 */ 441 void 442 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex) 443 { 444 hammer2_xop_nlink_t *xop = &arg->xop_nlink; 445 hammer2_pfs_t *pmp; 446 hammer2_inode_data_t *wipdata; 447 hammer2_chain_t *parent; 448 hammer2_chain_t *chain; 449 hammer2_chain_t *tmp; 450 hammer2_inode_t *ip; 451 hammer2_key_t key_dummy; 452 int cache_index = -1; 453 int error; 454 int did_delete = 0; 455 456 /* 457 * We need the precise parent chain to issue the deletion. 458 */ 459 ip = xop->head.ip2; 460 pmp = ip->pmp; 461 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 462 if (parent) 463 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS); 464 if (parent == NULL) { 465 chain = NULL; 466 error = EIO; 467 goto done; 468 } 469 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 470 if (chain == NULL) { 471 error = EIO; 472 goto done; 473 } 474 KKASSERT(chain->parent == parent); 475 476 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) { 477 /* 478 * Delete the original chain and hold onto it for the move 479 * to cdir. 480 * 481 * Replace the namespace with a hardlink pointer if the 482 * chain being moved is not already a hardlink target. 483 */ 484 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 485 did_delete = 1; 486 487 tmp = NULL; 488 error = hammer2_chain_create(&parent, &tmp, pmp, 489 chain->bref.key, 0, 490 HAMMER2_BREF_TYPE_INODE, 491 HAMMER2_INODE_BYTES, 492 xop->head.mtid, 0, 0); 493 if (error) 494 goto done; 495 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0); 496 wipdata = &tmp->data->ipdata; 497 bzero(wipdata, sizeof(*wipdata)); 498 wipdata->meta.name_key = chain->data->ipdata.meta.name_key; 499 wipdata->meta.name_len = chain->data->ipdata.meta.name_len; 500 bcopy(chain->data->ipdata.filename, wipdata->filename, 501 chain->data->ipdata.meta.name_len); 502 wipdata->meta.target_type = chain->data->ipdata.meta.type; 503 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; 504 wipdata->meta.inum = ip->meta.inum; 505 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; 506 wipdata->meta.nlinks = 1; 507 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; 508 509 hammer2_chain_unlock(tmp); 510 hammer2_chain_drop(tmp); 511 } else if (xop->head.ip1 != xop->head.ip3) { 512 /* 513 * Delete the hardlink target so it can be moved 514 * to cdir. 515 */ 516 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 517 did_delete = 1; 518 } else { 519 /* 520 * Deletion not necessary (just a nlinks update). 521 */ 522 did_delete = 0; 523 } 524 525 hammer2_chain_unlock(parent); 526 hammer2_chain_drop(parent); 527 parent = NULL; 528 529 /* 530 * Ok, back to the deleted chain. We must reconnect this chain 531 * as a hardlink target to cdir (ip3). 532 * 533 * WARNING! Frontend assumes filename length is 18 bytes. 534 */ 535 if (did_delete) { 536 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 537 wipdata = &chain->data->ipdata; 538 ksnprintf(wipdata->filename, sizeof(wipdata->filename), 539 "0x%016jx", (intmax_t)ip->meta.inum); 540 wipdata->meta.name_len = strlen(wipdata->filename); 541 wipdata->meta.name_key = ip->meta.inum; 542 543 /* 544 * We must seek parent properly for the create to reattach 545 * chain. XXX just use chain->parent or 546 * inode_chain_and_parent() ? 547 */ 548 parent = hammer2_inode_chain(xop->head.ip3, clindex, 549 HAMMER2_RESOLVE_ALWAYS); 550 if (parent == NULL) { 551 error = EIO; 552 goto done; 553 } 554 tmp = hammer2_chain_lookup(&parent, &key_dummy, 555 ip->meta.inum, ip->meta.inum, 556 &cache_index, 0); 557 if (tmp) { 558 hammer2_chain_unlock(tmp); 559 hammer2_chain_drop(tmp); 560 error = EEXIST; 561 goto done; 562 } 563 error = hammer2_chain_create(&parent, &chain, pmp, 564 wipdata->meta.name_key, 0, 565 HAMMER2_BREF_TYPE_INODE, 566 HAMMER2_INODE_BYTES, 567 xop->head.mtid, 0, 0); 568 } else { 569 error = 0; 570 } 571 572 /* 573 * Bump nlinks to synchronize with frontend. 574 */ 575 if (xop->nlinks_delta) { 576 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 577 chain->data->ipdata.meta.nlinks += xop->nlinks_delta; 578 } 579 580 /* 581 * To avoid having to scan the collision space we can simply 582 * reuse the inode's original name_key. But ip->meta.name_key 583 * may have already been updated by the front-end, so use xop->lhc. 584 * 585 * (frontend is responsible for fixing up ip->pip). 586 */ 587 done: 588 hammer2_xop_feed(&xop->head, NULL, clindex, error); 589 if (parent) { 590 hammer2_chain_unlock(parent); 591 hammer2_chain_drop(parent); 592 } 593 if (chain) { 594 hammer2_chain_unlock(chain); 595 hammer2_chain_drop(chain); 596 } 597 } 598 599 /* 600 * Backend for hammer2_vop_nrename() 601 * 602 * This handles the final step of renaming, either renaming the 603 * actual inode or renaming the hardlink pointer. 604 */ 605 void 606 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex) 607 { 608 hammer2_xop_nrename_t *xop = &arg->xop_nrename; 609 hammer2_pfs_t *pmp; 610 hammer2_chain_t *parent; 611 hammer2_chain_t *chain; 612 hammer2_chain_t *tmp; 613 hammer2_inode_t *ip; 614 hammer2_key_t key_dummy; 615 int cache_index = -1; 616 int error; 617 618 /* 619 * We need the precise parent chain to issue the deletion. 620 * 621 * If this is not a hardlink target we can act on the inode, 622 * otherwise we have to locate the hardlink pointer. 623 */ 624 ip = xop->head.ip2; 625 pmp = ip->pmp; 626 chain = NULL; 627 628 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { 629 /* 630 * Find ip's direct parent chain. 631 */ 632 parent = hammer2_inode_chain(ip, clindex, 633 HAMMER2_RESOLVE_ALWAYS); 634 if (parent) 635 hammer2_chain_getparent(&parent, 636 HAMMER2_RESOLVE_ALWAYS); 637 if (parent == NULL) { 638 error = EIO; 639 goto done; 640 } 641 chain = hammer2_inode_chain(ip, clindex, 642 HAMMER2_RESOLVE_ALWAYS); 643 if (chain == NULL) { 644 error = EIO; 645 goto done; 646 } 647 } else { 648 /* 649 * The hardlink pointer for the head.ip1 hardlink target 650 * is in fdip, do a namespace search. 651 */ 652 const hammer2_inode_data_t *ripdata; 653 hammer2_key_t lhc; 654 hammer2_key_t key_next; 655 const char *name; 656 size_t name_len; 657 658 parent = hammer2_inode_chain(xop->head.ip1, clindex, 659 HAMMER2_RESOLVE_ALWAYS); 660 if (parent == NULL) { 661 kprintf("xop_nrename: NULL parent\n"); 662 error = EIO; 663 goto done; 664 } 665 name = xop->head.name1; 666 name_len = xop->head.name1_len; 667 668 /* 669 * Lookup the directory entry 670 */ 671 lhc = hammer2_dirhash(name, name_len); 672 chain = hammer2_chain_lookup(&parent, &key_next, 673 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 674 &cache_index, 675 HAMMER2_LOOKUP_ALWAYS); 676 while (chain) { 677 ripdata = &chain->data->ipdata; 678 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 679 ripdata->meta.name_len == name_len && 680 bcmp(ripdata->filename, name, name_len) == 0) { 681 break; 682 } 683 chain = hammer2_chain_next(&parent, chain, &key_next, 684 key_next, 685 lhc + HAMMER2_DIRHASH_LOMASK, 686 &cache_index, 687 HAMMER2_LOOKUP_ALWAYS); 688 } 689 } 690 691 if (chain == NULL) { 692 /* XXX shouldn't happen, but does under fsstress */ 693 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n", 694 xop->head.name1, 695 xop->head.name2); 696 error = ENOENT; 697 goto done; 698 } 699 700 /* 701 * Delete it, then create it in the new namespace. 702 */ 703 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 704 hammer2_chain_unlock(parent); 705 hammer2_chain_drop(parent); 706 parent = NULL; /* safety */ 707 708 /* 709 * Ok, back to the deleted chain. We must reconnect this chain 710 * to tdir (ip3). The chain (a real inode or a hardlink pointer) 711 * is not otherwise modified. 712 * 713 * Frontend is expected to replicate the same inode meta data 714 * modifications. 715 * 716 * NOTE! This chain may not represent the actual inode, it 717 * can be a hardlink pointer. 718 * 719 * XXX in-inode parent directory specification? 720 */ 721 if (chain->data->ipdata.meta.name_key != xop->lhc || 722 xop->head.name1_len != xop->head.name2_len || 723 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) { 724 hammer2_inode_data_t *wipdata; 725 726 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 727 wipdata = &chain->data->ipdata; 728 729 bzero(wipdata->filename, sizeof(wipdata->filename)); 730 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); 731 wipdata->meta.name_key = xop->lhc; 732 wipdata->meta.name_len = xop->head.name2_len; 733 } 734 735 /* 736 * We must seek parent properly for the create. 737 */ 738 parent = hammer2_inode_chain(xop->head.ip3, clindex, 739 HAMMER2_RESOLVE_ALWAYS); 740 if (parent == NULL) { 741 error = EIO; 742 goto done; 743 } 744 tmp = hammer2_chain_lookup(&parent, &key_dummy, 745 xop->lhc, xop->lhc, 746 &cache_index, 0); 747 if (tmp) { 748 hammer2_chain_unlock(tmp); 749 hammer2_chain_drop(tmp); 750 error = EEXIST; 751 goto done; 752 } 753 754 error = hammer2_chain_create(&parent, &chain, pmp, 755 xop->lhc, 0, 756 HAMMER2_BREF_TYPE_INODE, 757 HAMMER2_INODE_BYTES, 758 xop->head.mtid, 0, 0); 759 /* 760 * (frontend is responsible for fixing up ip->pip). 761 */ 762 done: 763 hammer2_xop_feed(&xop->head, NULL, clindex, error); 764 if (parent) { 765 hammer2_chain_unlock(parent); 766 hammer2_chain_drop(parent); 767 } 768 if (chain) { 769 hammer2_chain_unlock(chain); 770 hammer2_chain_drop(chain); 771 } 772 } 773 774 /* 775 * Directory collision resolver scan helper (backend, threaded). 776 * 777 * Used by the inode create code to locate an unused lhc. 778 */ 779 void 780 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex) 781 { 782 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 783 hammer2_chain_t *parent; 784 hammer2_chain_t *chain; 785 hammer2_key_t key_next; 786 int cache_index = -1; /* XXX */ 787 int error = 0; 788 789 parent = hammer2_inode_chain(xop->head.ip1, clindex, 790 HAMMER2_RESOLVE_ALWAYS | 791 HAMMER2_RESOLVE_SHARED); 792 if (parent == NULL) { 793 kprintf("xop_nresolve: NULL parent\n"); 794 chain = NULL; 795 error = EIO; 796 goto done; 797 } 798 799 /* 800 * Lookup all possibly conflicting directory entries, the feed 801 * inherits the chain's lock so do not unlock it on the iteration. 802 */ 803 chain = hammer2_chain_lookup(&parent, &key_next, 804 xop->lhc, 805 xop->lhc + HAMMER2_DIRHASH_LOMASK, 806 &cache_index, 807 HAMMER2_LOOKUP_ALWAYS | 808 HAMMER2_LOOKUP_SHARED); 809 while (chain) { 810 error = hammer2_xop_feed(&xop->head, chain, clindex, 811 chain->error); 812 if (error) { 813 hammer2_chain_unlock(chain); 814 hammer2_chain_drop(chain); 815 chain = NULL; /* safety */ 816 break; 817 } 818 chain = hammer2_chain_next(&parent, chain, &key_next, 819 key_next, 820 xop->lhc + HAMMER2_DIRHASH_LOMASK, 821 &cache_index, 822 HAMMER2_LOOKUP_ALWAYS | 823 HAMMER2_LOOKUP_SHARED); 824 } 825 done: 826 hammer2_xop_feed(&xop->head, NULL, clindex, error); 827 if (parent) { 828 hammer2_chain_unlock(parent); 829 hammer2_chain_drop(parent); 830 } 831 } 832 833 /* 834 * Generic lookup of a specific key. 835 * 836 * Used by the inode hidden directory code to find the hidden directory. 837 */ 838 void 839 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex) 840 { 841 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 842 hammer2_chain_t *parent; 843 hammer2_chain_t *chain; 844 hammer2_key_t key_next; 845 int cache_index = -1; /* XXX */ 846 int error = 0; 847 848 parent = hammer2_inode_chain(xop->head.ip1, clindex, 849 HAMMER2_RESOLVE_ALWAYS | 850 HAMMER2_RESOLVE_SHARED); 851 chain = NULL; 852 if (parent == NULL) { 853 error = EIO; 854 goto done; 855 } 856 857 /* 858 * Lookup all possibly conflicting directory entries, the feed 859 * inherits the chain's lock so do not unlock it on the iteration. 860 */ 861 chain = hammer2_chain_lookup(&parent, &key_next, 862 xop->lhc, xop->lhc, 863 &cache_index, 864 HAMMER2_LOOKUP_ALWAYS | 865 HAMMER2_LOOKUP_SHARED); 866 if (chain) 867 hammer2_xop_feed(&xop->head, chain, clindex, chain->error); 868 else 869 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT); 870 871 done: 872 if (chain) { 873 hammer2_chain_unlock(chain); 874 hammer2_chain_drop(chain); 875 } 876 if (parent) { 877 hammer2_chain_unlock(parent); 878 hammer2_chain_drop(parent); 879 } 880 } 881 882 /* 883 * Generic scan 884 */ 885 void 886 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex) 887 { 888 hammer2_xop_scanall_t *xop = &arg->xop_scanall; 889 hammer2_chain_t *parent; 890 hammer2_chain_t *chain; 891 hammer2_key_t key_next; 892 int cache_index = -1; 893 int error = 0; 894 895 /* 896 * The inode's chain is the iterator. If we cannot acquire it our 897 * contribution ends here. 898 */ 899 parent = hammer2_inode_chain(xop->head.ip1, clindex, 900 HAMMER2_RESOLVE_ALWAYS | 901 HAMMER2_RESOLVE_SHARED); 902 if (parent == NULL) { 903 kprintf("xop_readdir: NULL parent\n"); 904 goto done; 905 } 906 907 /* 908 * Generic scan of exact records. Note that indirect blocks are 909 * automatically recursed and will not be returned. 910 */ 911 chain = hammer2_chain_lookup(&parent, &key_next, 912 xop->key_beg, xop->key_end, 913 &cache_index, HAMMER2_LOOKUP_SHARED | 914 HAMMER2_LOOKUP_NODIRECT); 915 while (chain) { 916 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 917 if (error) 918 break; 919 chain = hammer2_chain_next(&parent, chain, &key_next, 920 key_next, xop->key_end, 921 &cache_index, 922 HAMMER2_LOOKUP_SHARED | 923 HAMMER2_LOOKUP_NODIRECT); 924 } 925 if (chain) { 926 hammer2_chain_unlock(chain); 927 hammer2_chain_drop(chain); 928 } 929 hammer2_chain_unlock(parent); 930 hammer2_chain_drop(parent); 931 done: 932 hammer2_xop_feed(&xop->head, NULL, clindex, error); 933 } 934