1 /* 2 * Copyright (c) 2011-2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * by Daniel Flores (GSOC 2013 - mentored by Matthew Dillon, compression) 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 /* 37 * Per-node backend for kernel filesystem interface. 38 * 39 * This executes a VOP concurrently on multiple nodes, each node via its own 40 * thread, and competes to advance the original request. The original 41 * request is retired the moment all requirements are met, even if the 42 * operation is still in-progress on some nodes. 43 */ 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/fcntl.h> 48 #include <sys/buf.h> 49 #include <sys/proc.h> 50 #include <sys/namei.h> 51 #include <sys/mount.h> 52 #include <sys/vnode.h> 53 #include <sys/mountctl.h> 54 #include <sys/dirent.h> 55 #include <sys/uio.h> 56 #include <sys/objcache.h> 57 #include <sys/event.h> 58 #include <sys/file.h> 59 #include <vfs/fifofs/fifo.h> 60 61 #include "hammer2.h" 62 63 /* 64 * Backend for hammer2_vfs_root() 65 * 66 * This is called when a newly mounted PFS has not yet synchronized 67 * to the inode_tid and modify_tid. 68 */ 69 void 70 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex) 71 { 72 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster; 73 hammer2_chain_t *chain; 74 int error; 75 76 chain = hammer2_inode_chain(xop->head.ip1, clindex, 77 HAMMER2_RESOLVE_ALWAYS | 78 HAMMER2_RESOLVE_SHARED); 79 if (chain) 80 error = chain->error; 81 else 82 error = EIO; 83 84 hammer2_xop_feed(&xop->head, chain, clindex, error); 85 if (chain) 86 hammer2_chain_drop(chain); 87 } 88 89 /* 90 * Backend for hammer2_vop_readdir() 91 */ 92 void 93 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex) 94 { 95 hammer2_xop_readdir_t *xop = &arg->xop_readdir; 96 hammer2_chain_t *parent; 97 hammer2_chain_t *chain; 98 hammer2_key_t key_next; 99 hammer2_key_t lkey; 100 int cache_index = -1; 101 int error = 0; 102 103 lkey = xop->lkey; 104 if (hammer2_debug & 0x0020) 105 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey); 106 107 /* 108 * The inode's chain is the iterator. If we cannot acquire it our 109 * contribution ends here. 110 */ 111 parent = hammer2_inode_chain(xop->head.ip1, clindex, 112 HAMMER2_RESOLVE_ALWAYS | 113 HAMMER2_RESOLVE_SHARED); 114 if (parent == NULL) { 115 kprintf("xop_readdir: NULL parent\n"); 116 goto done; 117 } 118 119 /* 120 * Directory scan [re]start and loop, the feed inherits the chain's 121 * lock so do not unlock it on the iteration. 122 */ 123 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey, 124 &cache_index, HAMMER2_LOOKUP_SHARED); 125 if (chain == NULL) { 126 chain = hammer2_chain_lookup(&parent, &key_next, 127 lkey, HAMMER2_KEY_MAX, 128 &cache_index, 129 HAMMER2_LOOKUP_SHARED); 130 } 131 while (chain) { 132 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 133 if (error) 134 break; 135 chain = hammer2_chain_next(&parent, chain, &key_next, 136 key_next, HAMMER2_KEY_MAX, 137 &cache_index, 138 HAMMER2_LOOKUP_SHARED | 139 HAMMER2_LOOKUP_NOUNLOCK); 140 } 141 if (chain) 142 hammer2_chain_drop(chain); 143 hammer2_chain_unlock(parent); 144 hammer2_chain_drop(parent); 145 done: 146 hammer2_xop_feed(&xop->head, NULL, clindex, error); 147 } 148 149 /* 150 * Backend for hammer2_vop_nresolve() 151 */ 152 void 153 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex) 154 { 155 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve; 156 hammer2_chain_t *parent; 157 hammer2_chain_t *chain; 158 const hammer2_inode_data_t *ripdata; 159 const char *name; 160 size_t name_len; 161 hammer2_key_t key_next; 162 hammer2_key_t lhc; 163 int cache_index = -1; /* XXX */ 164 int error; 165 166 parent = hammer2_inode_chain(xop->head.ip1, clindex, 167 HAMMER2_RESOLVE_ALWAYS | 168 HAMMER2_RESOLVE_SHARED); 169 if (parent == NULL) { 170 kprintf("xop_nresolve: NULL parent\n"); 171 chain = NULL; 172 error = EIO; 173 goto done; 174 } 175 name = xop->head.name1; 176 name_len = xop->head.name1_len; 177 178 /* 179 * Lookup the directory entry 180 */ 181 lhc = hammer2_dirhash(name, name_len); 182 chain = hammer2_chain_lookup(&parent, &key_next, 183 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 184 &cache_index, 185 HAMMER2_LOOKUP_ALWAYS | 186 HAMMER2_LOOKUP_SHARED); 187 while (chain) { 188 ripdata = &chain->data->ipdata; 189 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 190 ripdata->meta.name_len == name_len && 191 bcmp(ripdata->filename, name, name_len) == 0) { 192 break; 193 } 194 chain = hammer2_chain_next(&parent, chain, &key_next, 195 key_next, 196 lhc + HAMMER2_DIRHASH_LOMASK, 197 &cache_index, 198 HAMMER2_LOOKUP_ALWAYS | 199 HAMMER2_LOOKUP_SHARED); 200 } 201 202 /* 203 * If the entry is a hardlink pointer, resolve it. 204 */ 205 error = 0; 206 if (chain) { 207 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 208 error = hammer2_chain_hardlink_find( 209 xop->head.ip1, 210 &parent, &chain, 211 HAMMER2_RESOLVE_SHARED); 212 } 213 } 214 done: 215 error = hammer2_xop_feed(&xop->head, chain, clindex, error); 216 if (chain) { 217 /* leave lock intact for feed */ 218 hammer2_chain_drop(chain); 219 } 220 if (parent) { 221 hammer2_chain_unlock(parent); 222 hammer2_chain_drop(parent); 223 } 224 } 225 226 /* 227 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper 228 * for hammer2_vop_nrename(). 229 * 230 * This function locates and removes the directory entry. If the 231 * entry is a hardlink pointer, this function will also remove the 232 * hardlink target if the target's nlinks is 1. 233 * 234 * The frontend is responsible for moving open inodes to the hidden directory 235 * and for decrementing nlinks. 236 */ 237 void 238 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex) 239 { 240 hammer2_xop_unlink_t *xop = &arg->xop_unlink; 241 hammer2_chain_t *parent; 242 hammer2_chain_t *chain; 243 const hammer2_inode_data_t *ripdata; 244 const char *name; 245 size_t name_len; 246 hammer2_key_t key_next; 247 hammer2_key_t lhc; 248 int cache_index = -1; /* XXX */ 249 int error; 250 251 /* 252 * Requires exclusive lock 253 */ 254 parent = hammer2_inode_chain(xop->head.ip1, clindex, 255 HAMMER2_RESOLVE_ALWAYS); 256 if (parent == NULL) { 257 kprintf("xop_nresolve: NULL parent\n"); 258 chain = NULL; 259 error = EIO; 260 goto done; 261 } 262 name = xop->head.name1; 263 name_len = xop->head.name1_len; 264 265 /* 266 * Lookup the directory entry 267 */ 268 lhc = hammer2_dirhash(name, name_len); 269 chain = hammer2_chain_lookup(&parent, &key_next, 270 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 271 &cache_index, 272 HAMMER2_LOOKUP_ALWAYS); 273 while (chain) { 274 ripdata = &chain->data->ipdata; 275 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 276 ripdata->meta.name_len == name_len && 277 bcmp(ripdata->filename, name, name_len) == 0) { 278 break; 279 } 280 chain = hammer2_chain_next(&parent, chain, &key_next, 281 key_next, 282 lhc + HAMMER2_DIRHASH_LOMASK, 283 &cache_index, 284 HAMMER2_LOOKUP_ALWAYS); 285 } 286 287 /* 288 * If the directory entry is a HARDLINK pointer then obtain the 289 * underlying file type for the directory typing tests and delete 290 * the HARDLINK pointer chain permanently. The frontend is left 291 * responsible for handling nlinks on and deleting the actual inode. 292 * 293 * If the directory entry is the actual inode then use its type 294 * for the directory typing tests and delete the chain, permanency 295 * depends on whether the inode is open or not. 296 * 297 * Check directory typing and delete the entry. Note that 298 * nlinks adjustments are made on the real inode by the frontend, 299 * not here. 300 */ 301 error = 0; 302 if (chain) { 303 int dopermanent = xop->dopermanent; 304 uint8_t type; 305 306 type = chain->data->ipdata.meta.type; 307 if (type == HAMMER2_OBJTYPE_HARDLINK) { 308 type = chain->data->ipdata.meta.target_type; 309 dopermanent |= HAMMER2_DELETE_PERMANENT; 310 } 311 312 if (type == HAMMER2_OBJTYPE_DIRECTORY && 313 xop->isdir == 0) { 314 error = ENOTDIR; 315 } else 316 if (type != HAMMER2_OBJTYPE_DIRECTORY && 317 xop->isdir >= 1) { 318 error = EISDIR; 319 } else { 320 /* 321 * This deletes the directory entry itself, which is 322 * also the inode when nlinks == 1. Hardlink targets 323 * are handled in the next conditional. 324 */ 325 hammer2_chain_delete(parent, chain, 326 xop->head.mtid, dopermanent); 327 } 328 } 329 330 /* 331 * If the entry is a hardlink pointer, resolve it. If this is the 332 * last link, delete it. The frontend has the master copy of nlinks 333 * but we still have to make adjustments here to synchronize with it. 334 * 335 * On delete / adjust nlinks if there is no error. But we still need 336 * to resolve the hardlink to feed the inode's real chain back to 337 * the frontend. 338 * 339 * XXX we are basically tracking the nlinks count by doing a delta 340 * adjustment instead of having the frontend pass the absolute 341 * value down. We really need to have the frontend pass the 342 * absolute value down (difficult because there might not be 343 * an 'ip'). See also hammer2_xop_nlink(). 344 */ 345 if (chain && 346 chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { 347 int error2; 348 349 error2 = hammer2_chain_hardlink_find(xop->head.ip1, 350 &parent, &chain, 0); 351 if (chain && error == 0 && error2 == 0 && 352 (int64_t)chain->data->ipdata.meta.nlinks <= 1) { 353 hammer2_chain_delete(parent, chain, 354 xop->head.mtid, 355 xop->dopermanent); 356 } else if (chain && error == 0 && error2 == 0) { 357 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 358 --chain->data->ipdata.meta.nlinks; 359 } 360 if (error == 0) 361 error = error2; 362 } 363 364 /* 365 * Chains passed to feed are expected to be locked shared. 366 */ 367 if (chain) { 368 hammer2_chain_unlock(chain); 369 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS | 370 HAMMER2_RESOLVE_SHARED); 371 } 372 373 /* 374 * We always return the hardlink target (the real inode) for 375 * further action. 376 */ 377 done: 378 hammer2_xop_feed(&xop->head, chain, clindex, error); 379 if (chain) 380 hammer2_chain_drop(chain); 381 if (parent) { 382 hammer2_chain_unlock(parent); 383 hammer2_chain_drop(parent); 384 } 385 } 386 387 /* 388 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename() 389 * 390 * ip1 - fdip 391 * ip2 - ip 392 * ip3 - cdip 393 * 394 * If a hardlink pointer: 395 * The existing hardlink target {fdip,ip} must be moved to another 396 * directory {cdip,ip} 397 * 398 * If not a hardlink pointer: 399 * Convert the target {fdip,ip} to a hardlink target {cdip,ip} and 400 * replace the original namespace {fdip,name} with a hardlink pointer. 401 */ 402 void 403 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex) 404 { 405 hammer2_xop_nlink_t *xop = &arg->xop_nlink; 406 hammer2_pfs_t *pmp; 407 hammer2_inode_data_t *wipdata; 408 hammer2_chain_t *parent; 409 hammer2_chain_t *chain; 410 hammer2_chain_t *tmp; 411 hammer2_inode_t *ip; 412 hammer2_key_t key_dummy; 413 int cache_index = -1; 414 int error; 415 int did_delete = 0; 416 417 /* 418 * We need the precise parent chain to issue the deletion. 419 */ 420 ip = xop->head.ip2; 421 pmp = ip->pmp; 422 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 423 if (parent) 424 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS); 425 if (parent == NULL) { 426 chain = NULL; 427 error = EIO; 428 goto done; 429 } 430 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); 431 if (chain == NULL) { 432 error = EIO; 433 goto done; 434 } 435 KKASSERT(chain->parent == parent); 436 437 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) { 438 /* 439 * Delete the original chain and hold onto it for the move 440 * to cdir. 441 * 442 * Replace the namespace with a hardlink pointer if the 443 * chain being moved is not already a hardlink target. 444 */ 445 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 446 did_delete = 1; 447 448 tmp = NULL; 449 error = hammer2_chain_create(&parent, &tmp, pmp, 450 chain->bref.key, 0, 451 HAMMER2_BREF_TYPE_INODE, 452 HAMMER2_INODE_BYTES, 453 xop->head.mtid, 0, 0); 454 if (error) 455 goto done; 456 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0); 457 wipdata = &tmp->data->ipdata; 458 bzero(wipdata, sizeof(*wipdata)); 459 wipdata->meta.name_key = chain->data->ipdata.meta.name_key; 460 wipdata->meta.name_len = chain->data->ipdata.meta.name_len; 461 bcopy(chain->data->ipdata.filename, wipdata->filename, 462 chain->data->ipdata.meta.name_len); 463 wipdata->meta.target_type = chain->data->ipdata.meta.type; 464 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; 465 wipdata->meta.inum = ip->meta.inum; 466 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; 467 wipdata->meta.nlinks = 1; 468 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; 469 470 hammer2_chain_unlock(tmp); 471 hammer2_chain_drop(tmp); 472 } else if (xop->head.ip1 != xop->head.ip3) { 473 /* 474 * Delete the hardlink target so it can be moved 475 * to cdir. 476 */ 477 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 478 did_delete = 1; 479 } else { 480 /* 481 * Deletion not necessary (just a nlinks update). 482 */ 483 did_delete = 0; 484 } 485 486 hammer2_chain_unlock(parent); 487 hammer2_chain_drop(parent); 488 parent = NULL; 489 490 /* 491 * Ok, back to the deleted chain. We must reconnect this chain 492 * as a hardlink target to cdir (ip3). 493 * 494 * WARNING! Frontend assumes filename length is 18 bytes. 495 */ 496 if (did_delete) { 497 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 498 wipdata = &chain->data->ipdata; 499 ksnprintf(wipdata->filename, sizeof(wipdata->filename), 500 "0x%016jx", (intmax_t)ip->meta.inum); 501 wipdata->meta.name_len = strlen(wipdata->filename); 502 wipdata->meta.name_key = ip->meta.inum; 503 504 /* 505 * We must seek parent properly for the create to reattach 506 * chain. XXX just use chain->parent or 507 * inode_chain_and_parent() ? 508 */ 509 parent = hammer2_inode_chain(xop->head.ip3, clindex, 510 HAMMER2_RESOLVE_ALWAYS); 511 if (parent == NULL) { 512 error = EIO; 513 goto done; 514 } 515 tmp = hammer2_chain_lookup(&parent, &key_dummy, 516 ip->meta.inum, ip->meta.inum, 517 &cache_index, 0); 518 if (tmp) { 519 hammer2_chain_unlock(tmp); 520 hammer2_chain_drop(tmp); 521 error = EEXIST; 522 goto done; 523 } 524 error = hammer2_chain_create(&parent, &chain, pmp, 525 wipdata->meta.name_key, 0, 526 HAMMER2_BREF_TYPE_INODE, 527 HAMMER2_INODE_BYTES, 528 xop->head.mtid, 0, 0); 529 } else { 530 error = 0; 531 } 532 533 /* 534 * Bump nlinks to synchronize with frontend. 535 */ 536 if (xop->nlinks_delta) { 537 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 538 chain->data->ipdata.meta.nlinks += xop->nlinks_delta; 539 } 540 541 /* 542 * To avoid having to scan the collision space we can simply 543 * reuse the inode's original name_key. But ip->meta.name_key 544 * may have already been updated by the front-end, so use xop->lhc. 545 * 546 * (frontend is responsible for fixing up ip->pip). 547 */ 548 done: 549 hammer2_xop_feed(&xop->head, NULL, clindex, error); 550 if (parent) { 551 hammer2_chain_unlock(parent); 552 hammer2_chain_drop(parent); 553 } 554 if (chain) { 555 hammer2_chain_unlock(chain); 556 hammer2_chain_drop(chain); 557 } 558 } 559 560 /* 561 * Backend for hammer2_vop_nrename() 562 * 563 * This handles the final step of renaming, either renaming the 564 * actual inode or renaming the hardlink pointer. 565 */ 566 void 567 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex) 568 { 569 hammer2_xop_nrename_t *xop = &arg->xop_nrename; 570 hammer2_pfs_t *pmp; 571 hammer2_chain_t *parent; 572 hammer2_chain_t *chain; 573 hammer2_chain_t *tmp; 574 hammer2_inode_t *ip; 575 hammer2_key_t key_dummy; 576 int cache_index = -1; 577 int error; 578 579 /* 580 * We need the precise parent chain to issue the deletion. 581 * 582 * If this is not a hardlink target we can act on the inode, 583 * otherwise we have to locate the hardlink pointer. 584 */ 585 ip = xop->head.ip2; 586 pmp = ip->pmp; 587 chain = NULL; 588 589 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { 590 /* 591 * Find ip's direct parent chain. 592 */ 593 parent = hammer2_inode_chain(ip, clindex, 594 HAMMER2_RESOLVE_ALWAYS); 595 if (parent) 596 hammer2_chain_getparent(&parent, 597 HAMMER2_RESOLVE_ALWAYS); 598 if (parent == NULL) { 599 error = EIO; 600 goto done; 601 } 602 chain = hammer2_inode_chain(ip, clindex, 603 HAMMER2_RESOLVE_ALWAYS); 604 if (chain == NULL) { 605 error = EIO; 606 goto done; 607 } 608 } else { 609 /* 610 * The hardlink pointer for the head.ip1 hardlink target 611 * is in fdip, do a namespace search. 612 */ 613 const hammer2_inode_data_t *ripdata; 614 hammer2_key_t lhc; 615 hammer2_key_t key_next; 616 const char *name; 617 size_t name_len; 618 619 parent = hammer2_inode_chain(xop->head.ip1, clindex, 620 HAMMER2_RESOLVE_ALWAYS); 621 if (parent == NULL) { 622 kprintf("xop_nrename: NULL parent\n"); 623 error = EIO; 624 goto done; 625 } 626 name = xop->head.name1; 627 name_len = xop->head.name1_len; 628 629 /* 630 * Lookup the directory entry 631 */ 632 lhc = hammer2_dirhash(name, name_len); 633 chain = hammer2_chain_lookup(&parent, &key_next, 634 lhc, lhc + HAMMER2_DIRHASH_LOMASK, 635 &cache_index, 636 HAMMER2_LOOKUP_ALWAYS); 637 while (chain) { 638 ripdata = &chain->data->ipdata; 639 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && 640 ripdata->meta.name_len == name_len && 641 bcmp(ripdata->filename, name, name_len) == 0) { 642 break; 643 } 644 chain = hammer2_chain_next(&parent, chain, &key_next, 645 key_next, 646 lhc + HAMMER2_DIRHASH_LOMASK, 647 &cache_index, 648 HAMMER2_LOOKUP_ALWAYS); 649 } 650 } 651 652 if (chain == NULL) { 653 /* XXX shouldn't happen, but does under fsstress */ 654 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n", 655 xop->head.name1, 656 xop->head.name2); 657 error = ENOENT; 658 goto done; 659 } 660 661 /* 662 * Delete it, then create it in the new namespace. 663 */ 664 hammer2_chain_delete(parent, chain, xop->head.mtid, 0); 665 hammer2_chain_unlock(parent); 666 hammer2_chain_drop(parent); 667 parent = NULL; /* safety */ 668 669 /* 670 * Ok, back to the deleted chain. We must reconnect this chain 671 * to tdir (ip3). The chain (a real inode or a hardlink pointer) 672 * is not otherwise modified. 673 * 674 * Frontend is expected to replicate the same inode meta data 675 * modifications. 676 * 677 * NOTE! This chain may not represent the actual inode, it 678 * can be a hardlink pointer. 679 * 680 * XXX in-inode parent directory specification? 681 */ 682 if (chain->data->ipdata.meta.name_key != xop->lhc || 683 xop->head.name1_len != xop->head.name2_len || 684 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) { 685 hammer2_inode_data_t *wipdata; 686 687 hammer2_chain_modify(chain, xop->head.mtid, 0, 0); 688 wipdata = &chain->data->ipdata; 689 690 bzero(wipdata->filename, sizeof(wipdata->filename)); 691 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); 692 wipdata->meta.name_key = xop->lhc; 693 wipdata->meta.name_len = xop->head.name2_len; 694 } 695 696 /* 697 * We must seek parent properly for the create. 698 */ 699 parent = hammer2_inode_chain(xop->head.ip3, clindex, 700 HAMMER2_RESOLVE_ALWAYS); 701 if (parent == NULL) { 702 error = EIO; 703 goto done; 704 } 705 tmp = hammer2_chain_lookup(&parent, &key_dummy, 706 xop->lhc, xop->lhc, 707 &cache_index, 0); 708 if (tmp) { 709 hammer2_chain_unlock(tmp); 710 hammer2_chain_drop(tmp); 711 error = EEXIST; 712 goto done; 713 } 714 715 error = hammer2_chain_create(&parent, &chain, pmp, 716 xop->lhc, 0, 717 HAMMER2_BREF_TYPE_INODE, 718 HAMMER2_INODE_BYTES, 719 xop->head.mtid, 0, 0); 720 /* 721 * (frontend is responsible for fixing up ip->pip). 722 */ 723 done: 724 hammer2_xop_feed(&xop->head, NULL, clindex, error); 725 if (parent) { 726 hammer2_chain_unlock(parent); 727 hammer2_chain_drop(parent); 728 } 729 if (chain) { 730 hammer2_chain_unlock(chain); 731 hammer2_chain_drop(chain); 732 } 733 } 734 735 /* 736 * Directory collision resolver scan helper (backend, threaded). 737 * 738 * Used by the inode create code to locate an unused lhc. 739 */ 740 void 741 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex) 742 { 743 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 744 hammer2_chain_t *parent; 745 hammer2_chain_t *chain; 746 hammer2_key_t key_next; 747 int cache_index = -1; /* XXX */ 748 int error = 0; 749 750 parent = hammer2_inode_chain(xop->head.ip1, clindex, 751 HAMMER2_RESOLVE_ALWAYS | 752 HAMMER2_RESOLVE_SHARED); 753 if (parent == NULL) { 754 kprintf("xop_nresolve: NULL parent\n"); 755 chain = NULL; 756 error = EIO; 757 goto done; 758 } 759 760 /* 761 * Lookup all possibly conflicting directory entries, the feed 762 * inherits the chain's lock so do not unlock it on the iteration. 763 */ 764 chain = hammer2_chain_lookup(&parent, &key_next, 765 xop->lhc, 766 xop->lhc + HAMMER2_DIRHASH_LOMASK, 767 &cache_index, 768 HAMMER2_LOOKUP_ALWAYS | 769 HAMMER2_LOOKUP_SHARED); 770 while (chain) { 771 error = hammer2_xop_feed(&xop->head, chain, clindex, 772 chain->error); 773 if (error) { 774 hammer2_chain_drop(chain); 775 chain = NULL; /* safety */ 776 break; 777 } 778 chain = hammer2_chain_next(&parent, chain, &key_next, 779 key_next, 780 xop->lhc + HAMMER2_DIRHASH_LOMASK, 781 &cache_index, 782 HAMMER2_LOOKUP_ALWAYS | 783 HAMMER2_LOOKUP_SHARED | 784 HAMMER2_LOOKUP_NOUNLOCK); 785 } 786 done: 787 hammer2_xop_feed(&xop->head, NULL, clindex, error); 788 if (parent) { 789 hammer2_chain_unlock(parent); 790 hammer2_chain_drop(parent); 791 } 792 } 793 794 /* 795 * Generic lookup of a specific key. 796 * 797 * Used by the inode hidden directory code to find the hidden directory. 798 */ 799 void 800 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex) 801 { 802 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc; 803 hammer2_chain_t *parent; 804 hammer2_chain_t *chain; 805 hammer2_key_t key_next; 806 int cache_index = -1; /* XXX */ 807 int error = 0; 808 809 parent = hammer2_inode_chain(xop->head.ip1, clindex, 810 HAMMER2_RESOLVE_ALWAYS | 811 HAMMER2_RESOLVE_SHARED); 812 chain = NULL; 813 if (parent == NULL) { 814 error = EIO; 815 goto done; 816 } 817 818 /* 819 * Lookup all possibly conflicting directory entries, the feed 820 * inherits the chain's lock so do not unlock it on the iteration. 821 */ 822 chain = hammer2_chain_lookup(&parent, &key_next, 823 xop->lhc, xop->lhc, 824 &cache_index, 825 HAMMER2_LOOKUP_ALWAYS | 826 HAMMER2_LOOKUP_SHARED); 827 if (chain) 828 hammer2_xop_feed(&xop->head, chain, clindex, chain->error); 829 else 830 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT); 831 832 done: 833 if (chain) { 834 /* leave lock intact for feed */ 835 hammer2_chain_drop(chain); 836 } 837 if (parent) { 838 hammer2_chain_unlock(parent); 839 hammer2_chain_drop(parent); 840 } 841 } 842 843 /* 844 * Generic scan 845 */ 846 void 847 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex) 848 { 849 hammer2_xop_scanall_t *xop = &arg->xop_scanall; 850 hammer2_chain_t *parent; 851 hammer2_chain_t *chain; 852 hammer2_key_t key_next; 853 int cache_index = -1; 854 int error = 0; 855 856 /* 857 * The inode's chain is the iterator. If we cannot acquire it our 858 * contribution ends here. 859 */ 860 parent = hammer2_inode_chain(xop->head.ip1, clindex, 861 HAMMER2_RESOLVE_ALWAYS | 862 HAMMER2_RESOLVE_SHARED); 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 &cache_index, HAMMER2_LOOKUP_SHARED | 875 HAMMER2_LOOKUP_NODIRECT); 876 while (chain) { 877 error = hammer2_xop_feed(&xop->head, chain, clindex, 0); 878 if (error) 879 break; 880 chain = hammer2_chain_next(&parent, chain, &key_next, 881 key_next, xop->key_end, 882 &cache_index, 883 HAMMER2_LOOKUP_SHARED | 884 HAMMER2_LOOKUP_NODIRECT | 885 HAMMER2_LOOKUP_NOUNLOCK); 886 } 887 if (chain) 888 hammer2_chain_drop(chain); 889 hammer2_chain_unlock(parent); 890 hammer2_chain_drop(parent); 891 done: 892 hammer2_xop_feed(&xop->head, NULL, clindex, error); 893 } 894