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