hammer2 - Consolidate backend rename ops
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
blobe74d5a121767592949c06aa7d7a9b6e5d45d2f6c
1 /*
2 * Copyright (c) 2011-2015 The DragonFly Project. All rights reserved.
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)
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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.
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.
37 * Per-node backend for kernel filesystem interface.
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.
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>
61 #include "hammer2.h"
64 * Determine if the specified directory is empty. Returns 0 on success.
66 * May return 0, ENOTDIR, or EAGAIN.
68 static
69 int
70 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex)
72 hammer2_chain_t *parent;
73 hammer2_chain_t *chain;
74 hammer2_key_t key_next;
75 hammer2_key_t inum;
76 int cache_index = -1;
77 int error;
79 error = 0;
80 chain = hammer2_chain_lookup_init(ochain, 0);
82 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
83 if (oparent)
84 hammer2_chain_unlock(oparent);
85 inum = chain->bref.embed.dirent.inum;
86 parent = NULL;
87 error = hammer2_chain_inode_find(chain->pmp, inum,
88 clindex, 0,
89 &parent, &chain);
90 if (parent) {
91 hammer2_chain_unlock(parent);
92 hammer2_chain_drop(parent);
94 if (oparent) {
95 hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS);
96 if (ochain->parent != oparent) {
97 if (chain) {
98 hammer2_chain_unlock(chain);
99 hammer2_chain_drop(chain);
101 kprintf("H2EAGAIN\n");
103 return EAGAIN;
109 * Determine if the directory is empty or not by checking its
110 * visible namespace (the area which contains directory entries).
112 parent = chain;
113 chain = NULL;
114 if (parent) {
115 chain = hammer2_chain_lookup(&parent, &key_next,
116 HAMMER2_DIRHASH_VISIBLE,
117 HAMMER2_KEY_MAX,
118 &cache_index, 0);
120 if (chain) {
121 error = ENOTEMPTY;
122 hammer2_chain_unlock(chain);
123 hammer2_chain_drop(chain);
124 } else {
125 error = 0;
127 hammer2_chain_lookup_done(parent);
129 return error;
133 * Backend for hammer2_vfs_root()
135 * This is called when a newly mounted PFS has not yet synchronized
136 * to the inode_tid and modify_tid.
138 void
139 hammer2_xop_ipcluster(hammer2_thread_t *thr, hammer2_xop_t *arg)
141 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
142 hammer2_chain_t *chain;
143 int error;
145 chain = hammer2_inode_chain(xop->head.ip1, thr->clindex,
146 HAMMER2_RESOLVE_ALWAYS |
147 HAMMER2_RESOLVE_SHARED);
148 if (chain)
149 error = chain->error;
150 else
151 error = EIO;
153 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
154 if (chain) {
155 hammer2_chain_unlock(chain);
156 hammer2_chain_drop(chain);
161 * Backend for hammer2_vop_readdir()
163 void
164 hammer2_xop_readdir(hammer2_thread_t *thr, hammer2_xop_t *arg)
166 hammer2_xop_readdir_t *xop = &arg->xop_readdir;
167 hammer2_chain_t *parent;
168 hammer2_chain_t *chain;
169 hammer2_key_t key_next;
170 hammer2_key_t lkey;
171 int cache_index = -1;
172 int error = 0;
174 lkey = xop->lkey;
175 if (hammer2_debug & 0x0020)
176 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
179 * The inode's chain is the iterator. If we cannot acquire it our
180 * contribution ends here.
182 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
183 HAMMER2_RESOLVE_ALWAYS |
184 HAMMER2_RESOLVE_SHARED);
185 if (parent == NULL) {
186 kprintf("xop_readdir: NULL parent\n");
187 goto done;
191 * Directory scan [re]start and loop, the feed inherits the chain's
192 * lock so do not unlock it on the iteration.
194 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
195 &cache_index, HAMMER2_LOOKUP_SHARED);
196 if (chain == NULL) {
197 chain = hammer2_chain_lookup(&parent, &key_next,
198 lkey, HAMMER2_KEY_MAX,
199 &cache_index,
200 HAMMER2_LOOKUP_SHARED);
202 while (chain) {
203 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
204 if (error)
205 break;
206 chain = hammer2_chain_next(&parent, chain, &key_next,
207 key_next, HAMMER2_KEY_MAX,
208 &cache_index,
209 HAMMER2_LOOKUP_SHARED);
211 if (chain) {
212 hammer2_chain_unlock(chain);
213 hammer2_chain_drop(chain);
215 hammer2_chain_unlock(parent);
216 hammer2_chain_drop(parent);
217 done:
218 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
222 * Backend for hammer2_vop_nresolve()
224 void
225 hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg)
227 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
228 hammer2_chain_t *parent;
229 hammer2_chain_t *chain;
230 const char *name;
231 size_t name_len;
232 hammer2_key_t key_next;
233 hammer2_key_t lhc;
234 int cache_index = -1; /* XXX */
235 int error;
237 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
238 HAMMER2_RESOLVE_ALWAYS |
239 HAMMER2_RESOLVE_SHARED);
240 if (parent == NULL) {
241 kprintf("xop_nresolve: NULL parent\n");
242 chain = NULL;
243 error = EIO;
244 goto done;
246 name = xop->head.name1;
247 name_len = xop->head.name1_len;
250 * Lookup the directory entry
252 lhc = hammer2_dirhash(name, name_len);
253 chain = hammer2_chain_lookup(&parent, &key_next,
254 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
255 &cache_index,
256 HAMMER2_LOOKUP_ALWAYS |
257 HAMMER2_LOOKUP_SHARED);
258 while (chain) {
259 if (hammer2_chain_dirent_test(chain, name, name_len))
260 break;
261 chain = hammer2_chain_next(&parent, chain, &key_next,
262 key_next,
263 lhc + HAMMER2_DIRHASH_LOMASK,
264 &cache_index,
265 HAMMER2_LOOKUP_ALWAYS |
266 HAMMER2_LOOKUP_SHARED);
270 * If the entry is a hardlink pointer, resolve it.
272 error = 0;
273 if (chain) {
274 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
275 lhc = chain->bref.embed.dirent.inum;
276 error = hammer2_chain_inode_find(chain->pmp,
277 lhc,
278 thr->clindex,
279 HAMMER2_LOOKUP_SHARED,
280 &parent,
281 &chain);
284 done:
285 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
286 if (chain) {
287 hammer2_chain_unlock(chain);
288 hammer2_chain_drop(chain);
290 if (parent) {
291 hammer2_chain_unlock(parent);
292 hammer2_chain_drop(parent);
297 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and
298 * backend for pfs_delete.
300 * This function locates and removes a directory entry, and will lookup
301 * and return the underlying inode. For directory entries the underlying
302 * inode is not removed. If the directory entry is the actual inode itself,
303 * it may be conditonally removed and returned.
305 * WARNING! Any target inode's nlinks may not be synchronized to the
306 * in-memory inode. The frontend's hammer2_inode_unlink_finisher()
307 * is responsible for the final disposition of the actual inode.
309 void
310 hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg)
312 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
313 hammer2_chain_t *parent;
314 hammer2_chain_t *chain;
315 const char *name;
316 size_t name_len;
317 hammer2_key_t key_next;
318 hammer2_key_t lhc;
319 int cache_index = -1; /* XXX */
320 int error;
322 again:
324 * Requires exclusive lock
326 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
327 HAMMER2_RESOLVE_ALWAYS);
328 chain = NULL;
329 if (parent == NULL) {
330 kprintf("xop_nresolve: NULL parent\n");
331 error = EIO;
332 goto done;
334 name = xop->head.name1;
335 name_len = xop->head.name1_len;
338 * Lookup the directory entry
340 lhc = hammer2_dirhash(name, name_len);
341 chain = hammer2_chain_lookup(&parent, &key_next,
342 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
343 &cache_index,
344 HAMMER2_LOOKUP_ALWAYS);
345 while (chain) {
346 if (hammer2_chain_dirent_test(chain, name, name_len))
347 break;
348 chain = hammer2_chain_next(&parent, chain, &key_next,
349 key_next,
350 lhc + HAMMER2_DIRHASH_LOMASK,
351 &cache_index,
352 HAMMER2_LOOKUP_ALWAYS);
356 * The directory entry will either be a BREF_TYPE_DIRENT or a
357 * BREF_TYPE_INODE. We always permanently delete DIRENTs, but
358 * must go by xop->dopermanent for BREF_TYPE_INODE.
360 * Note that the target chain's nlinks may not be synchronized with
361 * the in-memory hammer2_inode_t structure, so we don't try to do
362 * anything fancy here. The frontend deals with nlinks
363 * synchronization.
365 error = 0;
366 if (chain) {
367 int dopermanent = xop->dopermanent & 1;
368 int doforce = xop->dopermanent & 2;
369 uint8_t type;
372 * If the directory entry is the actual inode then use its
373 * type for the directory typing tests, otherwise if it is
374 * a directory entry, pull the type field from the entry.
376 * Directory entries are always permanently deleted
377 * (because they aren't the actual inode).
379 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
380 type = chain->bref.embed.dirent.type;
381 dopermanent |= HAMMER2_DELETE_PERMANENT;
382 } else {
383 type = chain->data->ipdata.meta.type;
387 * Check directory typing and delete the entry. Note that
388 * nlinks adjustments are made on the real inode by the
389 * frontend, not here.
391 * Unfortunately, checkdirempty() may have to unlock (parent).
392 * If it no longer matches chain->parent after re-locking,
393 * EAGAIN is returned.
395 if (type == HAMMER2_OBJTYPE_DIRECTORY && doforce) {
397 * If doforce then execute the operation even if
398 * the directory is not empty.
400 error = chain->error;
401 hammer2_chain_delete(parent, chain,
402 xop->head.mtid, dopermanent);
403 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
404 (error = checkdirempty(parent, chain, thr->clindex)) != 0) {
406 * error may be EAGAIN or ENOTEMPTY
408 if (error == EAGAIN) {
409 hammer2_chain_unlock(chain);
410 hammer2_chain_drop(chain);
411 hammer2_chain_unlock(parent);
412 hammer2_chain_drop(parent);
413 goto again;
415 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
416 xop->isdir == 0) {
417 error = ENOTDIR;
418 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
419 xop->isdir >= 1) {
420 error = EISDIR;
421 } else {
423 * Delete the directory entry. chain might also
424 * be a directly-embedded inode.
426 error = chain->error;
427 hammer2_chain_delete(parent, chain,
428 xop->head.mtid, dopermanent);
433 * If chain is a directory entry we must resolve it. We do not try
434 * to manipulate the contents as it might not be synchronized with
435 * the frontend hammer2_inode_t, nor do we try to lookup the
436 * frontend hammer2_inode_t here (we are the backend!).
438 if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
439 int error2;
441 lhc = chain->bref.embed.dirent.inum;
443 error2 = hammer2_chain_inode_find(chain->pmp, lhc,
444 thr->clindex, 0,
445 &parent, &chain);
446 if (error2) {
447 kprintf("inode_find: %016jx %p failed\n",
448 lhc, chain);
449 error2 = 0; /* silently ignore */
451 if (error == 0)
452 error = error2;
456 * Return the inode target for further action. Typically used by
457 * hammer2_inode_unlink_finisher().
459 done:
460 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
461 if (chain) {
462 hammer2_chain_unlock(chain);
463 hammer2_chain_drop(chain);
464 chain = NULL;
466 if (parent) {
467 hammer2_chain_unlock(parent);
468 hammer2_chain_drop(parent);
469 parent = NULL;
474 * Backend for hammer2_vop_nrename()
476 * This handles the backend rename operation. Typically this renames
477 * directory entries but can also be used to rename embedded inodes.
479 * NOTE! The frontend is responsible for updating the inode meta-data in
480 * the file being renamed and for decrementing the target-replaced
481 * inode's nlinks, if present.
483 void
484 hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg)
486 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
487 hammer2_pfs_t *pmp;
488 hammer2_chain_t *parent;
489 hammer2_chain_t *chain;
490 hammer2_chain_t *tmp;
491 hammer2_inode_t *ip;
492 hammer2_key_t key_next;
493 int cache_index = -1;
494 int error;
497 * We need the precise parent chain to issue the deletion.
499 * If this is a directory entry we must locate the underlying
500 * inode. If it is an embedded inode we can act directly on it.
502 ip = xop->head.ip2;
503 pmp = ip->pmp;
504 chain = NULL;
506 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
508 * Find ip's direct parent chain.
510 parent = hammer2_inode_chain(ip, thr->clindex,
511 HAMMER2_RESOLVE_ALWAYS);
512 if (parent)
513 hammer2_chain_getparent(&parent,
514 HAMMER2_RESOLVE_ALWAYS);
515 if (parent == NULL) {
516 error = EIO;
517 goto done;
519 chain = hammer2_inode_chain(ip, thr->clindex,
520 HAMMER2_RESOLVE_ALWAYS);
521 if (chain == NULL) {
522 error = EIO;
523 goto done;
525 } else {
527 * The directory entry for the head.ip1 inode
528 * is in fdip, do a namespace search.
530 hammer2_key_t lhc;
531 const char *name;
532 size_t name_len;
534 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
535 HAMMER2_RESOLVE_ALWAYS);
536 if (parent == NULL) {
537 kprintf("xop_nrename: NULL parent\n");
538 error = EIO;
539 goto done;
541 name = xop->head.name1;
542 name_len = xop->head.name1_len;
545 * Lookup the directory entry
547 lhc = hammer2_dirhash(name, name_len);
548 chain = hammer2_chain_lookup(&parent, &key_next,
549 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
550 &cache_index,
551 HAMMER2_LOOKUP_ALWAYS);
552 while (chain) {
553 if (hammer2_chain_dirent_test(chain, name, name_len))
554 break;
555 chain = hammer2_chain_next(&parent, chain, &key_next,
556 key_next,
557 lhc + HAMMER2_DIRHASH_LOMASK,
558 &cache_index,
559 HAMMER2_LOOKUP_ALWAYS);
563 if (chain == NULL) {
564 /* XXX shouldn't happen, but does under fsstress */
565 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
566 xop->head.name1,
567 xop->head.name2);
568 error = ENOENT;
569 goto done;
573 * Delete it, then create it in the new namespace.
575 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
576 hammer2_chain_unlock(parent);
577 hammer2_chain_drop(parent);
578 parent = NULL; /* safety */
581 * Adjust fields in the deleted chain appropriate for the rename
582 * operation.
584 * NOTE! For embedded inodes, the frontend will officially replicate
585 * the field adjustments, but we also do it here to maintain
586 * consistency in case of a crash.
588 if (chain->bref.key != xop->lhc ||
589 xop->head.name1_len != xop->head.name2_len ||
590 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
591 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
592 hammer2_inode_data_t *wipdata;
594 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
595 wipdata = &chain->data->ipdata;
597 bzero(wipdata->filename, sizeof(wipdata->filename));
598 bcopy(xop->head.name2, wipdata->filename,
599 xop->head.name2_len);
600 wipdata->meta.name_key = xop->lhc;
601 wipdata->meta.name_len = xop->head.name2_len;
603 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
604 if (xop->head.name2_len <= sizeof(chain->bref.check.buf)) {
605 hammer2_chain_resize(chain, xop->head.mtid, 0,
606 0, 0);
607 hammer2_chain_modify(chain, xop->head.mtid,
608 0, 0);
609 bzero(chain->bref.check.buf,
610 sizeof(chain->bref.check.buf));
611 bcopy(xop->head.name2, chain->bref.check.buf,
612 xop->head.name2_len);
613 } else {
614 hammer2_chain_resize(chain, xop->head.mtid, 0,
615 hammer2_getradix(HAMMER2_ALLOC_MIN), 0);
616 hammer2_chain_modify(chain, xop->head.mtid,
617 0, 0);
618 bzero(chain->data->buf,
619 sizeof(chain->data->buf));
620 bcopy(xop->head.name2, chain->data->buf,
621 xop->head.name2_len);
623 chain->bref.embed.dirent.namlen = xop->head.name2_len;
628 * The frontend will replicate this operation and is the real final
629 * authority, but adjust the inode's iparent field too if the inode
630 * is embedded in the directory.
632 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
633 chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
634 hammer2_inode_data_t *wipdata;
636 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
637 wipdata = &chain->data->ipdata;
639 wipdata->meta.iparent = xop->head.ip3->meta.inum;
643 * Destroy any matching target(s) before creating the new entry.
644 * This will result in some ping-ponging of the directory key
645 * iterator but that is ok.
647 parent = hammer2_inode_chain(xop->head.ip3, thr->clindex,
648 HAMMER2_RESOLVE_ALWAYS);
649 if (parent == NULL) {
650 error = EIO;
651 goto done;
654 tmp = hammer2_chain_lookup(&parent, &key_next,
655 xop->lhc & ~HAMMER2_DIRHASH_LOMASK,
656 xop->lhc | HAMMER2_DIRHASH_LOMASK,
657 &cache_index,
658 HAMMER2_LOOKUP_ALWAYS);
659 while (tmp) {
660 if (hammer2_chain_dirent_test(tmp, xop->head.name2,
661 xop->head.name2_len)) {
662 hammer2_chain_delete(parent, tmp, xop->head.mtid, 0);
664 tmp = hammer2_chain_next(&parent, tmp, &key_next,
665 key_next,
666 xop->lhc | HAMMER2_DIRHASH_LOMASK,
667 &cache_index,
668 HAMMER2_LOOKUP_ALWAYS);
672 * A relookup is required before the create to properly position
673 * the parent chain.
675 tmp = hammer2_chain_lookup(&parent, &key_next,
676 xop->lhc, xop->lhc,
677 &cache_index, 0);
678 KKASSERT(tmp == NULL);
679 error = hammer2_chain_create(&parent, &chain,
680 pmp, HAMMER2_METH_DEFAULT,
681 xop->lhc, 0,
682 HAMMER2_BREF_TYPE_INODE,
683 HAMMER2_INODE_BYTES,
684 xop->head.mtid, 0, 0);
685 done:
686 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
687 if (parent) {
688 hammer2_chain_unlock(parent);
689 hammer2_chain_drop(parent);
691 if (chain) {
692 hammer2_chain_unlock(chain);
693 hammer2_chain_drop(chain);
698 * Directory collision resolver scan helper (backend, threaded).
700 * Used by the inode create code to locate an unused lhc.
702 void
703 hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *arg)
705 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
706 hammer2_chain_t *parent;
707 hammer2_chain_t *chain;
708 hammer2_key_t key_next;
709 int cache_index = -1; /* XXX */
710 int error = 0;
712 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
713 HAMMER2_RESOLVE_ALWAYS |
714 HAMMER2_RESOLVE_SHARED);
715 if (parent == NULL) {
716 kprintf("xop_nresolve: NULL parent\n");
717 chain = NULL;
718 error = EIO;
719 goto done;
723 * Lookup all possibly conflicting directory entries, the feed
724 * inherits the chain's lock so do not unlock it on the iteration.
726 chain = hammer2_chain_lookup(&parent, &key_next,
727 xop->lhc,
728 xop->lhc + HAMMER2_DIRHASH_LOMASK,
729 &cache_index,
730 HAMMER2_LOOKUP_ALWAYS |
731 HAMMER2_LOOKUP_SHARED);
732 while (chain) {
733 error = hammer2_xop_feed(&xop->head, chain, thr->clindex,
734 chain->error);
735 if (error) {
736 hammer2_chain_unlock(chain);
737 hammer2_chain_drop(chain);
738 chain = NULL; /* safety */
739 break;
741 chain = hammer2_chain_next(&parent, chain, &key_next,
742 key_next,
743 xop->lhc + HAMMER2_DIRHASH_LOMASK,
744 &cache_index,
745 HAMMER2_LOOKUP_ALWAYS |
746 HAMMER2_LOOKUP_SHARED);
748 done:
749 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
750 if (parent) {
751 hammer2_chain_unlock(parent);
752 hammer2_chain_drop(parent);
757 * Generic lookup of a specific key.
759 * Used by the inode hidden directory code to find the hidden directory.
761 void
762 hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *arg)
764 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
765 hammer2_chain_t *parent;
766 hammer2_chain_t *chain;
767 hammer2_key_t key_next;
768 int cache_index = -1; /* XXX */
769 int error = 0;
771 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
772 HAMMER2_RESOLVE_ALWAYS |
773 HAMMER2_RESOLVE_SHARED);
774 chain = NULL;
775 if (parent == NULL) {
776 error = EIO;
777 goto done;
781 * Lookup all possibly conflicting directory entries, the feed
782 * inherits the chain's lock so do not unlock it on the iteration.
784 chain = hammer2_chain_lookup(&parent, &key_next,
785 xop->lhc, xop->lhc,
786 &cache_index,
787 HAMMER2_LOOKUP_ALWAYS |
788 HAMMER2_LOOKUP_SHARED);
789 if (chain)
790 hammer2_xop_feed(&xop->head, chain, thr->clindex, chain->error);
791 else
792 hammer2_xop_feed(&xop->head, NULL, thr->clindex, ENOENT);
794 done:
795 if (chain) {
796 hammer2_chain_unlock(chain);
797 hammer2_chain_drop(chain);
799 if (parent) {
800 hammer2_chain_unlock(parent);
801 hammer2_chain_drop(parent);
806 * Generic scan
808 * WARNING! Fed chains must be locked shared so ownership can be transfered
809 * and to prevent frontend/backend stalls that would occur with an
810 * exclusive lock. The shared lock also allows chain->data to be
811 * retained.
813 void
814 hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *arg)
816 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
817 hammer2_chain_t *parent;
818 hammer2_chain_t *chain;
819 hammer2_key_t key_next;
820 int cache_index = -1;
821 int error = 0;
824 * Assert required flags.
826 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
827 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
830 * The inode's chain is the iterator. If we cannot acquire it our
831 * contribution ends here.
833 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
834 xop->resolve_flags);
835 if (parent == NULL) {
836 kprintf("xop_readdir: NULL parent\n");
837 goto done;
841 * Generic scan of exact records. Note that indirect blocks are
842 * automatically recursed and will not be returned.
844 chain = hammer2_chain_lookup(&parent, &key_next,
845 xop->key_beg, xop->key_end,
846 &cache_index, xop->lookup_flags);
847 while (chain) {
848 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
849 if (error)
850 break;
851 chain = hammer2_chain_next(&parent, chain, &key_next,
852 key_next, xop->key_end,
853 &cache_index, xop->lookup_flags);
855 if (chain) {
856 hammer2_chain_unlock(chain);
857 hammer2_chain_drop(chain);
859 hammer2_chain_unlock(parent);
860 hammer2_chain_drop(parent);
861 done:
862 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);