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